Skip to content

Commit

Permalink
Merge pull request #1202 from qiboteam/pytorch_backend
Browse files Browse the repository at this point in the history
`pytorch` backend
  • Loading branch information
renatomello authored Mar 14, 2024
2 parents f0548cd + 07d28fa commit 3474d5e
Show file tree
Hide file tree
Showing 69 changed files with 1,286 additions and 636 deletions.
8 changes: 6 additions & 2 deletions doc/source/api-reference/qibo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2375,7 +2375,7 @@ The user can switch backends using
qibo.set_backend("numpy")
before creating any circuits or gates. The default backend is the first available
from ``qibojit``, ``tensorflow``, ``numpy``.
from ``qibojit``, ``pytorch``, ``tensorflow``, ``numpy``.

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

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/>`_.
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/>`_.
2 changes: 1 addition & 1 deletion doc/source/getting-started/backends.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ if the corresponding packages are installed, following the hierarchy below:

* :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.
* :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.
* `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.
* :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.
* :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.
* :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`.
* `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.

The default backend that is used is the first available from the above list.
The user can switch to a different using the ``qibo.set_backend`` method
Expand Down
6 changes: 3 additions & 3 deletions examples/adiabatic/trotter_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ def main(nqubits, hfield, T, save):
]
alphas = [1.0, 0.7, 0.4]
labels = [
r"$\delta t ^{}$".format(exponent - 1),
r"$\delta t ^{}$".format(exponent),
r"$\delta t ^{}$".format(exponent + 1),
f"$\\delta t ^{exponent - 1}$",
f"$\\delta t ^{exponent}$",
f"$\\delta t ^{exponent - 1}$",
]

plt.figure(figsize=(7, 4))
Expand Down
6 changes: 3 additions & 3 deletions examples/adiabatic3sat/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def read_file(file_name, instance):
solution (list): list of the correct outputs of the instance for testing.
clauses (list): list of all clauses, with the qubits each clause acts upon.
"""
file = open("../data3sat/{q}bit/n{q}i{i}.txt".format(q=file_name, i=instance))
file = open(f"../data3sat/{file_name}bit/n{file_name}i{instance}.txt")
control = list(map(int, file.readline().split()))
solution = list(map(str, file.readline().split()))
clauses = [list(map(int, file.readline().split())) for _ in range(control[1])]
Expand Down Expand Up @@ -100,12 +100,12 @@ def plot(qubits, ground, first, gap, dt, T):
plt.title("Energy during adiabatic evolution")
ax.legend()
fig.tight_layout()
fig.savefig("{}_qubits_energy.png".format(qubits), dpi=300, bbox_inches="tight")
fig.savefig(f"{qubits}_qubits_energy.png", dpi=300, bbox_inches="tight")
fig, ax = plt.subplots()
ax.plot(times, gap, label="gap energy", color="C0")
plt.ylabel("energy")
plt.xlabel("schedule")
plt.title("Energy during adiabatic evolution")
ax.legend()
fig.tight_layout()
fig.savefig("{}_qubits_gap.png".format(qubits), dpi=300, bbox_inches="tight")
fig.savefig(f"{qubits}_qubits_gap.png", dpi=300, bbox_inches="tight")
9 changes: 4 additions & 5 deletions examples/adiabatic3sat/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ def main(nqubits, instance, T, dt, solver, plot, dense, params, method, maxiter)
print("-" * 20 + "\n")
if plot and nqubits >= 14:
print(
"Currently not possible to calculate gap energy for {} qubits."
"\n Proceeding to adiabatic evolution without plotting data.\n"
"".format(nqubits)
f"Currently not possible to calculate gap energy for {nqubits} qubits."
+ "\n Proceeding to adiabatic evolution without plotting data.\n"
)
plot = False
if plot and method is not None:
Expand Down Expand Up @@ -97,9 +96,9 @@ def main(nqubits, instance, T, dt, solver, plot, dense, params, method, maxiter)
output_dec = (np.abs(final_state) ** 2).argmax()
max_output = "{0:0{bits}b}".format(output_dec, bits=nqubits)
max_prob = (np.abs(final_state) ** 2).max()
print("Exact cover instance with {} qubits.\n".format(nqubits))
print(f"Exact cover instance with {nqubits} qubits.\n")
if solution:
print("Known solution: {}\n".format("".join(solution)))
print(f"Known solution: {''.join(solution)}\n")
print("-" * 20 + "\n")
print(
f"Adiabatic evolution with total time {T}, evolution step {dt} and "
Expand Down
2 changes: 1 addition & 1 deletion examples/benchmarks/circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def CircuitFactory(nqubits, circuit_name, accelerators=None, **kwargs):
circuit = models.QFT(nqubits, accelerators=accelerators)
else:
if circuit_name not in _CIRCUITS:
raise KeyError("Unknown benchmark circuit type {}." "".format(circuit_name))
raise KeyError(f"Unknown benchmark circuit type {circuit_name}.")
circuit = models.Circuit(nqubits, accelerators=accelerators)
circuit.add(_CIRCUITS.get(circuit_name)(nqubits, **kwargs))
return circuit
4 changes: 2 additions & 2 deletions examples/benchmarks/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ def limit_gpu_memory(memory_limit=None):
print("\nNo GPU memory limiter used.\n")
return

print("\nAttempting to limit GPU memory to {}.\n".format(memory_limit))
print(f"\nAttempting to limit GPU memory to {memory_limit}.\n")
gpus = tf.config.list_physical_devices("GPU")
for gpu in tf.config.list_physical_devices("GPU"):
config = tf.config.experimental.VirtualDeviceConfiguration(
memory_limit=memory_limit
)
tf.config.experimental.set_virtual_device_configuration(gpu, [config])
print("Limiting memory of {} to {}.".format(gpu.name, memory_limit))
print(f"Limiting memory of {gpu.name} to {memory_limit}.")
print()


Expand Down
6 changes: 3 additions & 3 deletions examples/benchmarks/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ class BenchmarkLogger(list):
def __init__(self, filename=None):
self.filename = filename
if filename is not None and os.path.isfile(filename):
print("Extending existing logs from {}.".format(filename))
print(f"Extending existing logs from {filename}.")
with open(filename) as file:
super().__init__(json.load(file))
else:
if filename is not None:
print("Creating new logs in {}.".format(filename))
print(f"Creating new logs in {filename}.")
super().__init__()

def dump(self):
Expand All @@ -20,7 +20,7 @@ def dump(self):
json.dump(list(self), file)

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


def parse_accelerators(accelerators):
Expand Down
2 changes: 1 addition & 1 deletion examples/grover3sat/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def read_file(file_name, instance):
solution (list): list of the correct outputs of the instance for testing.
clauses (list): list of all clauses, with the qubits each clause acts upon.
"""
file = open("../data3sat/{q}bit/n{q}i{i}.txt".format(q=file_name, i=instance))
file = open(f"../data3sat/{file_name}bit/n{file_name}i{instance}.txt")
control = list(map(int, file.readline().split()))
solution = list(map(str, file.readline().split()))
clauses = [list(map(int, file.readline().split())) for _ in range(control[1])]
Expand Down
8 changes: 4 additions & 4 deletions examples/grover3sat/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ def main(nqubits, instance):
qubits = control[0]
clauses_num = control[1]
steps = int((np.pi / 4) * np.sqrt(2**qubits))
print("Qubits encoding the solution: {}\n".format(qubits))
print("Total number of qubits used: {}\n".format(qubits + clauses_num + 1))
print(f"Qubits encoding the solution: {qubits}\n")
print(f"Total number of qubits used: {qubits + clauses_num + 1}\n")
q, c, ancilla, circuit = functions.create_qc(qubits, clauses_num)
circuit = functions.grover(circuit, q, c, ancilla, clauses, steps)
result = circuit(nshots=100)
frequencies = result.frequencies(binary=True, registers=False)
most_common_bitstring = frequencies.most_common(1)[0][0]
print("Most common bitstring: {}\n".format(most_common_bitstring))
print(f"Most common bitstring: {most_common_bitstring}\n")
if solution:
print("Exact cover solution: {}\n".format("".join(solution)))
print(f"Exact cover solution: {''.join(solution)}\n")


if __name__ == "__main__":
Expand Down
14 changes: 6 additions & 8 deletions examples/hash-grover/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ def main(h_value, collisions, b):
h = "{0:0{bits}b}".format(h_value, bits=b)
if len(h) > 8:
raise ValueError(
"Hash should be at maximum an 8-bit number but given value contains {} bits.".format(
len(h)
)
f"Hash should be at maximum an 8-bit number but given value contains {len(h)} bits."
)
print("Target hash: {}\n".format(h))
print(f"Target hash: {h}\n")
if collisions:
grover_it = int(np.pi * np.sqrt((2**8) / collisions) / 4)
result = functions.grover(q, constant_1, constant_2, rot, h, grover_it)
Expand All @@ -36,19 +34,19 @@ def main(h_value, collisions, b):
print("Preimages:")
for i in most_common:
if functions.check_hash(q, i[0], h, constant_1, constant_2, rot):
print(" - {}\n".format(i[0]))
print(f" - {i[0]}\n")
else:
print(
" Incorrect preimage found, number of given collisions might not match.\n"
)
print("Total iterations taken: {}\n".format(grover_it))
print(f"Total iterations taken: {grover_it}\n")
else:
measured, total_iterations = functions.grover_unknown_M(
q, constant_1, constant_2, rot, h
)
print("Solution found in an iterative process.\n")
print("Preimage: {}\n".format(measured))
print("Total iterations taken: {}\n".format(total_iterations))
print(f"Preimage: {measured}\n")
print(f"Total iterations taken: {total_iterations}\n")


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion examples/unary/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ def paint_prob_distribution(bins, prob_sim, S0, sig, r, T):
ax.plot(x, y, label="PDF", color="black")
plt.ylabel("Probability")
plt.xlabel("Option price")
plt.title("Option price distribution for {} qubits ".format(bins))
plt.title(f"Option price distribution for {bins} qubits ")
ax.legend()
fig.tight_layout()
fig.savefig("Probability_distribution.png")
Expand Down
10 changes: 5 additions & 5 deletions examples/unary/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def main(data, bins, M, shots):

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

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

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

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

Expand Down
20 changes: 8 additions & 12 deletions examples/variational_classifier/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def main(nclasses, nqubits, nlayers, nshots, training, RxRzRx, method):
path_angles = (
LOCAL_FOLDER
/ "data"
/ "optimal_angles_ry_{}q_{}l.npy".format(nqubits, nlayers)
/ f"optimal_angles_ry_{nqubits}q_{nlayers}l.npy"
)
optimal_angles = np.load(path_angles)
except:
Expand All @@ -84,7 +84,7 @@ def main(nclasses, nqubits, nlayers, nshots, training, RxRzRx, method):
path_angles = (
LOCAL_FOLDER
/ "data"
/ "optimal_angles_rxrzrx_{}q_{}l.npy".format(nqubits, nlayers)
/ f"optimal_angles_rxrzrx_{nqubits}q_{nlayers}l.npy"
)
optimal_angles = np.load(path_angles)
except:
Expand All @@ -111,9 +111,7 @@ def main(nclasses, nqubits, nlayers, nshots, training, RxRzRx, method):
method=method,
)
path_angles = (
LOCAL_FOLDER
/ "data"
/ "optimal_angles_ry_{}q_{}l.npy".format(nqubits, nlayers)
LOCAL_FOLDER / "data" / f"optimal_angles_ry_{nqubits}q_{nlayers}l.npy"
)
np.save(
path_angles,
Expand All @@ -138,7 +136,7 @@ def main(nclasses, nqubits, nlayers, nshots, training, RxRzRx, method):
path_angles = (
LOCAL_FOLDER
/ "data"
/ "optimal_angles_rxrzrx_{}q_{}l.npy".format(nqubits, nlayers)
/ f"optimal_angles_rxrzrx_{nqubits}q_{nlayers}l.npy"
)
np.save(
path_angles,
Expand Down Expand Up @@ -170,14 +168,12 @@ def main(nclasses, nqubits, nlayers, nshots, training, RxRzRx, method):
]

print(
"Train set | # Clases: {} | # Qubits: {} | # Layers: {} | Accuracy: {}".format(
nclasses, nqubits, nlayers, qc.Accuracy(labels_train, predictions_train)
)
f"Train set | # Clases: {nclasses} | # Qubits: {nqubits} | # Layers: {nlayers} | "
+ f"Accuracy: {qc.Accuracy(labels_train, predictions_train)}"
)
print(
"Test set | # Clases: {} | # Qubits: {} | # Layers: {} | Accuracy: {}".format(
nclasses, nqubits, nlayers, qc.Accuracy(labels_test, predictions_test)
)
f"Test set | # Clases: {nclasses} | # Qubits: {nqubits} | # Layers: {nlayers} | "
+ f"Accuracy: {qc.Accuracy(labels_test, predictions_test)}"
)


Expand Down
Loading

0 comments on commit 3474d5e

Please sign in to comment.