Skip to content

Commit 3474d5e

Browse files
authored
Merge pull request #1202 from qiboteam/pytorch_backend
`pytorch` backend
2 parents f0548cd + 07d28fa commit 3474d5e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1286
-636
lines changed

doc/source/api-reference/qibo.rst

+6-2
Original file line numberDiff line numberDiff line change
@@ -2375,7 +2375,7 @@ The user can switch backends using
23752375
qibo.set_backend("numpy")
23762376
23772377
before creating any circuits or gates. The default backend is the first available
2378-
from ``qibojit``, ``tensorflow``, ``numpy``.
2378+
from ``qibojit``, ``pytorch``, ``tensorflow``, ``numpy``.
23792379

23802380
Some backends support different platforms. For example, the qibojit backend
23812381
provides two platforms (``cupy`` and ``cuquantum``) when used on GPU.
@@ -2456,4 +2456,8 @@ Alternatively, a Clifford circuit can also be executed starting from the :class:
24562456
Cloud Backends
24572457
^^^^^^^^^^^^^^
24582458

2459-
Additional backends, that support the remote execution of quantum circuits through cloud service providers, such as IBM and QRC-TII, are provided by the optional qibo plugin `qibo-cloud-backends <https://github.com/qiboteam/qibo-cloud-backends>`_. For more information please refer to the `official documentation <https://qibo.science/qibo-cloud-backends/stable/>`_.
2459+
Additional backends that support the remote execution of quantum circuits through
2460+
cloud service providers, such as IBM and QRC-TII, are provided by the optional qibo plugin
2461+
`qibo-cloud-backends <https://github.com/qiboteam/qibo-cloud-backends>`_.
2462+
For more information please refer to the
2463+
`official documentation <https://qibo.science/qibo-cloud-backends/stable/>`_.

doc/source/getting-started/backends.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ if the corresponding packages are installed, following the hierarchy below:
3636

3737
* :ref:`installing-numpy`: a lightweight quantum simulator shipped with the :ref:`installing-qibo` base package. Use this simulator if your CPU architecture is not supported by the other backends. Please note that the simulation performance is quite poor in comparison to other backends.
3838
* :ref:`installing-qibojit`: an efficient simulation backend for CPU, GPU and multi-GPU based on just-in-time (JIT) compiled custom operators. Install this package if you need to simulate quantum circuits with large number of qubits or complex quantum algorithms which may benefit from computing parallelism.
39-
* `qibotn <https://qibo.science/qibotn/stable/>`_: an interface to Tensor Networks simulation algorithms designed for GPUs and multi-node CPUs. This backend makes possible scaling quantum circuit simulation to a larger number of qubits.
4039
* :ref:`installing-tensorflow`: a pure TensorFlow implementation for quantum simulation which provides access to gradient descent optimization and the possibility to implement classical and quantum architectures together. This backend is not optimized for memory and speed, use :ref:`installing-qibojit` instead.
4140
* :ref:`installing-pytorch`: a pure PyTorch implementation for quantum simulation which provides access to gradient descent optimization and the possibility to implement classical and quantum architectures together. This backend is not optimized for memory and speed, use :ref:`installing-qibojit` instead.
4241
* :ref:`clifford <Clifford>`: a specialized backend for the simulation of quantum circuits with Clifford gates. This backend uses :ref:`installing-qibojit` and/or :ref:`installing-numpy`.
42+
* `qibotn <https://qibo.science/qibotn/stable/>`_: an interface to Tensor Networks simulation algorithms designed for GPUs and multi-node CPUs. This backend makes possible scaling quantum circuit simulation to a larger number of qubits.
4343

4444
The default backend that is used is the first available from the above list.
4545
The user can switch to a different using the ``qibo.set_backend`` method

examples/adiabatic/trotter_error.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ def main(nqubits, hfield, T, save):
6161
]
6262
alphas = [1.0, 0.7, 0.4]
6363
labels = [
64-
r"$\delta t ^{}$".format(exponent - 1),
65-
r"$\delta t ^{}$".format(exponent),
66-
r"$\delta t ^{}$".format(exponent + 1),
64+
f"$\\delta t ^{exponent - 1}$",
65+
f"$\\delta t ^{exponent}$",
66+
f"$\\delta t ^{exponent - 1}$",
6767
]
6868

6969
plt.figure(figsize=(7, 4))

