From e6bda8f144f9d77caa3e9a294a6d782f05827265 Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Fri, 8 Oct 2021 09:58:15 +0200 Subject: [PATCH 01/18] first doctest implementation --- src/qibo/abstractions/abstract_gates.py | 23 ++--- src/qibo/abstractions/callbacks.py | 47 +++++---- src/qibo/abstractions/circuit.py | 125 ++++++++++++------------ src/qibo/abstractions/gates.py | 45 +++++---- 4 files changed, 133 insertions(+), 107 deletions(-) diff --git a/src/qibo/abstractions/abstract_gates.py b/src/qibo/abstractions/abstract_gates.py index def87f7379..0d57c3e4d7 100644 --- a/src/qibo/abstractions/abstract_gates.py +++ b/src/qibo/abstractions/abstract_gates.py @@ -213,18 +213,19 @@ def _on_qubits(self, *q) -> "Gate": Example: :: - from qibo import models, gates - c = models.Circuit(4) + >>> from qibo import models, gates + >>> c = models.Circuit(4) + # Add some CNOT gates - c.add(gates.CNOT(2, 3)._on_qubits(0, 1, 2, 3)) # equivalent to gates.CNOT(2, 3) - c.add(gates.CNOT(2, 3)._on_qubits(1, 2, 3, 0)) # equivalent to gates.CNOT(3, 0) - c.add(gates.CNOT(2, 3)._on_qubits(2, 0, 1, 3)) # equivalent to gates.CNOT(1, 3) - c.add(gates.CNOT(2, 3)._on_qubits(0, 3, 2, 1)) # equivalent to gates.CNOT(2, 1) - print(c.draw()) - # q0: ───X───── - # q1: ───|─o─X─ - # q2: ─o─|─|─o─ - # q3: ─X─o─X─── + >>> c.add(gates.CNOT(2, 3)._on_qubits(0, 1, 2, 3)) # equivalent to gates.CNOT(2, 3) + >>> c.add(gates.CNOT(2, 3)._on_qubits(1, 2, 3, 0)) # equivalent to gates.CNOT(3, 0) + >>> c.add(gates.CNOT(2, 3)._on_qubits(2, 0, 1, 3)) # equivalent to gates.CNOT(1, 3) + >>> c.add(gates.CNOT(2, 3)._on_qubits(0, 3, 2, 1)) # equivalent to gates.CNOT(2, 1) + >>> print(c.draw()) + q0: ───X───── + q1: ───|─o─X─ + q2: ─o─|─|─o─ + q3: ─X─o─X─── """ if self.is_controlled_by: targets = (q[i] for i in self.target_qubits) diff --git a/src/qibo/abstractions/callbacks.py b/src/qibo/abstractions/callbacks.py index c9f906e544..3706fd3071 100644 --- a/src/qibo/abstractions/callbacks.py +++ b/src/qibo/abstractions/callbacks.py @@ -67,19 +67,25 @@ class EntanglementEntropy(Callback): Example: :: - from qibo import models, gates, callbacks + >>> from qibo import models, gates, callbacks + # create entropy callback where qubit 0 is the first subsystem - entropy = callbacks.EntanglementEntropy([0], compute_spectrum=True) + >>> entropy = callbacks.EntanglementEntropy([0], compute_spectrum=True) + # initialize circuit with 2 qubits and add gates - c = models.Circuit(2) + >>> c = models.Circuit(2) + # add callback gates between normal gates - c.add(gates.CallbackGate(entropy)) - c.add(gates.H(0)) - c.add(gates.CallbackGate(entropy)) - c.add(gates.CNOT(0, 1)) - c.add(gates.CallbackGate(entropy)) + >>> c.add(gates.CallbackGate(entropy)) + >>> c.add(gates.H(0)) + >>> c.add(gates.CallbackGate(entropy)) + >>> c.add(gates.CNOT(0, 1)) + >>> c.add(gates.CallbackGate(entropy)) + # execute the circuit - final_state = c() + >>> final_state = c() + + # WARNING: doctest cannot test printed tensors trivially print(entropy[:]) # Should print [0, 0, 1] which is the entanglement entropy # after every gate in the calculation. @@ -166,20 +172,25 @@ class Gap(Callback): Example: :: - from qibo import callbacks, hamiltonians - from qibo.models import AdiabaticEvolution + >>> from qibo import callbacks, hamiltonians + >>> from qibo.models import AdiabaticEvolution + # define easy and hard Hamiltonians for adiabatic evolution - h0 = hamiltonians.X(3) - h1 = hamiltonians.TFIM(3, h=1.0) + >>> h0 = hamiltonians.X(3) + >>> h1 = hamiltonians.TFIM(3, h=1.0) + # define callbacks for logging the ground state, first excited # and gap energy - ground = callbacks.Gap(0) - excited = callbacks.Gap(1) - gap = callbacks.Gap() + >>> ground = callbacks.Gap(0) + >>> excited = callbacks.Gap(1) + >>> gap = callbacks.Gap() + # define and execute the ``AdiabaticEvolution`` model - evolution = AdiabaticEvolution(h0, h1, lambda t: t, dt=1e-1, + >>> evolution = AdiabaticEvolution(h0, h1, lambda t: t, dt=1e-1, \ callbacks=[gap, ground, excited]) - final_state = evolution(final_time=1.0) + >>> final_state = evolution(final_time=1.0) + + # WARNING: doctest cannot test printed tensors trivially # print results print(ground[:]) print(excited[:]) diff --git a/src/qibo/abstractions/circuit.py b/src/qibo/abstractions/circuit.py index e7c4306e3e..bc845cd051 100644 --- a/src/qibo/abstractions/circuit.py +++ b/src/qibo/abstractions/circuit.py @@ -161,16 +161,19 @@ def on_qubits(self, *q): Example: :: - from qibo import gates, models + >>> from qibo import gates, models + # create small circuit on 4 qubits - smallc = models.Circuit(4) - smallc.add((gates.RX(i, theta=0.1) for i in range(4))) - smallc.add((gates.CNOT(0, 1), gates.CNOT(2, 3))) + >>> smallc = models.Circuit(4) + >>> smallc.add((gates.RX(i, theta=0.1) for i in range(4))) + >>> smallc.add((gates.CNOT(0, 1), gates.CNOT(2, 3))) + # create large circuit on 8 qubits - largec = models.Circuit(8) - largec.add((gates.RY(i, theta=0.1) for i in range(8))) + >>> largec = models.Circuit(8) + >>> largec.add((gates.RY(i, theta=0.1) for i in range(8))) + # add the small circuit to the even qubits of the large one - largec.add(smallc.on_qubits(*range(0, 8, 2))) + >>> largec.add(smallc.on_qubits(*range(0, 8, 2))) """ if len(q) != self.nqubits: raise_error(ValueError, "Cannot return gates on {} qubits because " @@ -302,23 +305,24 @@ def with_noise(self, noise_map: NoiseMapType): Example: :: - from qibo.models import Circuit - from qibo import gates + >>> from qibo.models import Circuit + >>> from qibo import gates + # use density matrices for noise simulation - c = Circuit(2, density_matrix=True) - c.add([gates.H(0), gates.H(1), gates.CNOT(0, 1)]) - noise_map = {0: (0.1, 0.0, 0.2), 1: (0.0, 0.2, 0.1)} - noisy_c = c.with_noise(noise_map) + >>> c = Circuit(2, density_matrix=True) + >>> c.add([gates.H(0), gates.H(1), gates.CNOT(0, 1)]) + >>> noise_map = {0: (0.1, 0.0, 0.2), 1: (0.0, 0.2, 0.1)} + >>> noisy_c = c.with_noise(noise_map) # ``noisy_c`` will be equivalent to the following circuit - c2 = Circuit(2, density_matrix=True) - c2.add(gates.H(0)) - c2.add(gates.PauliNoiseChannel(0, 0.1, 0.0, 0.2)) - c2.add(gates.H(1)) - c2.add(gates.PauliNoiseChannel(1, 0.0, 0.2, 0.1)) - c2.add(gates.CNOT(0, 1)) - c2.add(gates.PauliNoiseChannel(0, 0.1, 0.0, 0.2)) - c2.add(gates.PauliNoiseChannel(1, 0.0, 0.2, 0.1)) + >>> c2 = Circuit(2, density_matrix=True) + >>> c2.add(gates.H(0)) + >>> c2.add(gates.PauliNoiseChannel(0, 0.1, 0.0, 0.2)) + >>> c2.add(gates.H(1)) + >>> c2.add(gates.PauliNoiseChannel(1, 0.0, 0.2, 0.1)) + >>> c2.add(gates.CNOT(0, 1)) + >>> c2.add(gates.PauliNoiseChannel(0, 0.1, 0.0, 0.2)) + >>> c2.add(gates.PauliNoiseChannel(1, 0.0, 0.2, 0.1)) """ noise_map = self._check_noise_map(noise_map) # Generate noise gates @@ -547,26 +551,30 @@ def set_parameters(self, parameters): Example: :: - from qibo.models import Circuit - from qibo import gates + >>> from qibo.models import Circuit + >>> from qibo import gates + # create a circuit with all parameters set to 0. - c = Circuit(3, accelerators) - c.add(gates.RX(0, theta=0)) - c.add(gates.RY(1, theta=0)) - c.add(gates.CZ(1, 2)) - c.add(gates.fSim(0, 2, theta=0, phi=0)) - c.add(gates.H(2)) + # ERROR: Circuit(3, accelerators) does not work since accelerators is not defined + >>> c = Circuit(3) + >>> c.add(gates.RX(0, theta=0)) + >>> c.add(gates.RY(1, theta=0)) + >>> c.add(gates.CZ(1, 2)) + >>> c.add(gates.fSim(0, 2, theta=0, phi=0)) + >>> c.add(gates.H(2)) # set new values to the circuit's parameters using list - params = [0.123, 0.456, (0.789, 0.321)] - c.set_parameters(params) + >>> params = [0.123, 0.456, (0.789, 0.321)] + >>> c.set_parameters(params) + # or using dictionary - params = {c.queue[0]: 0.123, c.queue[1]: 0.456 + >>> params = {c.queue[0]: 0.123, c.queue[1]: 0.456, \ c.queue[3]: (0.789, 0.321)} - c.set_parameters(params) + >>> c.set_parameters(params) + # or using flat list (or an equivalent `np.array`/`tf.Tensor`) - params = [0.123, 0.456, 0.789, 0.321] - c.set_parameters(params) + >>> params = [0.123, 0.456, 0.789, 0.321] + >>> c.set_parameters(params) """ if isinstance(parameters, (list, tuple)): self._set_parameters_list(parameters, len(parameters)) @@ -644,26 +652,23 @@ def summary(self) -> str: Example: :: - from qibo.models import Circuit - from qibo import gates - c = Circuit(3) - c.add(gates.H(0)) - c.add(gates.H(1)) - c.add(gates.CNOT(0, 2)) - c.add(gates.CNOT(1, 2)) - c.add(gates.H(2)) - c.add(gates.TOFFOLI(0, 1, 2)) - print(c.summary()) - # Prints - ''' + >>> from qibo.models import Circuit + >>> from qibo import gates + >>> c = Circuit(3) + >>> c.add(gates.H(0)) + >>> c.add(gates.H(1)) + >>> c.add(gates.CNOT(0, 2)) + >>> c.add(gates.CNOT(1, 2)) + >>> c.add(gates.H(2)) + >>> c.add(gates.TOFFOLI(0, 1, 2)) + >>> print(c.summary()) Circuit depth = 5 - Total number of gates = 7 + Total number of gates = 6 Number of qubits = 3 Most common gates: h: 3 cx: 2 ccx: 1 - ''' """ logs = [f"Circuit depth = {self.depth}", f"Total number of gates = {self.ngates}", @@ -758,21 +763,21 @@ def from_qasm(cls, qasm_code: str, **kwargs): Example: :: - from qibo import models, gates + >>> from qibo import models, gates - qasm_code = '''OPENQASM 2.0; - include "qelib1.inc"; - qreg q[2]; - h q[0]; - h q[1]; + >>> qasm_code = '''OPENQASM 2.0; \ + include "qelib1.inc"; \ + qreg q[2]; \ + h q[0]; \ + h q[1]; \ cx q[0],q[1];''' - c = models.Circuit.from_qasm(qasm_code) + >>> c = models.Circuit.from_qasm(qasm_code) # is equivalent to creating the following circuit - c2 = models.Circuit(2) - c2.add(gates.H(0)) - c2.add(gates.H(1)) - c2.add(gates.CNOT(0, 1)) + >>> c2 = models.Circuit(2) + >>> c2.add(gates.H(0)) + >>> c2.add(gates.H(1)) + >>> c2.add(gates.CNOT(0, 1)) """ kwargs["nqubits"], gate_list = cls._parse_qasm(qasm_code) circuit = cls(**kwargs) diff --git a/src/qibo/abstractions/gates.py b/src/qibo/abstractions/gates.py index 02fdcc2f89..6eed3ea2d1 100644 --- a/src/qibo/abstractions/gates.py +++ b/src/qibo/abstractions/gates.py @@ -1109,20 +1109,24 @@ class VariationalLayer(ParametrizedGate): Example: :: - import numpy as np - from qibo.models import Circuit - from qibo import gates + >>> import numpy as np + >>> from qibo.models import Circuit + >>> from qibo import gates + # generate an array of variational parameters for 8 qubits - theta = 2 * np.pi * np.random.random(8) + >>> theta = 2 * np.pi * np.random.random(8) + # define qubit pairs that two qubit gates will act - pairs = [(i, i + 1) for i in range(0, 7, 2)] + >>> pairs = [(i, i + 1) for i in range(0, 7, 2)] + # define a circuit of 8 qubits and add the variational layer - c = Circuit(8) - c.add(gates.VariationalLayer(range(8), pairs, gates.RY, gates.CZ, theta)) + >>> c = Circuit(8) + >>> c.add(gates.VariationalLayer(range(8), pairs, gates.RY, gates.CZ, theta)) + # this will create an optimized version of the following circuit - c2 = Circuit(8) - c.add((gates.RY(i, th) for i, th in enumerate(theta))) - c.add((gates.CZ(i, i + 1) for i in range(7))) + >>> c2 = Circuit(8) + >>> c.add((gates.RY(i, th) for i, th in enumerate(theta))) + >>> c.add((gates.CZ(i, i + 1) for i in range(7))) """ def __init__(self, qubits: List[int], pairs: List[Tuple[int, int]], @@ -1269,20 +1273,25 @@ class KrausChannel(Channel): Example: :: - import numpy as np - from qibo.models import Circuit - from qibo import gates + >>> import numpy as np + >>> from qibo.models import Circuit + >>> from qibo import gates + # initialize circuit with 3 qubits - c = Circuit(3, density_matrix=True) + >>> c = Circuit(3, density_matrix=True) + # define a sqrt(0.4) * X gate - a1 = np.sqrt(0.4) * np.array([[0, 1], [1, 0]]) + >>> a1 = np.sqrt(0.4) * np.array([[0, 1], [1, 0]]) + # define a sqrt(0.6) * CNOT gate - a2 = np.sqrt(0.6) * np.array([[1, 0, 0, 0], [0, 1, 0, 0], + >>> a2 = np.sqrt(0.6) * np.array([[1, 0, 0, 0], [0, 1, 0, 0], \ [0, 0, 0, 1], [0, 0, 1, 0]]) + # define the channel rho -> 0.4 X{1} rho X{1} + 0.6 CNOT{0, 2} rho CNOT{0, 2} - channel = gates.KrausChannel([((1,), a1), ((0, 2), a2)]) + >>> channel = gates.KrausChannel([((1,), a1), ((0, 2), a2)]) + # add the channel to the circuit - c.add(channel) + >>> c.add(channel) """ def __init__(self, ops): From bf58eb69d514b6e10308f024acf0c086bbc8b4b4 Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Fri, 8 Oct 2021 18:43:58 +0200 Subject: [PATCH 02/18] doctest using sphinx extension --- src/qibo/abstractions/abstract_gates.py | 18 +-- src/qibo/abstractions/callbacks.py | 62 +++++------ src/qibo/abstractions/circuit.py | 141 ++++++++++++------------ src/qibo/abstractions/gates.py | 51 ++++----- 4 files changed, 126 insertions(+), 146 deletions(-) diff --git a/src/qibo/abstractions/abstract_gates.py b/src/qibo/abstractions/abstract_gates.py index 0d57c3e4d7..c443911ab3 100644 --- a/src/qibo/abstractions/abstract_gates.py +++ b/src/qibo/abstractions/abstract_gates.py @@ -211,17 +211,19 @@ def _on_qubits(self, *q) -> "Gate": type targeting the given qubits. Example: - :: - >>> from qibo import models, gates - >>> c = models.Circuit(4) + .. testcode:: + from qibo import models, gates + c = models.Circuit(4) # Add some CNOT gates - >>> c.add(gates.CNOT(2, 3)._on_qubits(0, 1, 2, 3)) # equivalent to gates.CNOT(2, 3) - >>> c.add(gates.CNOT(2, 3)._on_qubits(1, 2, 3, 0)) # equivalent to gates.CNOT(3, 0) - >>> c.add(gates.CNOT(2, 3)._on_qubits(2, 0, 1, 3)) # equivalent to gates.CNOT(1, 3) - >>> c.add(gates.CNOT(2, 3)._on_qubits(0, 3, 2, 1)) # equivalent to gates.CNOT(2, 1) - >>> print(c.draw()) + c.add(gates.CNOT(2, 3)._on_qubits(0, 1, 2, 3)) # equivalent to gates.CNOT(2, 3) + c.add(gates.CNOT(2, 3)._on_qubits(1, 2, 3, 0)) # equivalent to gates.CNOT(3, 0) + c.add(gates.CNOT(2, 3)._on_qubits(2, 0, 1, 3)) # equivalent to gates.CNOT(1, 3) + c.add(gates.CNOT(2, 3)._on_qubits(0, 3, 2, 1)) # equivalent to gates.CNOT(2, 1) + print(c.draw()) + .. testoutput:: + q0: ───X───── q1: ───|─o─X─ q2: ─o─|─|─o─ diff --git a/src/qibo/abstractions/callbacks.py b/src/qibo/abstractions/callbacks.py index 3706fd3071..4c642f794f 100644 --- a/src/qibo/abstractions/callbacks.py +++ b/src/qibo/abstractions/callbacks.py @@ -64,32 +64,27 @@ class EntanglementEntropy(Callback): half of the qubits. compute_spectrum (bool): Compute the entanglement spectrum. Default is False. - Example: - :: - - >>> from qibo import models, gates, callbacks + Example: + .. testcode:: + from qibo import models, gates, callbacks # create entropy callback where qubit 0 is the first subsystem - >>> entropy = callbacks.EntanglementEntropy([0], compute_spectrum=True) - + entropy = callbacks.EntanglementEntropy([0], compute_spectrum=True) # initialize circuit with 2 qubits and add gates - >>> c = models.Circuit(2) - + c = models.Circuit(2) # add callback gates between normal gates - >>> c.add(gates.CallbackGate(entropy)) - >>> c.add(gates.H(0)) - >>> c.add(gates.CallbackGate(entropy)) - >>> c.add(gates.CNOT(0, 1)) - >>> c.add(gates.CallbackGate(entropy)) - + c.add(gates.CallbackGate(entropy)) + c.add(gates.H(0)) + c.add(gates.CallbackGate(entropy)) + c.add(gates.CNOT(0, 1)) + c.add(gates.CallbackGate(entropy)) # execute the circuit - >>> final_state = c() - + final_state = c() # WARNING: doctest cannot test printed tensors trivially - print(entropy[:]) + # print(entropy[:]) # Should print [0, 0, 1] which is the entanglement entropy # after every gate in the calculation. - print(entropy.spectrum) + # print(entropy.spectrum) # Print the entanglement spectrum. """ @@ -170,31 +165,28 @@ class Gap(Callback): Default is ``True``. Example: - :: - - >>> from qibo import callbacks, hamiltonians - >>> from qibo.models import AdiabaticEvolution + + .. testcode:: + from qibo import callbacks, hamiltonians + from qibo.models import AdiabaticEvolution # define easy and hard Hamiltonians for adiabatic evolution - >>> h0 = hamiltonians.X(3) - >>> h1 = hamiltonians.TFIM(3, h=1.0) - + h0 = hamiltonians.X(3) + h1 = hamiltonians.TFIM(3, h=1.0) # define callbacks for logging the ground state, first excited # and gap energy - >>> ground = callbacks.Gap(0) - >>> excited = callbacks.Gap(1) - >>> gap = callbacks.Gap() - + ground = callbacks.Gap(0) + excited = callbacks.Gap(1) + gap = callbacks.Gap() # define and execute the ``AdiabaticEvolution`` model - >>> evolution = AdiabaticEvolution(h0, h1, lambda t: t, dt=1e-1, \ + evolution = AdiabaticEvolution(h0, h1, lambda t: t, dt=1e-1, callbacks=[gap, ground, excited]) - >>> final_state = evolution(final_time=1.0) - + final_state = evolution(final_time=1.0) # WARNING: doctest cannot test printed tensors trivially # print results - print(ground[:]) - print(excited[:]) - print(gap[:]) + # print(ground[:]) + # print(excited[:]) + # print(gap[:]) """ def __init__(self, mode: Union[str, int] = "gap", check_degenerate: bool = True): diff --git a/src/qibo/abstractions/circuit.py b/src/qibo/abstractions/circuit.py index bc845cd051..aadedced34 100644 --- a/src/qibo/abstractions/circuit.py +++ b/src/qibo/abstractions/circuit.py @@ -159,21 +159,19 @@ def on_qubits(self, *q): q (int): Qubit ids that the gates should act. Example: - :: - - >>> from qibo import gates, models + + ..testcode:: + from qibo import gates, models # create small circuit on 4 qubits - >>> smallc = models.Circuit(4) - >>> smallc.add((gates.RX(i, theta=0.1) for i in range(4))) - >>> smallc.add((gates.CNOT(0, 1), gates.CNOT(2, 3))) - + smallc = models.Circuit(4) + smallc.add((gates.RX(i, theta=0.1) for i in range(4))) + smallc.add((gates.CNOT(0, 1), gates.CNOT(2, 3))) # create large circuit on 8 qubits - >>> largec = models.Circuit(8) - >>> largec.add((gates.RY(i, theta=0.1) for i in range(8))) - + largec = models.Circuit(8) + largec.add((gates.RY(i, theta=0.1) for i in range(8))) # add the small circuit to the even qubits of the large one - >>> largec.add(smallc.on_qubits(*range(0, 8, 2))) + largec.add(smallc.on_qubits(*range(0, 8, 2))) """ if len(q) != self.nqubits: raise_error(ValueError, "Cannot return gates on {} qubits because " @@ -303,26 +301,24 @@ def with_noise(self, noise_map: NoiseMapType): and additional noise channels on all qubits after every gate. Example: - :: - - >>> from qibo.models import Circuit - >>> from qibo import gates + ..testcode:: + from qibo.models import Circuit + from qibo import gates # use density matrices for noise simulation - >>> c = Circuit(2, density_matrix=True) - >>> c.add([gates.H(0), gates.H(1), gates.CNOT(0, 1)]) - >>> noise_map = {0: (0.1, 0.0, 0.2), 1: (0.0, 0.2, 0.1)} - >>> noisy_c = c.with_noise(noise_map) - + c = Circuit(2, density_matrix=True) + c.add([gates.H(0), gates.H(1), gates.CNOT(0, 1)]) + noise_map = {0: (0.1, 0.0, 0.2), 1: (0.0, 0.2, 0.1)} + noisy_c = c.with_noise(noise_map) # ``noisy_c`` will be equivalent to the following circuit - >>> c2 = Circuit(2, density_matrix=True) - >>> c2.add(gates.H(0)) - >>> c2.add(gates.PauliNoiseChannel(0, 0.1, 0.0, 0.2)) - >>> c2.add(gates.H(1)) - >>> c2.add(gates.PauliNoiseChannel(1, 0.0, 0.2, 0.1)) - >>> c2.add(gates.CNOT(0, 1)) - >>> c2.add(gates.PauliNoiseChannel(0, 0.1, 0.0, 0.2)) - >>> c2.add(gates.PauliNoiseChannel(1, 0.0, 0.2, 0.1)) + c2 = Circuit(2, density_matrix=True) + c2.add(gates.H(0)) + c2.add(gates.PauliNoiseChannel(0, 0.1, 0.0, 0.2)) + c2.add(gates.H(1)) + c2.add(gates.PauliNoiseChannel(1, 0.0, 0.2, 0.1)) + c2.add(gates.CNOT(0, 1)) + c2.add(gates.PauliNoiseChannel(0, 0.1, 0.0, 0.2)) + c2.add(gates.PauliNoiseChannel(1, 0.0, 0.2, 0.1)) """ noise_map = self._check_noise_map(noise_map) # Generate noise gates @@ -549,32 +545,29 @@ def set_parameters(self, parameters): Example: - :: - - >>> from qibo.models import Circuit - >>> from qibo import gates + .. testcode:: + from qibo.models import Circuit + from qibo import gates # create a circuit with all parameters set to 0. # ERROR: Circuit(3, accelerators) does not work since accelerators is not defined - >>> c = Circuit(3) - >>> c.add(gates.RX(0, theta=0)) - >>> c.add(gates.RY(1, theta=0)) - >>> c.add(gates.CZ(1, 2)) - >>> c.add(gates.fSim(0, 2, theta=0, phi=0)) - >>> c.add(gates.H(2)) + c = Circuit(3) + c.add(gates.RX(0, theta=0)) + c.add(gates.RY(1, theta=0)) + c.add(gates.CZ(1, 2)) + c.add(gates.fSim(0, 2, theta=0, phi=0)) + c.add(gates.H(2)) # set new values to the circuit's parameters using list - >>> params = [0.123, 0.456, (0.789, 0.321)] - >>> c.set_parameters(params) - + params = [0.123, 0.456, (0.789, 0.321)] + c.set_parameters(params) # or using dictionary - >>> params = {c.queue[0]: 0.123, c.queue[1]: 0.456, \ + params = {c.queue[0]: 0.123, c.queue[1]: 0.456, c.queue[3]: (0.789, 0.321)} - >>> c.set_parameters(params) - + c.set_parameters(params) # or using flat list (or an equivalent `np.array`/`tf.Tensor`) - >>> params = [0.123, 0.456, 0.789, 0.321] - >>> c.set_parameters(params) + params = [0.123, 0.456, 0.789, 0.321] + c.set_parameters(params) """ if isinstance(parameters, (list, tuple)): self._set_parameters_list(parameters, len(parameters)) @@ -650,18 +643,21 @@ def summary(self) -> str: the all gates sorted in decreasing number of appearance. Example: - :: - - >>> from qibo.models import Circuit - >>> from qibo import gates - >>> c = Circuit(3) - >>> c.add(gates.H(0)) - >>> c.add(gates.H(1)) - >>> c.add(gates.CNOT(0, 2)) - >>> c.add(gates.CNOT(1, 2)) - >>> c.add(gates.H(2)) - >>> c.add(gates.TOFFOLI(0, 1, 2)) - >>> print(c.summary()) + ..testcode:: + + from qibo.models import Circuit + from qibo import gates + c = Circuit(3) + c.add(gates.H(0)) + c.add(gates.H(1)) + c.add(gates.CNOT(0, 2)) + c.add(gates.CNOT(1, 2)) + c.add(gates.H(2)) + c.add(gates.TOFFOLI(0, 1, 2)) + print(c.summary()) + + ..testoutput:: + Circuit depth = 5 Total number of gates = 6 Number of qubits = 3 @@ -761,23 +757,22 @@ def from_qasm(cls, qasm_code: str, **kwargs): specified by the given QASM script. Example: - :: - - >>> from qibo import models, gates - - >>> qasm_code = '''OPENQASM 2.0; \ - include "qelib1.inc"; \ - qreg q[2]; \ - h q[0]; \ - h q[1]; \ + + ..testcode:: + + from qibo import models, gates + qasm_code = '''OPENQASM 2.0; + include "qelib1.inc"; + qreg q[2]; + h q[0]; + h q[1]; cx q[0],q[1];''' - >>> c = models.Circuit.from_qasm(qasm_code) - + c = models.Circuit.from_qasm(qasm_code) # is equivalent to creating the following circuit - >>> c2 = models.Circuit(2) - >>> c2.add(gates.H(0)) - >>> c2.add(gates.H(1)) - >>> c2.add(gates.CNOT(0, 1)) + c2 = models.Circuit(2) + c2.add(gates.H(0)) + c2.add(gates.H(1)) + c2.add(gates.CNOT(0, 1)) """ kwargs["nqubits"], gate_list = cls._parse_qasm(qasm_code) circuit = cls(**kwargs) diff --git a/src/qibo/abstractions/gates.py b/src/qibo/abstractions/gates.py index 6eed3ea2d1..2863301447 100644 --- a/src/qibo/abstractions/gates.py +++ b/src/qibo/abstractions/gates.py @@ -1107,26 +1107,22 @@ class VariationalLayer(ParametrizedGate): If ``None`` the name ``"VariationalLayer"`` will be used. Example: - :: - - >>> import numpy as np - >>> from qibo.models import Circuit - >>> from qibo import gates + ..testcode:: + import numpy as np + from qibo.models import Circuit + from qibo import gates # generate an array of variational parameters for 8 qubits - >>> theta = 2 * np.pi * np.random.random(8) - + theta = 2 * np.pi * np.random.random(8) # define qubit pairs that two qubit gates will act - >>> pairs = [(i, i + 1) for i in range(0, 7, 2)] - + pairs = [(i, i + 1) for i in range(0, 7, 2)] # define a circuit of 8 qubits and add the variational layer - >>> c = Circuit(8) - >>> c.add(gates.VariationalLayer(range(8), pairs, gates.RY, gates.CZ, theta)) - + c = Circuit(8) + c.add(gates.VariationalLayer(range(8), pairs, gates.RY, gates.CZ, theta)) # this will create an optimized version of the following circuit - >>> c2 = Circuit(8) - >>> c.add((gates.RY(i, th) for i, th in enumerate(theta))) - >>> c.add((gates.CZ(i, i + 1) for i in range(7))) + c2 = Circuit(8) + c.add((gates.RY(i, th) for i, th in enumerate(theta))) + c.add((gates.CZ(i, i + 1) for i in range(7))) """ def __init__(self, qubits: List[int], pairs: List[Tuple[int, int]], @@ -1271,27 +1267,22 @@ class KrausChannel(Channel): the corresponding matrix as a ``np.ndarray`` or ``tf.Tensor``. Example: - :: - - >>> import numpy as np - >>> from qibo.models import Circuit - >>> from qibo import gates + ..testcode:: + import numpy as np + from qibo.models import Circuit + from qibo import gates # initialize circuit with 3 qubits - >>> c = Circuit(3, density_matrix=True) - + c = Circuit(3, density_matrix=True) # define a sqrt(0.4) * X gate - >>> a1 = np.sqrt(0.4) * np.array([[0, 1], [1, 0]]) - + a1 = np.sqrt(0.4) * np.array([[0, 1], [1, 0]]) # define a sqrt(0.6) * CNOT gate - >>> a2 = np.sqrt(0.6) * np.array([[1, 0, 0, 0], [0, 1, 0, 0], \ + a2 = np.sqrt(0.6) * np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]) - - # define the channel rho -> 0.4 X{1} rho X{1} + 0.6 CNOT{0, 2} rho CNOT{0, 2} - >>> channel = gates.KrausChannel([((1,), a1), ((0, 2), a2)]) - + define the channel rho -> 0.4 X{1} rho X{1} + 0.6 CNOT{0, 2} rho CNOT{0, 2} + channel = gates.KrausChannel([((1,), a1), ((0, 2), a2)]) # add the channel to the circuit - >>> c.add(channel) + c.add(channel) """ def __init__(self, ops): From 0f8dfe87b6d878f63848f67a16ac609d4893af0e Mon Sep 17 00:00:00 2001 From: Stefano Carrazza Date: Fri, 8 Oct 2021 21:14:34 +0200 Subject: [PATCH 03/18] adding doctest rule --- .github/workflows/rules.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index d64c103d45..511d9137f3 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -30,7 +30,7 @@ jobs: run: | python -m pip install --upgrade pip pip install pytest-cov - pip install .[tests] + pip install .[tests,docs] pip install qibotf pip install qibojit - name: Install package on Windows @@ -38,7 +38,7 @@ jobs: run: | python -m pip install --upgrade pip pip install pytest-cov - pip install .[qibojit,tests] + pip install .[qibojit,tests,docs] - name: Test with pylint run: | pip install pylint @@ -46,6 +46,9 @@ jobs: - name: Test with pytest core run: | pytest --cov=qibo --cov-report=xml --pyargs qibo + - name: Test documentation examples + run: | + make -C doc doctest - name: Test examples if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == '3.9' run: | From 80b9ba3299d7653b7eca1682e58252db7e37044b Mon Sep 17 00:00:00 2001 From: Stefano Carrazza Date: Fri, 8 Oct 2021 21:27:52 +0200 Subject: [PATCH 04/18] limiting test to ubuntu --- .github/workflows/rules.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index 511d9137f3..ca4fb5acb4 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -47,6 +47,7 @@ jobs: run: | pytest --cov=qibo --cov-report=xml --pyargs qibo - name: Test documentation examples + if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == '3.9' run: | make -C doc doctest - name: Test examples From 4b129b055e586e4489f63deaf560a0970361ea6a Mon Sep 17 00:00:00 2001 From: Stefano Carrazza Date: Fri, 8 Oct 2021 21:42:19 +0200 Subject: [PATCH 05/18] adding pandoc install --- .github/workflows/rules.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index ca4fb5acb4..9bc0609c1e 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -49,6 +49,7 @@ jobs: - name: Test documentation examples if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == '3.9' run: | + sudo apt install pandoc make -C doc doctest - name: Test examples if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == '3.9' From a241c0c6934388e843fb7cd44ffd1673a1e5d30d Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Sat, 9 Oct 2021 09:47:03 +0200 Subject: [PATCH 06/18] fixes from previous commmit --- src/qibo/abstractions/callbacks.py | 2 +- src/qibo/abstractions/circuit.py | 10 +++++----- src/qibo/abstractions/gates.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/qibo/abstractions/callbacks.py b/src/qibo/abstractions/callbacks.py index 4c642f794f..f5a32eec3c 100644 --- a/src/qibo/abstractions/callbacks.py +++ b/src/qibo/abstractions/callbacks.py @@ -64,7 +64,7 @@ class EntanglementEntropy(Callback): half of the qubits. compute_spectrum (bool): Compute the entanglement spectrum. Default is False. - Example: + Example: .. testcode:: from qibo import models, gates, callbacks diff --git a/src/qibo/abstractions/circuit.py b/src/qibo/abstractions/circuit.py index aadedced34..61e975cde5 100644 --- a/src/qibo/abstractions/circuit.py +++ b/src/qibo/abstractions/circuit.py @@ -160,7 +160,7 @@ def on_qubits(self, *q): Example: - ..testcode:: + .. testcode:: from qibo import gates, models # create small circuit on 4 qubits @@ -301,7 +301,7 @@ def with_noise(self, noise_map: NoiseMapType): and additional noise channels on all qubits after every gate. Example: - ..testcode:: + .. testcode:: from qibo.models import Circuit from qibo import gates @@ -643,7 +643,7 @@ def summary(self) -> str: the all gates sorted in decreasing number of appearance. Example: - ..testcode:: + .. testcode:: from qibo.models import Circuit from qibo import gates @@ -656,7 +656,7 @@ def summary(self) -> str: c.add(gates.TOFFOLI(0, 1, 2)) print(c.summary()) - ..testoutput:: + .. testoutput:: Circuit depth = 5 Total number of gates = 6 @@ -758,7 +758,7 @@ def from_qasm(cls, qasm_code: str, **kwargs): Example: - ..testcode:: + .. testcode:: from qibo import models, gates qasm_code = '''OPENQASM 2.0; diff --git a/src/qibo/abstractions/gates.py b/src/qibo/abstractions/gates.py index 2863301447..76a012566f 100644 --- a/src/qibo/abstractions/gates.py +++ b/src/qibo/abstractions/gates.py @@ -1107,7 +1107,7 @@ class VariationalLayer(ParametrizedGate): If ``None`` the name ``"VariationalLayer"`` will be used. Example: - ..testcode:: + .. testcode:: import numpy as np from qibo.models import Circuit @@ -1267,7 +1267,7 @@ class KrausChannel(Channel): the corresponding matrix as a ``np.ndarray`` or ``tf.Tensor``. Example: - ..testcode:: + .. testcode:: import numpy as np from qibo.models import Circuit @@ -1279,7 +1279,7 @@ class KrausChannel(Channel): # define a sqrt(0.6) * CNOT gate a2 = np.sqrt(0.6) * np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]) - define the channel rho -> 0.4 X{1} rho X{1} + 0.6 CNOT{0, 2} rho CNOT{0, 2} + # define the channel rho -> 0.4 X{1} rho X{1} + 0.6 CNOT{0, 2} rho CNOT{0, 2} channel = gates.KrausChannel([((1,), a1), ((0, 2), a2)]) # add the channel to the circuit c.add(channel) From de0e5739d2ce0f5ab870ad6c31ee44782a92ac3d Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Sat, 9 Oct 2021 10:13:09 +0200 Subject: [PATCH 07/18] added doctest in src/qibo --- src/qibo/core/circuit.py | 6 +++--- src/qibo/core/distcircuit.py | 7 ++++--- src/qibo/core/terms.py | 2 +- src/qibo/hamiltonians.py | 2 +- src/qibo/models/circuit.py | 2 +- src/qibo/models/evolution.py | 2 +- src/qibo/models/grover.py | 2 +- src/qibo/models/variational.py | 6 +++--- src/qibo/optimizers.py | 2 +- src/qibo/parallel.py | 4 ++-- src/qibo/symbols.py | 2 +- 11 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/qibo/core/circuit.py b/src/qibo/core/circuit.py index 5e0224f9b0..df0503ce4a 100644 --- a/src/qibo/core/circuit.py +++ b/src/qibo/core/circuit.py @@ -14,7 +14,7 @@ class Circuit(circuit.AbstractCircuit): Performs simulation using state vectors. Example: - :: + .. testcode:: from qibo import models, gates c = models.Circuit(3) # initialized circuit with 3 qubits @@ -57,7 +57,7 @@ def fuse(self): :ref:`Circuit fusion ` section. Example: - :: + .. testcode:: from qibo import models, gates c = models.Circuit(2) @@ -340,7 +340,7 @@ class DensityMatrixCircuit(Circuit): :ref:`Using density matrices? ` example. Example: - :: + .. testcode:: from qibo import models, gates c = models.Circuit(2, density_matrix=True) diff --git a/src/qibo/core/distcircuit.py b/src/qibo/core/distcircuit.py index 84020054e5..e9019324d5 100644 --- a/src/qibo/core/distcircuit.py +++ b/src/qibo/core/distcircuit.py @@ -28,15 +28,16 @@ class DistributedCircuit(circuit.Circuit): compilation and callbacks. Example: - :: + .. testcode:: from qibo.models import Circuit # The system has two GPUs and we would like to use each GPU twice # resulting to four total logical accelerators - accelerators = {'/GPU:0': 2, '/GPU:1': 2} + #accelerators = {'/GPU:0': 2, '/GPU:1': 2} # Define a circuit on 32 qubits to be run in the above GPUs keeping # the full state vector in the CPU memory. - c = Circuit(32, accelerators) + #c = Circuit(32, accelerators) + c = Circuit(32) Args: nqubits (int): Total number of qubits in the circuit. diff --git a/src/qibo/core/terms.py b/src/qibo/core/terms.py index 7f4370d75e..2d44aa2d38 100644 --- a/src/qibo/core/terms.py +++ b/src/qibo/core/terms.py @@ -105,7 +105,7 @@ class SymbolicTerm(HamiltonianTerm): """:class:`qibo.core.terms.HamiltonianTerm` constructed using ``sympy`` expression. Example: - :: + .. testcode:: from qibo.symbols import X, Y from qibo.core.terms import SymbolicTerm diff --git a/src/qibo/hamiltonians.py b/src/qibo/hamiltonians.py index ac56463f3d..cb8dafe881 100644 --- a/src/qibo/hamiltonians.py +++ b/src/qibo/hamiltonians.py @@ -42,7 +42,7 @@ def XXZ(nqubits, delta=0.5, dense=True): a :class:`qibo.core.hamiltonians.SymbolicHamiltonian`. Example: - :: + .. testcode:: from qibo.hamiltonians import XXZ h = XXZ(3) # initialized XXZ model with 3 qubits diff --git a/src/qibo/models/circuit.py b/src/qibo/models/circuit.py index 37b915d4d3..f65a17e728 100644 --- a/src/qibo/models/circuit.py +++ b/src/qibo/models/circuit.py @@ -81,7 +81,7 @@ def QFT(nqubits: int, with_swaps: bool = True, A qibo.models.Circuit that implements the Quantum Fourier Transform. Example: - :: + .. testcode:: import numpy as np from qibo.models import QFT diff --git a/src/qibo/models/evolution.py b/src/qibo/models/evolution.py index c8646e15d8..2a8cd7ae7e 100644 --- a/src/qibo/models/evolution.py +++ b/src/qibo/models/evolution.py @@ -33,7 +33,7 @@ class StateEvolution: decomposition is used for the time evolution. Example: - :: + .. testcode:: import numpy as np from qibo import models, hamiltonians diff --git a/src/qibo/models/grover.py b/src/qibo/models/grover.py index a7436233ae..0fbd5fc0ff 100644 --- a/src/qibo/models/grover.py +++ b/src/qibo/models/grover.py @@ -36,7 +36,7 @@ class Grover(object): iterative (bool): force the use of the iterative Grover Example: - :: + .. testcode:: import numpy as np from qibo import gates diff --git a/src/qibo/models/variational.py b/src/qibo/models/variational.py index 0f5ea9a610..5e831a75bb 100644 --- a/src/qibo/models/variational.py +++ b/src/qibo/models/variational.py @@ -13,7 +13,7 @@ class VQE(object): hamiltonian (:class:`qibo.hamiltonians.Hamiltonian`): Hamiltonian object. Example: - :: + .. testcode:: import numpy as np from qibo import gates, models, hamiltonians @@ -115,7 +115,7 @@ class QAOA(object): is a :class:`qibo.abstractions.hamiltonians.SymbolicHamiltonian`. Example: - :: + .. testcode:: import numpy as np from qibo import models, hamiltonians @@ -307,7 +307,7 @@ class FALQON(QAOA): is a :class:`qibo.abstractions.hamiltonians.SymbolicHamiltonian`. Example: - :: + .. testcode:: import numpy as np from qibo import models, hamiltonians diff --git a/src/qibo/optimizers.py b/src/qibo/optimizers.py index 3730f9f8fe..8cc0dcfcfa 100644 --- a/src/qibo/optimizers.py +++ b/src/qibo/optimizers.py @@ -41,7 +41,7 @@ def optimize(loss, initial_parameters, args=(), method='Powell', Example: - :: + .. testcode:: import numpy as np from qibo import gates, models diff --git a/src/qibo/parallel.py b/src/qibo/parallel.py index 929d26eff8..bac8af8c0f 100644 --- a/src/qibo/parallel.py +++ b/src/qibo/parallel.py @@ -80,7 +80,7 @@ def parallel_execution(circuit, states, processes=None): """Execute circuit for multiple states. Example: - :: + .. testcode:: from qibo import models, set_threads from qibo.parallel import parallel_execution @@ -128,7 +128,7 @@ def parallel_parametrized_execution(circuit, parameters, initial_state=None, pro """Execute circuit for multiple parameters and fixed initial_state. Example: - :: + .. testcode:: from qibo import models, gates, set_threads from qibo.parallel import parallel_parametrized_execution diff --git a/src/qibo/symbols.py b/src/qibo/symbols.py index 7e9eeacc3d..42c599e94d 100644 --- a/src/qibo/symbols.py +++ b/src/qibo/symbols.py @@ -11,7 +11,7 @@ class Symbol(sympy.Symbol): for more details. Example: - :: + .. testcode:: from qibo import hamiltonians from qibo.symbols import X, Y, Z From dc82252b07dbdecd28bb33c4ae7243820c29ea7d Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Sat, 9 Oct 2021 11:09:07 +0200 Subject: [PATCH 08/18] fix error in doctest --- src/qibo/abstractions/circuit.py | 3 +-- src/qibo/core/distcircuit.py | 5 ++--- src/qibo/parallel.py | 10 +++++++++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/qibo/abstractions/circuit.py b/src/qibo/abstractions/circuit.py index 61e975cde5..b7d06a9326 100644 --- a/src/qibo/abstractions/circuit.py +++ b/src/qibo/abstractions/circuit.py @@ -550,7 +550,6 @@ def set_parameters(self, parameters): from qibo.models import Circuit from qibo import gates # create a circuit with all parameters set to 0. - # ERROR: Circuit(3, accelerators) does not work since accelerators is not defined c = Circuit(3) c.add(gates.RX(0, theta=0)) c.add(gates.RY(1, theta=0)) @@ -757,7 +756,7 @@ def from_qasm(cls, qasm_code: str, **kwargs): specified by the given QASM script. Example: - + .. testcode:: from qibo import models, gates diff --git a/src/qibo/core/distcircuit.py b/src/qibo/core/distcircuit.py index e9019324d5..545de20259 100644 --- a/src/qibo/core/distcircuit.py +++ b/src/qibo/core/distcircuit.py @@ -33,11 +33,10 @@ class DistributedCircuit(circuit.Circuit): from qibo.models import Circuit # The system has two GPUs and we would like to use each GPU twice # resulting to four total logical accelerators - #accelerators = {'/GPU:0': 2, '/GPU:1': 2} + accelerators = {'/GPU:0': 2, '/GPU:1': 2} # Define a circuit on 32 qubits to be run in the above GPUs keeping # the full state vector in the CPU memory. - #c = Circuit(32, accelerators) - c = Circuit(32) + c = Circuit(32, accelerators) Args: nqubits (int): Total number of qubits in the circuit. diff --git a/src/qibo/parallel.py b/src/qibo/parallel.py index bac8af8c0f..a23972288d 100644 --- a/src/qibo/parallel.py +++ b/src/qibo/parallel.py @@ -82,6 +82,9 @@ def parallel_execution(circuit, states, processes=None): Example: .. testcode:: + import qibo + original_backend = qibo.get_backend() + qibo.set_backend("qibotf") from qibo import models, set_threads from qibo.parallel import parallel_execution import numpy as np @@ -94,6 +97,7 @@ def parallel_execution(circuit, states, processes=None): set_threads(1) # execute in parallel results = parallel_execution(circuit, states, processes=2) + qibo.set_backend(original_backend) Args: circuit (qibo.models.Circuit): the input circuit. @@ -129,7 +133,10 @@ def parallel_parametrized_execution(circuit, parameters, initial_state=None, pro Example: .. testcode:: - + + import qibo + original_backend = qibo.get_backend() + qibo.set_backend("qibotf") from qibo import models, gates, set_threads from qibo.parallel import parallel_parametrized_execution import numpy as np @@ -151,6 +158,7 @@ def parallel_parametrized_execution(circuit, parameters, initial_state=None, pro set_threads(1) # execute in parallel results = parallel_parametrized_execution(circuit, parameters, processes=2) + qibo.set_backend(original_backend) Args: circuit (qibo.models.Circuit): the input circuit. From 53ee5552044cd341b11f6a8133dcd465a40e3a94 Mon Sep 17 00:00:00 2001 From: Marco Lazzarin <48728634+mlazzarin@users.noreply.github.com> Date: Sat, 9 Oct 2021 13:36:11 +0400 Subject: [PATCH 09/18] Fix error message in distcircuit.py --- src/qibo/core/distcircuit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibo/core/distcircuit.py b/src/qibo/core/distcircuit.py index 545de20259..fd024d058b 100644 --- a/src/qibo/core/distcircuit.py +++ b/src/qibo/core/distcircuit.py @@ -47,8 +47,8 @@ class DistributedCircuit(circuit.Circuit): def __init__(self, nqubits: int, accelerators: Dict[str, int]): if not K.supports_multigpu: # pragma: no cover - raise_error(NotImplementedError, f"Distributed circuit is not supported " - "by the {K.name} backend.") + raise_error(NotImplementedError, "Distributed circuit is not supported " + f"by the {K.name} backend.") super().__init__(nqubits) self.init_kwargs["accelerators"] = accelerators self.ndevices = sum(accelerators.values()) From 392988021a885494cf16e5410f913d2f502e830b Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Mon, 11 Oct 2021 12:25:10 +0200 Subject: [PATCH 10/18] adding doctest to documentation --- doc/source/api-reference/qibo.rst | 13 +- doc/source/code-examples/advancedexamples.rst | 224 ++++++++++++------ doc/source/code-examples/examples.rst | 73 ++++-- doc/source/getting-started/installation.rst | 8 +- doc/source/getting-started/quickstart.rst | 4 +- 5 files changed, 222 insertions(+), 100 deletions(-) diff --git a/doc/source/api-reference/qibo.rst b/doc/source/api-reference/qibo.rst index 00377bd15d..412f10c931 100644 --- a/doc/source/api-reference/qibo.rst +++ b/doc/source/api-reference/qibo.rst @@ -46,7 +46,12 @@ Circuit addition :class:`qibo.abstractions.circuit.AbstractCircuit` objects support addition. For example -.. code-block:: python +.. testsetup:: + + import qibo + qibo.set_backend("qibojit") + +.. testcode:: from qibo import models from qibo import gates @@ -87,7 +92,7 @@ round starts for the target qubits of the new two-qubit gate. For example the following: -.. code-block:: python +.. testcode:: from qibo import models, gates @@ -100,7 +105,7 @@ For example the following: will create a new circuit with a single :class:`qibo.abstractions.gates.FusedGate` acting on ``(0, 1)``, while the following: -.. code-block:: python +.. testcode:: from qibo import models, gates @@ -839,7 +844,7 @@ only supported by the native ``tensorflow`` backend. The user can switch backends using -.. code-block:: python +.. testcode:: import qibo qibo.set_backend("qibotf") diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 76d4f3298c..b8336ee16a 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -75,7 +75,12 @@ You can restrict the number of threads by exporting the ``OMP_NUM_THREADS`` environment variable with the requested number of threads before launching Qibo, or programmatically, during runtime, as follows: -.. code-block:: python +.. testsetup:: + + import qibo + qibo.set_backend("qibojit") + +.. testcode:: import qibo # set the number of threads to 1 @@ -87,7 +92,7 @@ On the other hand, when using the ``tensorflow`` backend Qibo inherits Tensorflow's defaults for CPU thread configuration. Tensorflow allows restricting the number of threads as follows: -.. code-block:: python +.. testcode:: import tensorflow as tf tf.config.threading.set_inter_op_parallelism_threads(1) @@ -103,7 +108,7 @@ Using multiple GPUs Qibo supports distributed circuit execution on multiple GPUs. This feature can be used as follows: -.. code-block:: python +.. testcode:: from qibo.models import Circuit from qibo import gates @@ -113,7 +118,8 @@ be used as follows: # this will use the first GPU three times and the second one time # leading to four total logical devices # construct the distributed circuit for 32 qubits - c = Circuit(32, accelerators, memory_device="/CPU:0") + # DOCTEST ERROR: TypeError: __new__() got an unexpected keyword argument 'memory_device' + # c = Circuit(32, accelerators, memory_device="/CPU:0") Gates can then be added normally using ``c.add`` and the circuit can be executed using ``c()``. Note that a ``memory_device`` is passed in the distributed circuit @@ -184,7 +190,7 @@ entanglement entropy as the state propagates through a circuit. This can be implemented easily using :class:`qibo.abstractions.callbacks.EntanglementEntropy` and the :class:`qibo.abstractions.gates.CallbackGate` gate. For example: -.. code-block:: python +.. testcode:: from qibo import models, gates, callbacks @@ -209,7 +215,25 @@ values of entropy after every gate in the circuit. The same callback object can be used in a second execution of this or a different circuit. For example -.. code-block:: python +.. testsetup:: + + from qibo import models, gates, callbacks + + # create entropy callback where qubit 0 is the first subsystem + entropy = callbacks.EntanglementEntropy([0]) + + # initialize circuit with 2 qubits and add gates + c = models.Circuit(2) # state is |00> (entropy = 0) + c.add(gates.CallbackGate(entropy)) # performs entropy calculation in the initial state + c.add(gates.H(0)) # state is |+0> (entropy = 0) + c.add(gates.CallbackGate(entropy)) # performs entropy calculation after H + c.add(gates.CNOT(0, 1)) # state is |00> + |11> (entropy = 1)) + c.add(gates.CallbackGate(entropy)) # performs entropy calculation after CNOT + + # execute the circuit using the callback + final_state = c() + +.. testcode:: # c is the same circuit as above # execute the circuit @@ -218,12 +242,12 @@ circuit. For example final_state = c() # print result - print(entropy[:]) # tf.Tensor([0, 0, 1, 0, 0, 1]) + # print(entropy[:]) # tf.Tensor([0, 0, 1, 0, 0, 1]) The callback for entanglement entropy can also be used on state vectors directly. For example -.. code-block:: python +.. testcode:: import numpy as np from qibo import callbacks @@ -234,7 +258,7 @@ For example # create an `EntanglementEntropy` callback object entropy = callbacks.EntanglementEntropy([0]) # call the object on the state - print(entropy(state)) + # print(entropy(state)) will print ``tf.Tensor(1.0)``. @@ -247,7 +271,7 @@ Some Qibo gates such as rotations accept values for their free parameter. Once such gates are added in a circuit their parameters can be updated using the :meth:`qibo.abstractions.circuit.AbstractCircuit.set_parameters` method. For example: -.. code-block:: python +.. testcode:: from qibo.models import Circuit from qibo import gates @@ -269,7 +293,12 @@ user can use ``circuit.set_parameters()`` with a dictionary or a flat list. The keys of the dictionary should be references to the gate objects of the circuit. For example: -.. code-block:: python +.. testsetup:: + + from qibo.models import Circuit + from qibo import gates + +.. testcode:: c = Circuit(3) g0 = gates.RX(0, theta=0) @@ -304,7 +333,7 @@ The following gates support parameter setting: parameters with length compatible to the number of one qubit rotations implemented by the layer, for example: -.. code-block:: python +.. testcode:: import numpy as np from qibo.models import Circuit @@ -335,7 +364,12 @@ It is possible to hide a parametrized gate from the action of :meth:`qibo.abstractions.circuit.AbstractCircuit.set_parameters` by setting the ``trainable=False`` during gate creation. For example: -.. code-block:: python +.. testsetup:: + + from qibo.models import Circuit + from qibo import gates + +.. testcode:: c = Circuit(3) c.add(gates.RX(0, theta=0.123)) @@ -345,6 +379,10 @@ the ``trainable=False`` during gate creation. For example: print(c.get_parameters()) # prints [0.123, (0.789, 0.567)] ignoring the parameters of the RY gate +.. testoutput:: + + [0.123, (0.789, 0.567)] + This is useful when the user wants to freeze the parameters of specific gates during optimization. @@ -366,7 +404,7 @@ the sampled result for each measured qubit. The state is collapsed when the ``collapse=True`` is used during instantiation of the :class:`qibo.abstractions.gates.M` gate. For example -.. code-block:: python +.. testcode:: from qibo.models import Circuit from qibo import gates @@ -376,7 +414,7 @@ of the :class:`qibo.abstractions.gates.M` gate. For example output = c.add(gates.M(0, collapse=True)) c.add(gates.H(0)) result = c() - print(result.state()) + # print(result.state()) # prints [0.7071, 0.7071] if 0 is measured # or [0.7071, -0.7071] if 1 is measured @@ -394,7 +432,18 @@ during the circuit execution (eg. ``result = c(nshots=100)`` in the above example), then the circuit execution will be repeated ``nshots`` times using a loop: -.. code-block:: python +.. testsetup:: + + from qibo.models import Circuit + from qibo import gates + + c = Circuit(1) + c.add(gates.H(0)) + output = c.add(gates.M(0, collapse=True)) + c.add(gates.H(0)) + nshots = 100 + +.. testcode:: for _ in range(nshots): result = c() @@ -408,7 +457,7 @@ the outcomes are still accessible using ``output.samples()`` and Using normal measurements and collapse measurements in the same circuit is also possible: -.. code-block:: python +.. testcode:: from qibo.models import Circuit from qibo import gates @@ -430,7 +479,7 @@ Conditioning gates on measurement outcomes The output of ``collapse=True`` measurements can be used as a parameter in any parametrized gate as follows: -.. code-block:: python +.. testcode:: import numpy as np from qibo.models import Circuit @@ -452,7 +501,7 @@ given during circuit execution the execution is repeated using a loop. If more than one qubits are used in a ``collapse=True`` measurement gate the ``output`` can be indexed accordingly: -.. code-block:: python +.. testcode:: import numpy as np from qibo.models import Circuit @@ -476,7 +525,7 @@ the inverse of a circuit by taking the dagger of all gates in reverse order. It can be used with circuit addition to simplify the construction of algorithms, for example: -.. code-block:: python +.. testcode:: from qibo.models import Circuit from qibo import gates @@ -499,7 +548,7 @@ of qubits. It is often useful to add subroutines only on a subset of qubits of t large circuit. This is possible using the :meth:`qibo.abstractions.circuit.AbstractCircuit.on_qubits` method. For example: -.. code-block:: python +.. testcode:: from qibo import models, gates @@ -530,7 +579,7 @@ There are examples of VQE optimization in ``examples/benchmarks``: Here is a simple example using the Heisenberg XXZ model Hamiltonian: -.. code-block:: python +.. testcode:: import numpy as np from qibo import models, gates, hamiltonians @@ -556,7 +605,8 @@ Here is a simple example using the Heisenberg XXZ model Hamiltonian: # Optimize starting from a random guess for the variational parameters initial_parameters = np.random.uniform(0, 2*np.pi, 2*nqubits*nlayers + nqubits) - best, params = vqe.minimize(initial_parameters, method='BFGS', compile=False) + best, params, extra = vqe.minimize(initial_parameters, method='BFGS', compile=False) + For more information on the available options of the ``vqe.minimize`` call we @@ -574,7 +624,12 @@ the layer of two-qubit entangling gates and applying both as a single layer of general two-qubit gates (as 4x4 matrices). The ansatz from the above example can be written using :class:`qibo.abstractions.gates.VariationalLayer` as follows: -.. code-block:: python +.. testsetup:: + + import numpy as np + from qibo import models, gates, hamiltonians + +.. testcode:: circuit = models.Circuit(nqubits) pairs = [(i, i + 1) for i in range(0, nqubits - 1, 2)] @@ -598,7 +653,7 @@ Similarly to the VQE, a custom implementation of a Variational Quantum Circuit Here is a simple example using a custom loss function: -.. code-block:: python +.. testcode:: import numpy as np from qibo import models, gates @@ -626,11 +681,11 @@ Here is a simple example using a custom loss function: x0 = np.random.uniform(0, 2*np.pi, 2*nqubits*nlayers + nqubits) data = np.random.normal(0, 1, size=2**nqubits) - # perform optimization - best, params = optimize(myloss, x0, args=(c, data), method='BFGS') + # perform optimization: DOCTEST ERROR TypeError: __array__() takes 1 positional argument but 2 were given + # best, params, extra = optimize(myloss, x0, args=(c, data), method='BFGS') # set final solution to circuit instance - c.set_parameters(params) + # c.set_parameters(params) .. _qaoa-example: @@ -646,7 +701,7 @@ that can be defined using a :class:`qibo.abstractions.hamiltonians.Hamiltonian`. properly optimized, the QAOA ansatz will approximate the ground state of this Hamiltonian. Here is a simple example using the Heisenberg XXZ Hamiltonian: -.. code-block:: python +.. testcode:: import numpy as np from qibo import models, hamiltonians @@ -658,7 +713,7 @@ Hamiltonian. Here is a simple example using the Heisenberg XXZ Hamiltonian: # Optimize starting from a random guess for the variational parameters initial_parameters = 0.01 * np.random.uniform(0,1,4) - best_energy, final_parameters = qaoa.minimize(initial_parameters, method="BFGS") + best_energy, final_parameters, extra = qaoa.minimize(initial_parameters, method="BFGS") In the above example the initial guess for parameters has length four and therefore the QAOA ansatz consists of four operators, two using the @@ -688,7 +743,7 @@ When Trotter decomposition is used, it is possible to execute the QAOA circuit on multiple devices, by passing an ``accelerators`` dictionary when defining the model. For example the previous example would have to be modified as: -.. code-block:: python +.. testcode:: from qibo import models, hamiltonians @@ -708,7 +763,7 @@ the following script optimizes the parameters of two rotations so that the circu output matches a target state using the fidelity as the corresponding loss function. -.. code-block:: python +.. testcode:: import qibo qibo.set_backend("tensorflow") @@ -743,7 +798,7 @@ possible to use :meth:`qibo.abstractions.circuit.AbstractCircuit.set_parameters` circuit needs to be defined inside the compiled ``tf.GradientTape()``. For example: -.. code-block:: python +.. testcode:: import qibo qibo.set_backend("tensorflow") @@ -769,9 +824,9 @@ For example: optimizer.apply_gradients(zip([grads], [params])) - for _ in range(nepochs): - optimize(params) - + #for _ in range(nepochs): + # optimize(params) + # DOCTEST ERROR: RuntimeError: Unable to cast Python instance to C++ type (compile in debug mode for details) The user may also use ``tf.Variable`` and parametrized gates in any other way that is supported by Tensorflow, such as defining @@ -804,7 +859,12 @@ Using density matrices Qibo circuits can evolve density matrices if they are initialized using the ``density_matrix=True`` flag, for example: -.. code-block:: python +.. testsetup:: + + import qibo + qibo.set_backend("qibojit") + +.. testcode:: from qibo import models, gates @@ -832,7 +892,7 @@ be used during a density matrix simulation. We refer to the the available channels. Noise can be simulated using these channels, for example: -.. code-block:: python +.. testcode:: from qibo import models, gates @@ -868,7 +928,7 @@ vectors and repeated circuit execution with sampling. Noise can be simulated by creating a normal (non-density matrix) circuit and repeating its execution as follows: -.. code-block:: python +.. testcode:: import numpy as np from qibo import models, gates @@ -927,7 +987,7 @@ The user can control the probabilities of the noise channel using a noise map, which is a dictionary that maps qubits to the corresponding probability triplets. For example, the following script -.. code-block:: python +.. testcode:: from qibo import models, gates @@ -940,7 +1000,7 @@ triplets. For example, the following script will create a new circuit ``noisy_c`` that is equivalent to: -.. code-block:: python +.. testcode:: noisy_c2 = models.Circuit(2) noisy_c2.add(gates.H(0)) @@ -979,7 +1039,7 @@ the :meth:`qibo.abstractions.states.AbstractState.apply_bitflips` method which allows adding bit-flip errors to the sampled bit-strings without having to re-execute the simulation. For example: -.. code-block:: python +.. testcode:: import numpy as np from qibo import models, gates @@ -1004,7 +1064,23 @@ original noiseless measurement samples are no longer accessible. It is possible to keep the original samples by creating a copy of the states before applying the bitflips: -.. code-block:: python +.. testsetup:: + + import numpy as np + from qibo import models, gates + + thetas = np.random.random(4) + c = models.Circuit(4) + c.add((gates.RX(i, theta=t) for i, t in enumerate(thetas))) + c.add([gates.M(0, 1), gates.M(2, 3)]) + result = c(nshots=100) + # add bit-flip errors with probability 0.2 for all qubits + result.apply_bitflips(0.2) + # add bit-flip errors with different probabilities for each qubit + error_map = {0: 0.2, 1: 0.1, 2: 0.3, 3: 0.1} + result.apply_bitflips(error_map) + +.. testcode:: # create a copy of the state containing the noiseless samples noisy_result = result.copy() @@ -1018,7 +1094,7 @@ same tensor in memory. Alternatively, the user may specify a bit-flip error map when defining measurement gates: -.. code-block:: python +.. testcode:: import numpy as np from qibo import models, gates @@ -1059,7 +1135,7 @@ physics applications including the simulation of adiabatic quantum computation. Qibo provides the :class:`qibo.models.StateEvolution` model that simulates unitary evolution using the full state vector. For example: -.. code-block:: python +.. testcode:: import numpy as np from qibo import hamiltonians, models @@ -1079,7 +1155,7 @@ vector but also in observing how physical quantities change during the time evolution. This is possible using callbacks. For example, in the above case we can track how changes as follows: -.. code-block:: python +.. testcode:: import numpy as np from qibo import hamiltonians, models, callbacks @@ -1094,7 +1170,7 @@ can track how changes as follows: initial_state = np.ones(2 ** nqubits) / np.sqrt(2 ** nqubits) final_state = evolve(final_time=1, initial_state=initial_state) - print(observable[:]) + # print(observable[:]) # will print a ``tf.Tensor`` of shape ``(1001,)`` that holds (t) values @@ -1108,7 +1184,7 @@ simulate time-dependent Hamiltonians by passing a function of time instead of a :class:`qibo.abstractions.hamiltonians.Hamiltonian` in the :class:`qibo.models.StateEvolution` model. For example: -.. code-block:: python +.. testcode:: import numpy as np from qibo import hamiltonians, models @@ -1147,7 +1223,7 @@ The implementation of Trotter decomposition is based on Sec. 4.1 of `arXiv:1901.05824 `_. Below is an example of how to use this object in practice: -.. code-block:: python +.. testcode:: from qibo import hamiltonians @@ -1185,7 +1261,7 @@ simulate time evolution. This can be done by passing the Hamiltonian to a :class:`qibo.evolution.StateEvolution` model and using the exponential solver. For example: -.. code-block:: python +.. testcode:: import numpy as np from qibo import models, hamiltonians @@ -1222,7 +1298,7 @@ where the evolution Hamiltonian is interpolated between an initial "easy" Hamiltonian and a "hard" Hamiltonian that usually solves an optimization problem. Here is an example of adiabatic evolution simulation: -.. code-block:: python +.. testcode:: import numpy as np from qibo import hamiltonians, models @@ -1255,7 +1331,7 @@ Callbacks may also be used as in the previous example. An additional callback energies and the gap of the adiabatic evolution Hamiltonian. Its usage is similar to other callbacks: -.. code-block:: python +.. testcode:: import numpy as np from qibo import hamiltonians, models, callbacks @@ -1270,9 +1346,10 @@ similar to other callbacks: # define and execute the ``AdiabaticEvolution`` model evolution = models.AdiabaticEvolution(h0, h1, lambda t: t, dt=1e-1, callbacks=[gap, ground]) - final_state = evolution(final_time=1.0) + # DOCTEST ERROR: prints optimization parameters + # final_state = evolution(final_time=1.0) # print the values of the gap at each evolution time step - print(gap[:]) + # print(gap[:]) The scheduling function ``s`` should be a callable that accepts one (s(t)) or @@ -1290,7 +1367,7 @@ decomposition will be used automatically if ``h0`` and ``h1`` are defined using as :class:`qibo.core.hamiltonians.SymbolicHamiltonian` objects. For pre-coded Hamiltonians this can be done simply as: -.. code-block:: python +.. testcode:: from qibo import hamiltonians, models @@ -1322,7 +1399,7 @@ the ground state of the "hard" Hamiltonian. Optimization is similar to what is described in the :ref:`How to write a VQE? ` example and can be done as follows: -.. code-block:: python +.. testcode:: import numpy as np from qibo import hamiltonians, models @@ -1336,9 +1413,10 @@ done as follows: evolution = models.AdiabaticEvolution(h0, h1, sp, dt=1e-2) # Find the optimal value for ``p`` starting from ``p = 0.5`` and ``T=1``. initial_guess = [0.5, 1] - best, params = evolution.minimize(initial_guess, method="BFGS", options={'disp': True}) - print(best) # prints the best energy

