The Quantum Circuit
from mpqp import QCircuit
MPQP is focused on gate based quantum computing. As such, the main element of
a script using MPQP is the quantum circuit, or QCircuit
. The
QCircuit
contains the data of all gates, measures, and noise models you
want to apply to your qubits.
The qubits are only referred by their indices, so one could keep track of specific registers using python features, for instance
>>> circ = QCircuit(6)
>>> targets = range(3)
>>> ancillas = range(3,6)
>>> for i in range(3):
... circ.add(CNOT(targets[i], ancillas[i]))
could be use to add CNOT gates to your circuit, using the two registers
targets
and ancillas
.
- class QCircuit(data, *, nb_qubits=None, nb_cbits=None, label=None)[source]
Bases:
object
This class models a quantum circuit.
A circuit is composed of instructions and noise models applied on quantum and/or classical bits. These elements (instructions and noise models) will be called
components
hereafter.- Parameters
data (int | Sequence[Union[Instruction, NoiseModel]]) – Number of qubits or list of
components
to initialize the circuit with. If the number of qubits is passed, it should be a positive int.nb_qubits (Optional[int]) – Optional number of qubits, in case you input the sequence of instruction and want to hardcode the number of qubits.
nb_cbits (Optional[int]) – Number of classical bits. It should be positive.
label (Optional[str]) – Name of the circuit.
Examples
>>> circuit = QCircuit(2) >>> circuit.pretty_print() QCircuit : Size (Qubits, Cbits) = (2, 0), Nb instructions = 0 q_0: q_1:
>>> circuit = QCircuit(5, nb_cbits=2, label="Circuit 1") >>> circuit.add(Rx(1.23, 3)) >>> circuit.pretty_print() QCircuit Circuit 1: Size (Qubits, Cbits) = (5, 2), Nb instructions = 1 q_0: ──────────── q_1: ──────────── q_2: ──────────── ┌──────────┐ q_3: ┤ Rx(1.23) ├ └──────────┘ q_4: ──────────── c: 2/════════════
>>> circuit = QCircuit(3, label="NoiseExample") >>> circuit.add([H(0), T(1), CNOT(0,1), S(2)]) >>> circuit.add(BasisMeasure(list(range(3)), shots=2345)) >>> circuit.add(Depolarizing(prob=0.50, targets=[0, 1])) >>> circuit.pretty_print() QCircuit NoiseExample: Size (Qubits, Cbits) = (3, 3), Nb instructions = 5 Depolarizing noise: probability 0.5 on qubits [0, 1] ┌───┐ ┌─┐ q_0: ┤ H ├──■──┤M├─── ├───┤┌─┴─┐└╥┘┌─┐ q_1: ┤ T ├┤ X ├─╫─┤M├ ├───┤└┬─┬┘ ║ └╥┘ q_2: ┤ S ├─┤M├──╫──╫─ └───┘ └╥┘ ║ ║ c: 3/═══════╩═══╩══╩═ 2 0 1
- add(components)[source]
Adds a
component
or a list ofcomponent
at the end of the circuit.- Parameters
components (Union[Instruction, NoiseModel, Sequence[mpqp.core.instruction.instruction.Instruction | mpqp.noise.noise_model.NoiseModel]]) – Instruction(s) or NoiseModel(s) to append to the circuit.
Examples
>>> circuit = QCircuit(2) >>> circuit.add(X(0)) >>> circuit.add([CNOT(0, 1), BasisMeasure([0, 1], shots=100)]) >>> circuit.pretty_print() QCircuit : Size (Qubits, Cbits) = (2, 2), Nb instructions = 3 ┌───┐ ┌─┐ q_0: ┤ X ├──■──┤M├─── └───┘┌─┴─┐└╥┘┌─┐ q_1: ─────┤ X ├─╫─┤M├ └───┘ ║ └╥┘ c: 2/═══════════╩══╩═ 0 1
>>> circuit.add(Depolarizing(0.3, [0,1], dimension=2, gates=[CNOT])) >>> circuit.add([Depolarizing(0.02, [0])]) >>> circuit.pretty_print() QCircuit : Size (Qubits, Cbits) = (2, 2), Nb instructions = 3 Depolarizing noise: probability 0.3 for gate CNOT Depolarizing noise: probability 0.02 on qubit 0 ┌───┐ ┌─┐ q_0: ┤ X ├──■──┤M├─── └───┘┌─┴─┐└╥┘┌─┐ q_1: ─────┤ X ├─╫─┤M├ └───┘ ║ └╥┘ c: 2/═══════════╩══╩═ 0 1
- append(other, qubits_offset=0)[source]
Appends the circuit at the end (right side) of this circuit, inplace.
If the size of the
other
is smaller than this circuit, the parameterqubits_offset
can be used to indicate at which qubit theother
circuit must be added.- Parameters
other (QCircuit) – The circuit to append at the end of this circuit.
qubits_offset (int) – If the circuit in parameter is smaller, this parameter precise at which qubit (vertically) the circuit will be added.
- Raises
NumberQubitsError – if the circuit in parameter is larger than this circuit or if the
qubits_offset
is too big such that theother
circuit would “stick out”.- Return type
None
Example
>>> c1 = QCircuit([CNOT(0,1),CNOT(1,2)]) >>> c2 = QCircuit([X(1),CNOT(1,2)]) >>> c1.append(c2) >>> print(c1) q_0: ──■───────────────── ┌─┴─┐ ┌───┐ q_1: ┤ X ├──■──┤ X ├──■── └───┘┌─┴─┐└───┘┌─┴─┐ q_2: ─────┤ X ├─────┤ X ├ └───┘ └───┘
- count_gates(gate=None)[source]
Returns the number of gates contained in the circuit. If a specific gate is given in the
gate
arg, it returns the number of occurrences of this gate.- Parameters
gate (Optional[Type[Gate]]) – The gate for which we want to know its occurrence in this circuit.
- Returns
The number of gates (eventually of a specific type) contained in the circuit.
- Return type
int
Examples
>>> circuit = QCircuit( ... [X(0), Y(1), Z(2), CNOT(0, 1), SWAP(0, 1), CZ(1, 2), X(2), X(1), X(0)] ... ) >>> circuit.count_gates() 9 >>> circuit.count_gates(X) 4 >>> circuit.count_gates(Ry) 0
- depth()[source]
Computes the depth of the circuit.
- Returns
Depth of the circuit.
- Return type
int
Examples
>>> QCircuit([CNOT(0, 1), CNOT(1, 2), CNOT(0, 1), X(2)]).depth() 3 >>> QCircuit([CNOT(0, 1), CNOT(1, 2), CNOT(0, 1), Barrier(), X(2)]).depth() 4
- display(output='mpl')[source]
Displays this circuit in the desired output format.
For now, this uses the qiskit circuit drawer, so all formats supported by qiskit are supported.
- Parameters
output (str) – Format of the output, see docs.quantum.ibm.com/build/circuit-visualization for more information.
Examples
>>> theta = symbols("θ") >>> circ = QCircuit([ ... P(theta, 0), ... ExpectationMeasure([0], Observable(np.array([[0, 1], [1, 0]])), shots=1000) ... ]) >>> circ.display("text") ┌──────┐ q: ┤ P(θ) ├ └──────┘ >>> print(circ.display("latex_source")) \documentclass[border=2px]{standalone} \usepackage[braket, qm]{qcircuit} \usepackage{graphicx} \begin{document} \scalebox{1.0}{ \Qcircuit @C=1.0em @R=0.2em @!R { \\ \nghost{{q} : } & \lstick{{q} : } & \gate{\mathrm{P}\,(\mathrm{{\ensuremath{\theta}}})} & \qw & \qw\\ \\ }} \end{document}
- get_measurements()[source]
Returns all the measurements present in this circuit.
- Returns
The list of all measurements present in the circuit.
- Return type
Example
>>> circuit = QCircuit([ ... BasisMeasure([0, 1], shots=1000), ... ExpectationMeasure([1], Observable(np.identity(2)), shots=1000) ... ]) >>> circuit.get_measurements() [BasisMeasure([0, 1], shots=1000), ExpectationMeasure([1], Observable(array([[1.+0.j, 0.+0.j], [0.+0.j, 1.+0.j]], dtype=complex64)), shots=1000)]
- inverse()[source]
Generate the inverse (dagger) of this circuit.
- Returns
The inverse circuit.
- Return type
Examples
>>> c1 = QCircuit([H(0), CNOT(0,1)]) >>> print(c1) ┌───┐ q_0: ┤ H ├──■── └───┘┌─┴─┐ q_1: ─────┤ X ├ └───┘ >>> print(c1.inverse()) ┌───┐ q_0: ──■──┤ H ├ ┌─┴─┐└───┘ q_1: ┤ X ├───── └───┘ >>> c2 = QCircuit([S(0), CZ(0,1), H(1), Ry(4.56, 1)]) >>> print(c2) ┌───┐ q_0: ┤ S ├─■────────────────── └───┘ │ ┌───┐┌──────────┐ q_1: ──────■─┤ H ├┤ Ry(4.56) ├ └───┘└──────────┘ >>> print(c2.inverse()) ┌───┐ q_0: ──────────────────■─┤ S ├ ┌──────────┐┌───┐ │ └───┘ q_1: ┤ Ry(4.56) ├┤ H ├─■────── └──────────┘└───┘
# TODO implement, test, fill second example The inverse could be computed in several ways, depending on the definition of the circuit. One can inverse each gate in the circuit, or take the global unitary of the gate and inverse it.
- pretty_print()[source]
Provides a pretty print of the QCircuit.
Examples
>>> c = QCircuit([H(0), CNOT(0,1)]) >>> c.pretty_print() QCircuit : Size (Qubits, Cbits) = (2, 0), Nb instructions = 2 ┌───┐ q_0: ┤ H ├──■── └───┘┌─┴─┐ q_1: ─────┤ X ├ └───┘
- size()[source]
Provides the size of the circuit, in terms of number of quantum and classical bits.
- Returns
A couple
(q, c)
of integers, withq
the number of qubits, andc
the number of cbits of this circuit.- Return type
tuple[int, int]
Examples
>>> c1 = QCircuit([CNOT(0,1),CNOT(1,2)]) >>> c1.size() (3, 0) >>> c2 = QCircuit(3,nb_cbits=2) >>> c2.size() (3, 2) >>> c3 = QCircuit([CNOT(0,1),CNOT(1,2), BasisMeasure([0,1,2], shots=200)]) >>> c3.size() (3, 3)
- subs(values, remove_symbolic=False)[source]
Substitute the parameters of the circuit with complex values. Optionally also remove all symbolic variables such as \(\pi\) (needed for example for circuit execution).
Since we use
sympy
for gates’ parameters,values
can in fact be anything thesubs
method fromsympy
would accept.- Parameters
values (dict[Expr | str, Complex]) – Mapping between the variables and the replacing values.
remove_symbolic (bool) – If symbolic values should be replaced by their numeric counterpart.
- Returns
The circuit with the replaced parameters.
- Return type
Examples
>>> theta, k = symbols("θ k") >>> c = QCircuit( ... [Rx(theta, 0), CNOT(1,0), CNOT(1,2), X(2), Rk(2,1), H(0), CRk(k, 0, 1), ... BasisMeasure(list(range(3)), shots=1000)] ... ) >>> print(c) ┌───────┐┌───┐┌───┐ ┌─┐ q_0: ┤ Rx(θ) ├┤ X ├┤ H ├────────────■─────────────────┤M├─── └───────┘└─┬─┘└───┘┌─────────┐ │P(2**(1 - k)*pi) └╥┘┌─┐ q_1: ───────────■────■──┤ P(pi/2) ├─■──────────────────╫─┤M├ ┌─┴─┐└──┬───┬──┘ ┌─┐ ║ └╥┘ q_2: ──────────────┤ X ├───┤ X ├───────────┤M├─────────╫──╫─ └───┘ └───┘ └╥┘ ║ ║ c: 3/═══════════════════════════════════════╩══════════╩══╩═ 2 0 1 >>> print(c.subs({theta: np.pi, k: 1})) ┌───────┐┌───┐┌───┐ ┌─┐ q_0: ┤ Rx(π) ├┤ X ├┤ H ├───────────■─────┤M├─── └───────┘└─┬─┘└───┘┌────────┐ │P(π) └╥┘┌─┐ q_1: ───────────■────■──┤ P(π/2) ├─■──────╫─┤M├ ┌─┴─┐└─┬───┬──┘ ┌─┐ ║ └╥┘ q_2: ──────────────┤ X ├──┤ X ├─────┤M├───╫──╫─ └───┘ └───┘ └╥┘ ║ ║ c: 3/════════════════════════════════╩════╩══╩═ 2 0 1
- tensor(other)[source]
Computes the tensor product of this circuit with the one in parameter.
In the circuit notation, the upper part of the output circuit will correspond to the first circuit, while the bottom part correspond to the one in parameter.
- Parameters
other (QCircuit) – QCircuit being the second operand of the tensor product with this circuit.
other – QCircuit being the second operand of the tensor product with this circuit.
- Returns
The QCircuit resulting from the tensor product of this circuit with the one in parameter.
- Returns
The QCircuit resulting from the tensor product of this circuit with the one in parameter.
- Return type
Example
>>> c1 = QCircuit([CNOT(0,1),CNOT(1,2)]) >>> c2 = QCircuit([X(1),CNOT(1,2)]) >>> print(c1.tensor(c2)) q_0: ──■─────── ┌─┴─┐ q_1: ┤ X ├──■── └───┘┌─┴─┐ q_2: ─────┤ X ├ └───┘ q_3: ────────── ┌───┐ q_4: ┤ X ├──■── └───┘┌─┴─┐ q_5: ─────┤ X ├ └───┘
- to_other_language(language=Language.QISKIT, cirq_proc_id=None)[source]
Transforms this circuit into the corresponding circuit in the language specified in the
language
arg.By default, the circuit is translated to the corresponding
QuantumCircuit
in Qiskit, since it is the interface we use to generate the OpenQASM code.In the future, we will generate the OpenQASM code on our own, and this method will be used only for complex objects that are not tractable by OpenQASM (like hybrid structures).
Note
Most providers take noise into account at the job level. A notable exception is Braket, where the noise is contained in the circuit object. For this reason you find the noise included in the Braket circuits.
- Parameters
language (Language) – Enum representing the target language.
cirq_proc_id (Optional[str]) – Identifier of the processor for cirq.
- Returns
The corresponding circuit in the target language.
- Return type
Union[QuantumCircuit, myQLM_Circuit, braket_Circuit, cirq_Circuit]
Examples
>>> circuit = QCircuit([X(0), CNOT(0, 1)]) >>> qc = circuit.to_other_language() >>> type(qc) <class 'qiskit.circuit.quantumcircuit.QuantumCircuit'> >>> circuit2 = QCircuit([H(0), CZ(0,1), Depolarizing(0.6, [0])]) >>> braket_circuit = circuit2.to_other_language(Language.BRAKET) >>> print(braket_circuit) T : │ 0 │ 1 │ ┌───┐ ┌───────────┐ ┌───────────┐ q0 : ─┤ H ├─┤ DEPO(0.6) ├───●───┤ DEPO(0.6) ├─ └───┘ └───────────┘ │ └───────────┘ ┌─┴─┐ q1 : ─────────────────────┤ Z ├─────────────── └───┘ T : │ 0 │ 1 │
- to_qasm2()[source]
Converts this circuit to the corresponding OpenQASM 2 code.
For now, we use an intermediate conversion to a Qiskit
QuantumCircuit
.- Returns
A string representing the OpenQASM2 code corresponding to this circuit.
- Return type
str
Example
>>> circuit = QCircuit([X(0), CNOT(0, 1), BasisMeasure([0, 1], shots=100)]) >>> print(circuit.to_qasm2()) OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; creg c[2]; x q[0]; cx q[0],q[1]; measure q[0] -> c[0]; measure q[1] -> c[1];
- to_qasm3()[source]
Converts this circuit to the corresponding OpenQASM 3 code.
For now, we use an intermediate conversion to OpenQASM 2, and then a converter from 2 to 3.
- Returns
A string representing the OpenQASM3 code corresponding to this circuit.
- Return type
str
Example
>>> circuit = QCircuit([X(0), CNOT(0, 1), BasisMeasure([0, 1], shots=100)]) >>> print(circuit.to_qasm3()) OPENQASM 3.0; include "stdgates.inc"; qubit[2] q; bit[2] c; x q[0]; cx q[0],q[1]; c[0] = measure q[0]; c[1] = measure q[1];
- variables()[source]
Returns all the parameters involved in this circuit.
- Returns
All the parameters of the circuit.
- Return type
set[Basic]
Example
>>> circ = QCircuit([ ... Rx(theta, 0), CNOT(1,0), CNOT(1,2), X(2), Rk(2,1), ... H(0), CRk(k, 0, 1), ExpectationMeasure([1], obs) ... ]) >>> circ.variables() {θ, k}
- without_measurements()[source]
Provides a copy of this circuit with all the measurements removed.
- Returns
A copy of this circuit with all the measurements removed.
- Return type
Example
>>> circuit = QCircuit([X(0), CNOT(0, 1), BasisMeasure([0, 1], shots=100)]) >>> print(circuit) ┌───┐ ┌─┐ q_0: ┤ X ├──■──┤M├─── └───┘┌─┴─┐└╥┘┌─┐ q_1: ─────┤ X ├─╫─┤M├ └───┘ ║ └╥┘ c: 2/═══════════╩══╩═ 0 1 >>> print(circuit.without_measurements()) ┌───┐ q_0: ┤ X ├──■── └───┘┌─┴─┐ q_1: ─────┤ X ├ └───┘
- without_noises()[source]
Provides a copy of this circuit with all the noise models removed.
- Returns
A copy of this circuit with all the noise models removed.
- Return type
Example
>>> circuit = QCircuit(2) >>> circuit.add([CNOT(0, 1), Depolarizing(prob=0.4, targets=[0, 1]), BasisMeasure([0, 1], shots=100)]) >>> print(circuit) ┌─┐ q_0: ──■──┤M├─── ┌─┴─┐└╥┘┌─┐ q_1: ┤ X ├─╫─┤M├ └───┘ ║ └╥┘ c: 2/══════╩══╩═ 0 1 NoiseModel: Depolarizing(0.4, [0, 1], 1) >>> print(circuit.without_noises()) ┌─┐ q_0: ──■──┤M├─── ┌─┴─┐└╥┘┌─┐ q_1: ┤ X ├─╫─┤M├ └───┘ ║ └╥┘ c: 2/══════╩══╩═ 0 1
- instructions: list[mpqp.core.instruction.instruction.Instruction]
List of instructions of the circuit.
- label
See parameter description.
- nb_cbits
See parameter description.
- nb_qubits: int
Number of qubits of the circuit.
- noises: list[mpqp.noise.noise_model.NoiseModel]
List of noise models attached to the circuit.