examples/adiabatic3sat/functions.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def read_file(file_name, instance):
1717
solution (list): list of the correct outputs of the instance for testing.
1818
clauses (list): list of all clauses, with the qubits each clause acts upon.
1919
"""
20-
file = open("../data3sat/{q}bit/n{q}i{i}.txt".format(q=file_name, i=instance))
20+
file = open(f"../data3sat/{file_name}bit/n{file_name}i{instance}.txt")
2121
control = list(map(int, file.readline().split()))
2222
solution = list(map(str, file.readline().split()))
2323
clauses = [list(map(int, file.readline().split())) for _ in range(control[1])]
@@ -100,12 +100,12 @@ def plot(qubits, ground, first, gap, dt, T):
100100
plt.title("Energy during adiabatic evolution")
101101
ax.legend()
102102
fig.tight_layout()
103-
fig.savefig("{}_qubits_energy.png".format(qubits), dpi=300, bbox_inches="tight")
103+
fig.savefig(f"{qubits}_qubits_energy.png", dpi=300, bbox_inches="tight")
104104
fig, ax = plt.subplots()
105105
ax.plot(times, gap, label="gap energy", color="C0")
106106
plt.ylabel("energy")
107107
plt.xlabel("schedule")
108108
plt.title("Energy during adiabatic evolution")
109109
ax.legend()
110110
fig.tight_layout()
111-
fig.savefig("{}_qubits_gap.png".format(qubits), dpi=300, bbox_inches="tight")
111+
fig.savefig(f"{qubits}_qubits_gap.png", dpi=300, bbox_inches="tight")

examples/adiabatic3sat/main.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,8 @@ def main(nqubits, instance, T, dt, solver, plot, dense, params, method, maxiter)
4848
print("-" * 20 + "\n")
4949
if plot and nqubits >= 14:
5050
print(
51-
"Currently not possible to calculate gap energy for {} qubits."
52-
"\n Proceeding to adiabatic evolution without plotting data.\n"
53-
"".format(nqubits)
51+
f"Currently not possible to calculate gap energy for {nqubits} qubits."
52+
+ "\n Proceeding to adiabatic evolution without plotting data.\n"
5453
)
5554
plot = False
5655
if plot and method is not None:
@@ -97,9 +96,9 @@ def main(nqubits, instance, T, dt, solver, plot, dense, params, method, maxiter)
9796
output_dec = (np.abs(final_state) ** 2).argmax()
9897
max_output = "{0:0{bits}b}".format(output_dec, bits=nqubits)
9998
max_prob = (np.abs(final_state) ** 2).max()
100-
print("Exact cover instance with {} qubits.\n".format(nqubits))
99+
print(f"Exact cover instance with {nqubits} qubits.\n")
101100
if solution:
102-
print("Known solution: {}\n".format("".join(solution)))
101+
print(f"Known solution: {''.join(solution)}\n")
103102
print("-" * 20 + "\n")
104103
print(
105104
f"Adiabatic evolution with total time {T}, evolution step {dt} and "

examples/benchmarks/circuits.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def CircuitFactory(nqubits, circuit_name, accelerators=None, **kwargs):
6767
circuit = models.QFT(nqubits, accelerators=accelerators)
6868
else:
6969
if circuit_name not in _CIRCUITS:
70-
raise KeyError("Unknown benchmark circuit type {}." "".format(circuit_name))
70+
raise KeyError(f"Unknown benchmark circuit type {circuit_name}.")
7171
circuit = models.Circuit(nqubits, accelerators=accelerators)
7272
circuit.add(_CIRCUITS.get(circuit_name)(nqubits, **kwargs))
7373
return circuit

examples/benchmarks/main.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ def limit_gpu_memory(memory_limit=None):
5353
print("\nNo GPU memory limiter used.\n")
5454
return
5555

56-
print("\nAttempting to limit GPU memory to {}.\n".format(memory_limit))
56+
print(f"\nAttempting to limit GPU memory to {memory_limit}.\n")
5757
gpus = tf.config.list_physical_devices("GPU")
5858
for gpu in tf.config.list_physical_devices("GPU"):
5959
config = tf.config.experimental.VirtualDeviceConfiguration(
6060
memory_limit=memory_limit
6161
)
6262
tf.config.experimental.set_virtual_device_configuration(gpu, [config])
63-
print("Limiting memory of {} to {}.".format(gpu.name, memory_limit))
63+
print(f"Limiting memory of {gpu.name} to {memory_limit}.")
6464
print()
6565

6666

examples/benchmarks/utils.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ class BenchmarkLogger(list):
66
def __init__(self, filename=None):
77
self.filename = filename
88
if filename is not None and os.path.isfile(filename):
9-
print("Extending existing logs from {}.".format(filename))
9+
print(f"Extending existing logs from {filename}.")
1010
with open(filename) as file:
1111
super().__init__(json.load(file))
1212
else:
1313
if filename is not None:
14-
print("Creating new logs in {}.".format(filename))
14+
print(f"Creating new logs in {filename}.")
1515
super().__init__()
1616

1717
def dump(self):
@@ -20,7 +20,7 @@ def dump(self):
2020
json.dump(list(self), file)
2121

2222
def __str__(self):
23-
return "\n".join("{}: {}".format(k, v) for k, v in self[-1].items())
23+
return "\n".join(f"{k}: {v}" for k, v in self[-1].items())
2424

2525

2626
def parse_accelerators(accelerators):

examples/grover3sat/functions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def read_file(file_name, instance):
1515
solution (list): list of the correct outputs of the instance for testing.
1616
clauses (list): list of all clauses, with the qubits each clause acts upon.
1717
"""
18-
file = open("../data3sat/{q}bit/n{q}i{i}.txt".format(q=file_name, i=instance))
18+
file = open(f"../data3sat/{file_name}bit/n{file_name}i{instance}.txt")
1919
control = list(map(int, file.readline().split()))
2020
solution = list(map(str, file.readline().split()))
2121
clauses = [list(map(int, file.readline().split())) for _ in range(control[1])]