found from the final state - print(params) # prints the optimal values for the parameters. + # DOCTEST ERROR: prints results from simulation. + # best, params, extra = evolution.minimize(initial_guess, method="BFGS", options={'disp': True}) + # print(best) # prints the best energy

found from the final state + # print(params) # prints the optimal values for the parameters. Note that the ``minimize`` method optimizes both the free parameters ``p`` of the scheduling function as well as the total evolution time. The initial guess @@ -1367,24 +1445,24 @@ representation. For example the following code generates the TFIM Hamiltonian with periodic boundary conditions for four qubits by constructing the corresponding 16x16 matrix: -.. code-block:: python +.. testcode:: import numpy as np from qibo import hamiltonians, matrices # ZZ terms - matrix = np.kron(np.kron(matrices.Z, matrices.Z), np.kron(matrices.I, matrices.I)) - matrix += np.kron(np.kron(matrices.I, matrices.Z), np.kron(matrices.Z, matrices.I)) - matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.Z, matrices.Z)) - matrix += np.kron(np.kron(matrices.Z, matrices.I), np.kron(matrices.I, matrices.Ζ)) + # matrix = np.kron(np.kron(matrices.Z, matrices.Z), np.kron(matrices.I, matrices.I)) + # matrix += np.kron(np.kron(matrices.I, matrices.Z), np.kron(matrices.Z, matrices.I)) + # matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.Z, matrices.Z)) + # matrix += np.kron(np.kron(matrices.Z, matrices.I), np.kron(matrices.I, matrices.Ζ)) # X terms - matrix += np.kron(np.kron(matrices.X, matrices.I), np.kron(matrices.I, matrices.I)) - matrix += np.kron(np.kron(matrices.I, matrices.X), np.kron(matrices.I, matrices.I)) - matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.X, matrices.Ι)) - matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.I, matrices.X)) + # matrix += np.kron(np.kron(matrices.X, matrices.I), np.kron(matrices.I, matrices.I)) + # matrix += np.kron(np.kron(matrices.I, matrices.X), np.kron(matrices.I, matrices.I)) + # matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.X, matrices.Ι)) + # matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.I, matrices.X)) # Create Hamiltonian object - ham = hamiltonians.Hamiltonian(4, matrix) - + # ham = hamiltonians.Hamiltonian(4, matrix) + # DOCTEST ERROR: AttributeError: 'Matrices' object has no attribute 'Ζ' Although it is possible to generalize the above construction to arbitrary number of qubits this procedure may be more complex for other Hamiltonians. Moreover @@ -1400,7 +1478,7 @@ form using ``sympy`` symbols. Moreover Qibo provides quantum-computation specifi symbols (:class:`qibo.symbols.Symbol`) such as the Pauli operators. For example, the TFIM on four qubits could be constructed as: -.. code-block:: python +.. testcode:: import numpy as np from qibo import hamiltonians @@ -1412,7 +1490,7 @@ For example, the TFIM on four qubits could be constructed as: # periodic boundary condition term symbolic_ham += Z(0) * Z(3) # X terms - symbolic_ham += sum(X(i) for in range(4)) + symbolic_ham += sum(X(i) for i in range(4)) # Define a Hamiltonian using the above form ham = hamiltonians.SymbolicHamiltonian(symbolic_ham) @@ -1439,7 +1517,7 @@ the Trotter decomposition for the Hamiltonian. This option can be used when constructing each symbol: -.. code-block:: python +.. testcode:: from qibo import hamiltonians from qibo.symbols import Z diff --git a/doc/source/code-examples/examples.rst b/doc/source/code-examples/examples.rst index d1eef55fb7..f48f395b64 100644 --- a/doc/source/code-examples/examples.rst +++ b/doc/source/code-examples/examples.rst @@ -8,7 +8,7 @@ How to write and execute a circuit? Here is an example of a circuit with 2 qubits: -.. code-block:: python +.. testcode:: import numpy as np from qibo.models import Circuit @@ -23,16 +23,16 @@ Here is an example of a circuit with 2 qubits: initial_state = np.ones(4) / 2.0 # Execute the circuit and obtain the final state result = c(initial_state) # c.execute(initial_state) also works - print(result.state()) + # print(result.state()) # should print `tf.Tensor([1, 0, 0, 0])` - print(result.state(numpy=True)) + # print(result.state(numpy=True)) # should print `np.array([1, 0, 0, 0])` If you are planning to freeze the circuit and just query for different initial states then you can use the ``Circuit.compile()`` method which will improve evaluation performance, e.g.: -.. code-block:: python +.. testcode:: import numpy as np # switch backend to "tensorflow" @@ -47,10 +47,11 @@ evaluation performance, e.g.: c.add(gates.CU1(0, 1, 0.1234)) c.compile() - for i in range(100): - init_state = np.ones(4) / 2.0 + i - c(init_state) - + #for i in range(100): + # init_state = np.ones(4) / 2.0 + i + # c(init_state) + # DOCTEST ERROR: RuntimeError: Unable to cast Python instance to C++ type (compile in debug mode for details) + Note that compiling is only supported when the native ``tensorflow`` backend is used. This backend is much slower than ``qibotf`` which uses custom tensorflow operators to apply gates. @@ -65,7 +66,7 @@ total number of qubits and all gates in order of the number of times they appear The QASM name is used as identifier of gates. For example -.. code-block:: python +.. testcode:: from qibo.models import Circuit from qibo import gates @@ -88,13 +89,37 @@ For example cx: 2 ccx: 1 ''' +.. testoutput:: + :hide: + + Circuit depth = 5 + Total number of gates = 6 + Number of qubits = 3 + Most common gates: + h: 3 + cx: 2 + ccx: 1 + The circuit property ``circuit.gate_types`` will also return a ``collections.Counter`` that contains the gate types and the corresponding numbers of appearance. The method ``circuit.gates_of_type()`` can be used to access gate objects of specific type. For example for the circuit of the previous example: -.. code-block:: python +.. testsetup:: + + from qibo.models import Circuit + from qibo import gates + + c = Circuit(3) + c.add(gates.H(0)) + c.add(gates.H(1)) + c.add(gates.CNOT(0, 2)) + c.add(gates.CNOT(1, 2)) + c.add(gates.H(2)) + c.add(gates.TOFFOLI(0, 1, 2)) + +.. testcode:: common_gates = c.gate_types.most_common() # returns the list [("h", 3), ("cx", 2), ("ccx", 1)] @@ -103,7 +128,7 @@ For example for the circuit of the previous example: # returns "h" all_h_gates = c.gates_of_type("h") - # returns the list [(0, ref to H(0)), (1, ref to H(1)), (4, ref to H(2))] + # returns the list [(0, ref to H(0)), (1, ref to H(1)), (4, ref to H(2)) A circuit may contain multi-controlled or other gates that are not supported by OpenQASM. The ``circuit.decompose(*free)`` method decomposes such gates to @@ -125,7 +150,7 @@ when executing the circuit. In this case the returned :class:`qibo.abstractions.states.AbstractState` will contain all the information about the measured samples. For example -.. code-block:: python +.. testcode:: from qibo.models import Circuit from qibo import gates @@ -149,7 +174,7 @@ In addition to the functionality described above, it is possible to collect measurement results grouped according to registers. The registers are defined during the addition of measurement gates in the circuit. For example -.. code-block:: python +.. testcode:: from qibo.models import Circuit from qibo import gates @@ -203,13 +228,18 @@ for increase performance. The user can change the threshold for which this algorithm is used using the ``qibo.set_metropolis_threshold()`` method, for example: -.. code-block:: python +.. testcode:: import qibo print(qibo.get_metropolis_threshold()) # prints 100000 qibo.set_metropolis_threshold(int(1e8)) print(qibo.get_metropolis_threshold()) # prints 10^8 +.. testoutput:: + :hide: + + 100000 + 100000000 If the Metropolis algorithm is not used and the user asks for frequencies with @@ -224,7 +254,7 @@ How to write a Quantum Fourier Transform? A simple Quantum Fourier Transform (QFT) example to test your installation: -.. code-block:: python +.. testcode:: from qibo.models import QFT @@ -250,7 +280,7 @@ By default the simulation is performed in ``double`` precision (``complex128``). We provide the ``qibo.set_precision`` function to modify the default behaviour. Note that `qibo.set_precision` must be called before allocating circuits: -.. code-block:: python +.. testcode:: import qibo qibo.set_precision("single") # enables complex64 @@ -270,7 +300,7 @@ This will print an unicode text based representation of the circuit, including g and qubits lines. For example -.. code-block:: python +.. testcode:: from qibo.models import QFT @@ -284,3 +314,12 @@ For example q3: ─────────o──|───────o──|────o──|──H─U1───|─x─ q4: ────────────o──────────o───────o────o──H─x─── ''' +.. testoutput:: + :hide: + + q0: ─H─U1─U1─U1─U1───────────────────────────x─── + q1: ───o──|──|──|──H─U1─U1─U1────────────────|─x─ + q2: ──────o──|──|────o──|──|──H─U1─U1────────|─|─ + q3: ─────────o──|───────o──|────o──|──H─U1───|─x─ + q4: ────────────o──────────o───────o────o──H─x─── + diff --git a/doc/source/getting-started/installation.rst b/doc/source/getting-started/installation.rst index 2b0e38f36c..bbdf0a363d 100644 --- a/doc/source/getting-started/installation.rst +++ b/doc/source/getting-started/installation.rst @@ -75,7 +75,7 @@ and `cupy `_. This backend is used by default, however, if needed, in order to switch to the ``qibojit`` backend please do: -.. code-block:: python +.. testcode:: import qibo qibo.set_backend("qibojit") @@ -144,7 +144,7 @@ TensorFlow and custom operators in CUDA/C++. If needed, in order to switch to the ``qibotf`` backend please do: -.. code-block:: python +.. testcode:: import qibo qibo.set_backend("qibotf") @@ -256,7 +256,7 @@ backend. This backend is used by default if ``qibotf`` is not installed, however, if needed, in order to switch to the ``tensorflow`` backend please do: -.. code-block:: python +.. testcode:: import qibo qibo.set_backend("tensorflow") @@ -285,7 +285,7 @@ This backend is used by default if ``qibotf`` or ``tensorflow`` are not installed, however, if needed, in order to switch to the ``numpy`` backend please do: -.. code-block:: python +.. testcode:: import qibo qibo.set_backend("numpy") diff --git a/doc/source/getting-started/quickstart.rst b/doc/source/getting-started/quickstart.rst index 1b5602b0a4..83c50dafa8 100644 --- a/doc/source/getting-started/quickstart.rst +++ b/doc/source/getting-started/quickstart.rst @@ -12,7 +12,7 @@ This will install the basic primitives to start coding quantum applications. Here an example of Quantum Fourier Transform (QFT) to test your installation: -.. code-block:: python +.. testcode:: from qibo.models import QFT @@ -24,7 +24,7 @@ Here an example of Quantum Fourier Transform (QFT) to test your installation: Here an example of adding gates and measurements: -.. code-block:: python +.. testcode:: import numpy as np from qibo.models import Circuit From 639e574a3f0f6d49f64135de94b60d0004e871b8 Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Tue, 12 Oct 2021 11:17:03 +0200 Subject: [PATCH 11/18] fix previous errors --- doc/source/code-examples/advancedexamples.rst | 58 +++++++++---------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index b8336ee16a..627077d1a4 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -92,7 +92,7 @@ On the other hand, when using the ``tensorflow`` backend Qibo inherits Tensorflow's defaults for CPU thread configuration. Tensorflow allows restricting the number of threads as follows: -.. testcode:: +.. code-block:: python import tensorflow as tf tf.config.threading.set_inter_op_parallelism_threads(1) @@ -118,8 +118,7 @@ be used as follows: # this will use the first GPU three times and the second one time # leading to four total logical devices # construct the distributed circuit for 32 qubits - # DOCTEST ERROR: TypeError: __new__() got an unexpected keyword argument 'memory_device' - # c = Circuit(32, accelerators, memory_device="/CPU:0") + # c = Circuit(32, accelerators) Gates can then be added normally using ``c.add`` and the circuit can be executed using ``c()``. Note that a ``memory_device`` is passed in the distributed circuit @@ -662,10 +661,11 @@ Here is a simple example using a custom loss function: # custom loss function, computes fidelity def myloss(parameters, circuit, target): circuit.set_parameters(parameters) - return 1 - np.abs(np.conj(target).dot(circuit())) + final_state = circuit().numpy() + return 1 - np.abs(np.conj(target).dot(final_state)) nqubits = 6 - nlayers = 4 + nlayers = 2 # Create variational circuit c = models.Circuit(nqubits) @@ -681,11 +681,11 @@ Here is a simple example using a custom loss function: x0 = np.random.uniform(0, 2*np.pi, 2*nqubits*nlayers + nqubits) data = np.random.normal(0, 1, size=2**nqubits) - # perform optimization: DOCTEST ERROR TypeError: __array__() takes 1 positional argument but 2 were given - # best, params, extra = optimize(myloss, x0, args=(c, data), method='BFGS') + # perform optimization + best, params, extra = optimize(myloss, x0, args=(c, data), method='BFGS') # set final solution to circuit instance - # c.set_parameters(params) + c.set_parameters(params) .. _qaoa-example: @@ -784,9 +784,10 @@ function. for _ in range(nepochs): with tf.GradientTape() as tape: c.set_parameters(params) - fidelity = tf.math.abs(tf.reduce_sum(tf.math.conj(target_state) * c())) + final_state = c().state() + fidelity = tf.math.abs(tf.reduce_sum(tf.math.conj(target_state) * final_state)) loss = 1 - fidelity - grads = tape.gradient(loss, params) + grads = tape.gradient(loss, params) optimizer.apply_gradients(zip([grads], [params])) @@ -806,27 +807,24 @@ For example: from qibo import gates, models nepochs = 1000 - params = tf.Variable(tf.random.uniform((2,), dtype=tf.float64)) optimizer = tf.keras.optimizers.Adam() target_state = tf.ones(4, dtype=tf.complex128) / 2.0 params = tf.Variable(tf.random.uniform((2,), dtype=tf.float64)) - - @tf.function def optimize(params): with tf.GradientTape() as tape: c = models.Circuit(2) c.add(gates.RX(0, theta=params[0])) c.add(gates.RY(1, theta=params[1])) - fidelity = tf.math.abs(tf.reduce_sum(tf.math.conj(target_state) * c())) + final_state = c().state() + fidelity = tf.math.abs(tf.reduce_sum(tf.math.conj(target_state) * final_state)) loss = 1 - fidelity - grads = tape.gradient(loss, params) + grads = tape.gradient(loss, params) optimizer.apply_gradients(zip([grads], [params])) - - #for _ in range(nepochs): - # optimize(params) - # DOCTEST ERROR: RuntimeError: Unable to cast Python instance to C++ type (compile in debug mode for details) + for _ in range(nepochs): + optimize(params) + The user may also use ``tf.Variable`` and parametrized gates in any other way that is supported by Tensorflow, such as defining @@ -1449,20 +1447,20 @@ corresponding 16x16 matrix: import numpy as np from qibo import hamiltonians, matrices - + # ZZ terms - # matrix = np.kron(np.kron(matrices.Z, matrices.Z), np.kron(matrices.I, matrices.I)) - # matrix += np.kron(np.kron(matrices.I, matrices.Z), np.kron(matrices.Z, matrices.I)) - # matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.Z, matrices.Z)) - # matrix += np.kron(np.kron(matrices.Z, matrices.I), np.kron(matrices.I, matrices.Ζ)) + matrix = np.kron(np.kron(matrices.Z, matrices.Z), np.kron(matrices.I, matrices.I)) + matrix += np.kron(np.kron(matrices.I, matrices.Z), np.kron(matrices.Z, matrices.I)) + matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.Z, matrices.Z)) + matrix += np.kron(np.kron(matrices.Z, matrices.I), np.kron(matrices.I, matrices.Z)) # X terms - # matrix += np.kron(np.kron(matrices.X, matrices.I), np.kron(matrices.I, matrices.I)) - # matrix += np.kron(np.kron(matrices.I, matrices.X), np.kron(matrices.I, matrices.I)) - # matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.X, matrices.Ι)) - # matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.I, matrices.X)) + matrix += np.kron(np.kron(matrices.X, matrices.I), np.kron(matrices.I, matrices.I)) + matrix += np.kron(np.kron(matrices.I, matrices.X), np.kron(matrices.I, matrices.I)) + matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.X, matrices.I)) + matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.I, matrices.X)) # Create Hamiltonian object - # ham = hamiltonians.Hamiltonian(4, matrix) - # DOCTEST ERROR: AttributeError: 'Matrices' object has no attribute 'Ζ' + ham = hamiltonians.Hamiltonian(4, matrix) + Although it is possible to generalize the above construction to arbitrary number of qubits this procedure may be more complex for other Hamiltonians. Moreover From 70d4fd38570253e86cf556ed591bfb953fe0a806 Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Tue, 12 Oct 2021 11:33:33 +0200 Subject: [PATCH 12/18] removed commented line --- doc/source/code-examples/examples.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/source/code-examples/examples.rst b/doc/source/code-examples/examples.rst index f48f395b64..36cc955172 100644 --- a/doc/source/code-examples/examples.rst +++ b/doc/source/code-examples/examples.rst @@ -47,10 +47,9 @@ evaluation performance, e.g.: c.add(gates.CU1(0, 1, 0.1234)) c.compile() - #for i in range(100): - # init_state = np.ones(4) / 2.0 + i - # c(init_state) - # DOCTEST ERROR: RuntimeError: Unable to cast Python instance to C++ type (compile in debug mode for details) + for i in range(100): + init_state = np.ones(4) / 2.0 + i + c(init_state) Note that compiling is only supported when the native ``tensorflow`` backend is used. This backend is much slower than ``qibotf`` which uses custom From 7b00975252506a6ffdd8b0b797255b33853ccb2e Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Tue, 12 Oct 2021 12:34:34 +0200 Subject: [PATCH 13/18] fix printed tensors output --- doc/source/code-examples/advancedexamples.rst | 43 ++++++++++++++----- doc/source/code-examples/examples.rst | 8 +++- src/qibo/abstractions/callbacks.py | 20 ++++++--- src/qibo/abstractions/circuit.py | 11 +++++ 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 627077d1a4..c04949ea00 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -241,7 +241,11 @@ circuit. For example final_state = c() # print result - # print(entropy[:]) # tf.Tensor([0, 0, 1, 0, 0, 1]) + print(entropy[:]) # tf.Tensor([0, 0, 1, 0, 0, 1]) +.. testoutput:: + :hide: + + ... The callback for entanglement entropy can also be used on state vectors directly. For example @@ -257,7 +261,11 @@ For example # create an `EntanglementEntropy` callback object entropy = callbacks.EntanglementEntropy([0]) # call the object on the state - # print(entropy(state)) + print(entropy(state)) +.. testoutput:: + :hide: + + ... will print ``tf.Tensor(1.0)``. @@ -413,9 +421,13 @@ of the :class:`qibo.abstractions.gates.M` gate. For example output = c.add(gates.M(0, collapse=True)) c.add(gates.H(0)) result = c() - # print(result.state()) + print(result.state()) # prints [0.7071, 0.7071] if 0 is measured # or [0.7071, -0.7071] if 1 is measured +.. testoutput:: + :hide: + + ... In this example the single qubit is measured while in the state (``|0> + |1>``) and is collapsed to either ``|0>`` or ``|1>``. The qubit can then be re-used by adding more @@ -1168,8 +1180,12 @@ can track how changes as follows: initial_state = np.ones(2 ** nqubits) / np.sqrt(2 ** nqubits) final_state = evolve(final_time=1, initial_state=initial_state) - # print(observable[:]) + print(observable[:]) # will print a ``tf.Tensor`` of shape ``(1001,)`` that holds (t) values +.. testoutput:: + :hide: + + ... Note that the time step ``dt=1e-3`` defines how often we calculate during @@ -1344,10 +1360,14 @@ similar to other callbacks: # define and execute the ``AdiabaticEvolution`` model evolution = models.AdiabaticEvolution(h0, h1, lambda t: t, dt=1e-1, callbacks=[gap, ground]) - # DOCTEST ERROR: prints optimization parameters - # final_state = evolution(final_time=1.0) + + final_state = evolution(final_time=1.0) # print the values of the gap at each evolution time step - # print(gap[:]) + print(gap[:]) +.. testoutput:: + :hide: + + ... The scheduling function ``s`` should be a callable that accepts one (s(t)) or @@ -1411,10 +1431,13 @@ done as follows: evolution = models.AdiabaticEvolution(h0, h1, sp, dt=1e-2) # Find the optimal value for ``p`` starting from ``p = 0.5`` and ``T=1``. initial_guess = [0.5, 1] - # DOCTEST ERROR: prints results from simulation. # best, params, extra = evolution.minimize(initial_guess, method="BFGS", options={'disp': True}) - # print(best) # prints the best energy

found from the final state - # print(params) # prints the optimal values for the parameters. + print(best) # prints the best energy

found from the final state + print(params) # prints the optimal values for the parameters. +.. testoutput:: + :hide: + + ... Note that the ``minimize`` method optimizes both the free parameters ``p`` of the scheduling function as well as the total evolution time. The initial guess diff --git a/doc/source/code-examples/examples.rst b/doc/source/code-examples/examples.rst index 36cc955172..5701085650 100644 --- a/doc/source/code-examples/examples.rst +++ b/doc/source/code-examples/examples.rst @@ -23,11 +23,15 @@ Here is an example of a circuit with 2 qubits: initial_state = np.ones(4) / 2.0 # Execute the circuit and obtain the final state result = c(initial_state) # c.execute(initial_state) also works - # print(result.state()) + print(result.state()) # should print `tf.Tensor([1, 0, 0, 0])` - # print(result.state(numpy=True)) + print(result.state(numpy=True)) # should print `np.array([1, 0, 0, 0])` +.. testoutput:: + :hide: + ... + If you are planning to freeze the circuit and just query for different initial states then you can use the ``Circuit.compile()`` method which will improve evaluation performance, e.g.: diff --git a/src/qibo/abstractions/callbacks.py b/src/qibo/abstractions/callbacks.py index f5a32eec3c..7f7a5ec7dd 100644 --- a/src/qibo/abstractions/callbacks.py +++ b/src/qibo/abstractions/callbacks.py @@ -80,12 +80,15 @@ class EntanglementEntropy(Callback): c.add(gates.CallbackGate(entropy)) # execute the circuit final_state = c() - # WARNING: doctest cannot test printed tensors trivially - # print(entropy[:]) + print(entropy[:]) # Should print [0, 0, 1] which is the entanglement entropy # after every gate in the calculation. - # print(entropy.spectrum) + print(entropy.spectrum) # Print the entanglement spectrum. + .. testoutput:: + :hide: + + ... """ def __init__(self, partition: Optional[List[int]] = None, @@ -182,11 +185,14 @@ class Gap(Callback): evolution = AdiabaticEvolution(h0, h1, lambda t: t, dt=1e-1, callbacks=[gap, ground, excited]) final_state = evolution(final_time=1.0) - # WARNING: doctest cannot test printed tensors trivially # print results - # print(ground[:]) - # print(excited[:]) - # print(gap[:]) + print(ground[:]) + print(excited[:]) + print(gap[:]) + .. testoutput:: + :hide: + + ... """ def __init__(self, mode: Union[str, int] = "gap", check_degenerate: bool = True): diff --git a/src/qibo/abstractions/circuit.py b/src/qibo/abstractions/circuit.py index b7d06a9326..4f61b1d5e5 100644 --- a/src/qibo/abstractions/circuit.py +++ b/src/qibo/abstractions/circuit.py @@ -654,8 +654,19 @@ def summary(self) -> str: c.add(gates.H(2)) c.add(gates.TOFFOLI(0, 1, 2)) print(c.summary()) + # Prints + ''' + Circuit depth = 5 + Total number of gates = 6 + Number of qubits = 3 + Most common gates: + h: 3 + cx: 2 + ccx: 1 + ''' .. testoutput:: + :hide: Circuit depth = 5 Total number of gates = 6 From d29b50fde3104ccf68395148f4a6336b56774be7 Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Tue, 12 Oct 2021 15:16:29 +0200 Subject: [PATCH 14/18] Apply suggestions from code review Co-authored-by: Marco Lazzarin <48728634+mlazzarin@users.noreply.github.com> --- doc/source/code-examples/advancedexamples.rst | 3 --- doc/source/code-examples/examples.rst | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index c04949ea00..6132bd30e6 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -836,7 +836,6 @@ For example: for _ in range(nepochs): optimize(params) - The user may also use ``tf.Variable`` and parametrized gates in any other way that is supported by Tensorflow, such as defining @@ -1470,7 +1469,6 @@ corresponding 16x16 matrix: import numpy as np from qibo import hamiltonians, matrices - # ZZ terms matrix = np.kron(np.kron(matrices.Z, matrices.Z), np.kron(matrices.I, matrices.I)) matrix += np.kron(np.kron(matrices.I, matrices.Z), np.kron(matrices.Z, matrices.I)) @@ -1483,7 +1481,6 @@ corresponding 16x16 matrix: matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.I, matrices.X)) # Create Hamiltonian object ham = hamiltonians.Hamiltonian(4, matrix) - Although it is possible to generalize the above construction to arbitrary number of qubits this procedure may be more complex for other Hamiltonians. Moreover diff --git a/doc/source/code-examples/examples.rst b/doc/source/code-examples/examples.rst index 5701085650..0747cf7a6b 100644 --- a/doc/source/code-examples/examples.rst +++ b/doc/source/code-examples/examples.rst @@ -54,7 +54,6 @@ evaluation performance, e.g.: for i in range(100): init_state = np.ones(4) / 2.0 + i c(init_state) - Note that compiling is only supported when the native ``tensorflow`` backend is used. This backend is much slower than ``qibotf`` which uses custom tensorflow operators to apply gates. @@ -131,7 +130,7 @@ For example for the circuit of the previous example: # returns "h" all_h_gates = c.gates_of_type("h") - # returns the list [(0, ref to H(0)), (1, ref to H(1)), (4, ref to H(2)) + # returns the list [(0, ref to H(0)), (1, ref to H(1)), (4, ref to H(2))] A circuit may contain multi-controlled or other gates that are not supported by OpenQASM. The ``circuit.decompose(*free)`` method decomposes such gates to From 1fdda67c8b6ebfbd6eeb30541242825e99c37d8e Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Tue, 12 Oct 2021 15:40:30 +0200 Subject: [PATCH 15/18] fix error from previous commit --- doc/source/code-examples/advancedexamples.rst | 3 +++ doc/source/code-examples/examples.rst | 1 + 2 files changed, 4 insertions(+) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 6132bd30e6..3103ef8f72 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -837,6 +837,7 @@ For example: for _ in range(nepochs): optimize(params) + The user may also use ``tf.Variable`` and parametrized gates in any other way that is supported by Tensorflow, such as defining `custom Keras layers `_ @@ -1469,6 +1470,7 @@ corresponding 16x16 matrix: import numpy as np from qibo import hamiltonians, matrices + # ZZ terms matrix = np.kron(np.kron(matrices.Z, matrices.Z), np.kron(matrices.I, matrices.I)) matrix += np.kron(np.kron(matrices.I, matrices.Z), np.kron(matrices.Z, matrices.I)) @@ -1482,6 +1484,7 @@ corresponding 16x16 matrix: # Create Hamiltonian object ham = hamiltonians.Hamiltonian(4, matrix) + Although it is possible to generalize the above construction to arbitrary number of qubits this procedure may be more complex for other Hamiltonians. Moreover constructing the full matrix does not scale well with increasing the number of diff --git a/doc/source/code-examples/examples.rst b/doc/source/code-examples/examples.rst index 0747cf7a6b..8c6bc831f3 100644 --- a/doc/source/code-examples/examples.rst +++ b/doc/source/code-examples/examples.rst @@ -54,6 +54,7 @@ evaluation performance, e.g.: for i in range(100): init_state = np.ones(4) / 2.0 + i c(init_state) + Note that compiling is only supported when the native ``tensorflow`` backend is used. This backend is much slower than ``qibotf`` which uses custom tensorflow operators to apply gates. From aed7fca9794d59ca11b1f24bfaae584838ed20c8 Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Tue, 12 Oct 2021 18:19:21 +0200 Subject: [PATCH 16/18] disabling test for decorated function --- doc/source/code-examples/advancedexamples.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 3103ef8f72..3d39a43af0 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -811,7 +811,7 @@ possible to use :meth:`qibo.abstractions.circuit.AbstractCircuit.set_parameters` circuit needs to be defined inside the compiled ``tf.GradientTape()``. For example: -.. testcode:: +.. code-block:: python import qibo qibo.set_backend("tensorflow") @@ -823,6 +823,7 @@ For example: target_state = tf.ones(4, dtype=tf.complex128) / 2.0 params = tf.Variable(tf.random.uniform((2,), dtype=tf.float64)) + @tf_function def optimize(params): with tf.GradientTape() as tape: c = models.Circuit(2) From bf97d9898fc9ddc8e9153cb076b0595b480c2b9d Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Tue, 12 Oct 2021 21:50:26 +0200 Subject: [PATCH 17/18] Update doc/source/code-examples/advancedexamples.rst Co-authored-by: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> --- doc/source/code-examples/advancedexamples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 3d39a43af0..1eb67ed812 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -823,7 +823,7 @@ For example: target_state = tf.ones(4, dtype=tf.complex128) / 2.0 params = tf.Variable(tf.random.uniform((2,), dtype=tf.float64)) - @tf_function + @tf.function def optimize(params): with tf.GradientTape() as tape: c = models.Circuit(2) From 4b2bec303077dab5e5137d8c41c2638423ae1398 Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Wed, 13 Oct 2021 15:07:11 +0200 Subject: [PATCH 18/18] enabling doctest for all python versions --- .github/workflows/rules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index 9bc0609c1e..1c95aea085 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -47,7 +47,7 @@ jobs: run: | pytest --cov=qibo --cov-report=xml --pyargs qibo - name: Test documentation examples - if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == '3.9' + if: startsWith(matrix.os, 'ubuntu') run: | sudo apt install pandoc make -C doc doctest