examples/grover3sat/main.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ def main(nqubits, instance):
1818
qubits = control[0]
1919
clauses_num = control[1]
2020
steps = int((np.pi / 4) * np.sqrt(2**qubits))
21-
print("Qubits encoding the solution: {}\n".format(qubits))
22-
print("Total number of qubits used: {}\n".format(qubits + clauses_num + 1))
21+
print(f"Qubits encoding the solution: {qubits}\n")
22+
print(f"Total number of qubits used: {qubits + clauses_num + 1}\n")
2323
q, c, ancilla, circuit = functions.create_qc(qubits, clauses_num)
2424
circuit = functions.grover(circuit, q, c, ancilla, clauses, steps)
2525
result = circuit(nshots=100)
2626
frequencies = result.frequencies(binary=True, registers=False)
2727
most_common_bitstring = frequencies.most_common(1)[0][0]
28-
print("Most common bitstring: {}\n".format(most_common_bitstring))
28+
print(f"Most common bitstring: {most_common_bitstring}\n")
2929
if solution:
30-
print("Exact cover solution: {}\n".format("".join(solution)))
30+
print(f"Exact cover solution: {''.join(solution)}\n")
3131

3232

3333
if __name__ == "__main__":

examples/hash-grover/main.py

+6-8
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,9 @@ def main(h_value, collisions, b):
2323
h = "{0:0{bits}b}".format(h_value, bits=b)
2424
if len(h) > 8:
2525
raise ValueError(
26-
"Hash should be at maximum an 8-bit number but given value contains {} bits.".format(
27-
len(h)
28-
)
26+
f"Hash should be at maximum an 8-bit number but given value contains {len(h)} bits."
2927
)
30-
print("Target hash: {}\n".format(h))
28+
print(f"Target hash: {h}\n")
3129
if collisions:
3230
grover_it = int(np.pi * np.sqrt((2**8) / collisions) / 4)
3331
result = functions.grover(q, constant_1, constant_2, rot, h, grover_it)
@@ -36,19 +34,19 @@ def main(h_value, collisions, b):
3634
print("Preimages:")
3735
for i in most_common:
3836
if functions.check_hash(q, i[0], h, constant_1, constant_2, rot):
39-
print(" - {}\n".format(i[0]))
37+
print(f" - {i[0]}\n")
4038
else:
4139
print(
4240
" Incorrect preimage found, number of given collisions might not match.\n"
4341
)
44-
print("Total iterations taken: {}\n".format(grover_it))
42+
print(f"Total iterations taken: {grover_it}\n")
4543
else:
4644
measured, total_iterations = functions.grover_unknown_M(
4745
q, constant_1, constant_2, rot, h
4846
)
4947
print("Solution found in an iterative process.\n")
50-
print("Preimage: {}\n".format(measured))
51-
print("Total iterations taken: {}\n".format(total_iterations))
48+
print(f"Preimage: {measured}\n")
49+
print(f"Total iterations taken: {total_iterations}\n")
5250

5351

5452
if __name__ == "__main__":

examples/unary/functions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ def paint_prob_distribution(bins, prob_sim, S0, sig, r, T):
482482
ax.plot(x, y, label="PDF", color="black")
483483
plt.ylabel("Probability")
484484
plt.xlabel("Option price")
485-
plt.title("Option price distribution for {} qubits ".format(bins))
485+
plt.title(f"Option price distribution for {bins} qubits ")
486486
ax.legend()
487487
fig.tight_layout()
488488
fig.savefig("Probability_distribution.png")

examples/unary/main.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def main(data, bins, M, shots):
1515

1616
# Generate the probability distribution plots
1717
fun.paint_prob_distribution(bins, prob_sim, S0, sig, r, T)
18-
print("Histogram printed for unary simulation with {} qubits.\n".format(bins))
18+
print(f"Histogram printed for unary simulation with {bins} qubits.\n")
1919

2020
# Create circuit to compute the expected payoff
2121
circuit, S = fun.load_payoff_quantum_sim(bins, S0, sig, r, T, K)
@@ -28,14 +28,14 @@ def main(data, bins, M, shots):
2828

2929
# Finding differences between exact value and quantum approximation
3030
error = fun.diff_qu_cl(qu_payoff_sim, cl_payoff)
31-
print("Exact value of the expected payoff: {}\n".format(cl_payoff))
32-
print("Expected payoff from quantum simulation: {}\n".format(qu_payoff_sim))
33-
print("Percentage error: {} %\n".format(error))
31+
print(f"Exact value of the expected payoff: {cl_payoff}\n")
32+
print(f"Expected payoff from quantum simulation: {qu_payoff_sim}\n")
33+
print(f"Percentage error: {error} %\n")
3434
print("-" * 60 + "\n")
3535

3636
# Applying amplitude estimation
3737
a_s, error_s = fun.amplitude_estimation(bins, M, data)
38-
print("Amplitude estimation with a total of {} runs.\n".format(M))
38+
print(f"Amplitude estimation with a total of {M} runs.\n")
3939
fun.paint_AE(a_s, error_s, bins, M, data)
4040
print("Amplitude estimation result plots generated.")
4141

examples/variational_classifier/main.py

+8-12
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def main(nclasses, nqubits, nlayers, nshots, training, RxRzRx, method):
7272
path_angles = (
7373
LOCAL_FOLDER
7474
/ "data"
75-
/ "optimal_angles_ry_{}q_{}l.npy".format(nqubits, nlayers)
75+
/ f"optimal_angles_ry_{nqubits}q_{nlayers}l.npy"
7676
)
7777
optimal_angles = np.load(path_angles)
7878
except:
@@ -84,7 +84,7 @@ def main(nclasses, nqubits, nlayers, nshots, training, RxRzRx, method):
8484
path_angles = (
8585
LOCAL_FOLDER
8686
/ "data"
87-
/ "optimal_angles_rxrzrx_{}q_{}l.npy".format(nqubits, nlayers)
87+
/ f"optimal_angles_rxrzrx_{nqubits}q_{nlayers}l.npy"
8888
)
8989
optimal_angles = np.load(path_angles)
9090
except:
@@ -111,9 +111,7 @@ def main(nclasses, nqubits, nlayers, nshots, training, RxRzRx, method):
111111
method=method,
112112
)
113113
path_angles = (
114-
LOCAL_FOLDER
115-
/ "data"
116-
/ "optimal_angles_ry_{}q_{}l.npy".format(nqubits, nlayers)
114+
LOCAL_FOLDER / "data" / f"optimal_angles_ry_{nqubits}q_{nlayers}l.npy"
117115
)
118116
np.save(
119117
path_angles,
@@ -138,7 +136,7 @@ def main(nclasses, nqubits, nlayers, nshots, training, RxRzRx, method):
138136
path_angles = (
139137
LOCAL_FOLDER
140138
/ "data"
141-
/ "optimal_angles_rxrzrx_{}q_{}l.npy".format(nqubits, nlayers)
139+
/ f"optimal_angles_rxrzrx_{nqubits}q_{nlayers}l.npy"
142140
)
143141
np.save(
144142
path_angles,
@@ -170,14 +168,12 @@ def main(nclasses, nqubits, nlayers, nshots, training, RxRzRx, method):
170168
]
171169

172170
print(
173-
"Train set | # Clases: {} | # Qubits: {} | # Layers: {} | Accuracy: {}".format(
174-
nclasses, nqubits, nlayers, qc.Accuracy(labels_train, predictions_train)
175-
)
171+
f"Train set | # Clases: {nclasses} | # Qubits: {nqubits} | # Layers: {nlayers} | "
172+
+ f"Accuracy: {qc.Accuracy(labels_train, predictions_train)}"
176173
)
177174
print(
178-
"Test set | # Clases: {} | # Qubits: {} | # Layers: {} | Accuracy: {}".format(
179-
nclasses, nqubits, nlayers, qc.Accuracy(labels_test, predictions_test)
180-
)
175+
f"Test set | # Clases: {nclasses} | # Qubits: {nqubits} | # Layers: {nlayers} | "
176+
+ f"Accuracy: {qc.Accuracy(labels_test, predictions_test)}"
181177
)
182178

183179

0 commit comments

Comments
 (0)