Circuit#
- class discopy.quantum.circuit.Circuit(inside, dom, cod, _scan=True)[source]#
Bases:
Diagram[complex]
A circuit is a tensor diagram with bits and qubits as
dom
andcod
.- Parameters:
inside (tuple[Layer, ...]) – The layers inside the circuit diagram.
dom (quantum.circuit.Ty) – The domain of the circuit diagram.
cod (quantum.circuit.Ty) – The codomain of the circuit diagram.
- classmethod id(dom=None)[source]#
The identity circuit on a given domain.
- Parameters:
dom (int | Ty | None) – The domain (and codomain) of the identity, or
qubit ** dom
ifdom
is anint
.
- property is_mixed#
Whether the circuit is mixed, i.e. it contains both bits and qubits or it discards qubits.
Mixed circuits can be evaluated only by a
ChannelFunctor
not adiscopy.tensor.Functor
.
- eval(*others, backend=None, mixed=False, contractor=None, **params)[source]#
Evaluate a circuit on a backend, or simulate it with numpy.
- Parameters:
others (
discopy.quantum.circuit.Circuit
) – Other circuits to process in batch.backend (pytket.Backend, optional) – Backend on which to run the circuit, if none then we apply
discopy.tensor.Functor
orChannelFunctor
instead.mixed (bool, optional) – Whether to apply
discopy.tensor.Functor
orChannelFunctor
.contractor (callable, optional) – Use
tensornetwork
contraction instead of discopy’s basic eval feature.params (kwargs, optional) – Get passed to Circuit.get_counts.
- Returns:
tensor (Tensor[float]) – If
backend is not None
.tensor (Tensor[complex]) – If
mixed=False
.channel (
Channel
) – Otherwise.
Examples
We can evaluate a pure circuit (i.e. with
not circuit.is_mixed
) as a unitarydiscopy.tensor.Tensor
or as aChannel
:>>> from discopy.quantum import *
>>> H.eval().round(2) Tensor[complex]([0.71+0.j, ..., -0.71+0.j], dom=Dim(2), cod=Dim(2)) >>> H.eval(mixed=True).round(1) Channel([0.5+0.j, ..., 0.5+0.j], dom=Q(Dim(2)), cod=Q(Dim(2)))
We can evaluate a mixed circuit as a
Channel
:>>> from discopy.quantum import Channel >>> assert Measure().eval()\ ... == Channel(dom=Q(Dim(2)), cod=C(Dim(2)), ... array=[1, 0, 0, 0, 0, 0, 0, 1]) >>> circuit = Bits(1, 0) @ Ket(0) >> Discard(bit ** 2 @ qubit) >>> assert circuit.eval() == Channel(dom=CQ(), cod=CQ(), array=[1])
We can execute any circuit on a pytket.Backend and get a
discopy.tensor.Tensor
of real-valued probabilities.>>> circuit = Ket(0, 0) >> sqrt(2) @ H @ X >> CX >> Measure() @ Bra(0) >>> from discopy.quantum.tk import mockBackend >>> backend = mockBackend({(0, 1): 512, (1, 0): 512}) >>> assert circuit.eval(backend=backend, n_shots=2**10).round()\ ... == Tensor[float](dom=Dim(1), cod=Dim(2), array=[0., 1.])
Note
Any extra parameter is passed to
Circuit.get_counts()
. For instance, to evaluate a unitary circuit (i.e. with no measurements) on apytket.Backend
one should setmeasure_all=True
.
- get_counts(*others, backend=None, **params)[source]#
Get counts from a backend, or simulate them with numpy.
- Parameters:
others (
discopy.quantum.circuit.Circuit
) – Other circuits to process in batch.backend (pytket.Backend, optional) – Backend on which to run the circuit, if none then numpy.
n_shots (int, optional) – Number of shots, default is
2**10
.measure_all (bool, optional) – Whether to measure all qubits, default is
False
.normalize (bool, optional) – Whether to normalize the counts, default is
True
.post_select (bool, optional) – Whether to perform post-selection, default is
True
.scale (bool, optional) – Whether to scale the output, default is
True
.seed (int, optional) – Seed to feed the backend, default is
None
.compilation (callable, optional) – Compilation function to apply before getting counts.
- Returns:
counts – From bitstrings to counts.
- Return type:
dict
Examples
>>> from discopy.quantum import * >>> circuit = H @ X >> CX >> Measure(2) >>> from discopy.quantum.tk import mockBackend >>> backend = mockBackend({(0, 1): 512, (1, 0): 512}) >>> circuit.get_counts(backend=backend, n_shots=2**10) {(0, 1): 0.5, (1, 0): 0.5}
- measure(mixed=False)[source]#
Measure a circuit on the computational basis using
numpy
.- Parameters:
mixed (Whether to apply a
tensor.Functor
) – or achannel.Functor
.- Returns:
array
- Return type:
numpy.ndarray
- to_tn(mixed=False)[source]#
Send a diagram to a mixed
tensornetwork
.- Parameters:
mixed (bool, default: False) – Whether to perform mixed (also known as density matrix) evaluation of the circuit.
- Returns:
nodes (
tensornetwork.Node
) – Nodes of the network.output_edge_order (list of
tensornetwork.Edge
) – Output edges of the network.
- to_tk()[source]#
Export to t|ket>.
- Returns:
tk_circuit – A
pytket.Circuit
.- Return type:
pytket.Circuit
Note
No measurements are performed.
SWAP gates are treated as logical swaps.
If the circuit contains scalars or a
Bra
, thentk_circuit
will hold attributespost_selection
andscalar
.
Examples
>>> from discopy.quantum import *
>>> bell_test = H @ qubit >> CX >> Measure() @ Measure() >>> bell_test.to_tk() tk.Circuit(2, 2).H(0).CX(0, 1).Measure(0, 0).Measure(1, 1)
>>> circuit0 = sqrt(2) @ H @ Rx(0.5) >> CX >> Measure() @ Discard() >>> circuit0.to_tk() tk.Circuit(2, 1).H(0).Rx(1.0, 1).CX(0, 1).Measure(0, 0).scale(2)
>>> circuit1 = Ket(1, 0) >> CX >> qubit @ Ket(0) @ qubit >>> circuit1.to_tk() tk.Circuit(3).X(0).CX(0, 2)
>>> circuit2 = X @ qubit ** 2\ ... >> qubit @ SWAP >> CX @ qubit >> qubit @ SWAP >>> circuit2.to_tk() tk.Circuit(3).X(0).CX(0, 2)
>>> circuit3 = Ket(0, 0)\ ... >> H @ qubit\ ... >> qubit @ X\ ... >> CX\ ... >> qubit @ Bra(0) >>> print(repr(circuit3.to_tk())) tk.Circuit(2, 1).H(0).X(1).CX(0, 1).Measure(1, 0).post_select({0: 0})
- to_pennylane(probabilities=False, backend_config=None, diff_method='best')[source]#
Export DisCoPy circuit to PennylaneCircuit.
- Parameters:
probabilties (bool, default: False) – If True, the PennylaneCircuit will return the normalized probabilties of measuring the computational basis states when run. If False, it returns the unnormalized quantum states in the computational basis.
backend_config (dict, default: None) – A dictionary of PennyLane backend configration options, including the provider (e.g. IBM or Honeywell), the device, the number of shots, etc. See the PennyLane plugin documentation for more details.
diff_method (str, default: "best") – The differentiation method to use to obtain gradients for the PennyLane circuit. Some gradient methods are only compatible with simulated circuits. See the PennyLane documentation for more details.
- Return type:
discopy.quantum.pennylane.PennylaneCircuit
- static from_tk(*tk_circuits)[source]#
Translate a
pytket.Circuit
into aCircuit
, or a list ofpytket
circuits into aSum
.- Parameters:
tk_circuits (pytket.Circuit) – potentially with
scalar
andpost_selection
attributes.- Returns:
circuit – Such that
Circuit.from_tk(circuit.to_tk()) == circuit
.- Return type:
Note
Circuit.init_and_discard()
is applied beforehand.SWAP gates are introduced when applying gates to non-adjacent qubits.
Examples
>>> from discopy.quantum import * >>> import pytket as tk
>>> c = Rz(0.5) @ qubit >> qubit @ Rx(0.25) >> CX >>> assert Circuit.from_tk(c.to_tk()) == c.init_and_discard()
>>> tk_GHZ = tk.Circuit(3).H(1).CX(1, 2).CX(1, 0) >>> pprint = lambda c: print(str(c).replace(' >>', '\n >>')) >>> pprint(Circuit.from_tk(tk_GHZ)) Ket(0) >> qubit @ Ket(0) >> qubit @ qubit @ Ket(0) >> qubit @ H @ qubit >> qubit @ CX >> SWAP @ qubit >> CX @ qubit >> SWAP @ qubit >> Discard(qubit) @ qubit @ qubit >> Discard(qubit) @ qubit >> Discard(qubit) >>> circuit = Ket(1, 0) >> CX >> qubit @ Ket(0) @ qubit >>> print(Circuit.from_tk(circuit.to_tk())[3:-3]) X @ qubit @ qubit >> qubit @ SWAP >> CX @ qubit >> qubit @ SWAP
>>> bell_state = Circuit.caps(qubit, qubit) >>> bell_effect = bell_state[::-1] >>> circuit = bell_state @ qubit >> qubit @ bell_effect >> Bra(0) >>> pprint(Circuit.from_tk(circuit.to_tk())[3:]) H @ qubit @ qubit >> CX @ qubit >> qubit @ CX >> qubit @ H @ qubit >> Bra(0) @ qubit @ qubit >> Bra(0) @ qubit >> Bra(0) >> scalar(4)
- grad(var, **params)[source]#
Gradient with respect to
var
.- Parameters:
var (sympy.Symbol) – Differentiated variable.
- Returns:
circuit
- Return type:
discopy.quantum.circuit.Sum
Examples
>>> from math import pi >>> from sympy.abc import phi >>> from discopy.quantum import * >>> circuit = Rz(phi / 2) @ Rz(phi + 1) >> CX >>> assert circuit.grad(phi, mixed=False)\ ... == (scalar(pi/2) @ Rz(phi/2 + .5) @ Rz(phi + 1) >> CX)\ ... + (Rz(phi / 2) @ scalar(pi) @ Rz(phi + 1.5) >> CX)
- jacobian(variables, **params)[source]#
Jacobian with respect to
variables
.- Parameters:
variables (List[sympy.Symbol]) – Differentiated variables.
- Returns:
circuit – with
circuit.dom == self.dom
andcircuit.cod == Digit(len(variables)) @ self.cod
.- Return type:
discopy.quantum.circuit.Sum
Examples
>>> from sympy.abc import x, y >>> from discopy.quantum.gates import Bits, Ket, Rx, Rz >>> circuit = Ket(0) >> Rx(x) >> Rz(y) >>> assert circuit.jacobian([x, y])\ ... == (Bits(0) @ circuit.grad(x)) + (Bits(1) @ circuit.grad(y)) >>> assert not circuit.jacobian([]) >>> assert circuit.jacobian([x]) == circuit.grad(x)
- CCX(i, j, k)#
Apply CCX gate to a circuit given qubit indices.
- Parameters:
i (int) – First control index.
j (int) – Second control index.
k (int) – Target index.
- Return type:
- CCZ(i, j, k)#
Apply CCZ gate to a circuit given qubit indices.
- Parameters:
i (int) – First control index.
j (int) – Second control index.
k (int) – Target index.
- Return type:
- CRx(phi, i, j)#
Apply
CRx
to a circuit given phase and indices.- Parameters:
phi (float) – Phase.
i (int) – Control index.
j (int) – Target index.
- Return type:
- CRz(phi, i, j)#
Apply
CRz
to a circuit given phase and indices.- Parameters:
phi (float) – Phase.
i (int) – Control index.
j (int) – Target index.
- Return type:
- CU1(phi, i, j)#
Apply
CU1
to a circuit given phase and indices.- Parameters:
phi (float) – Phase.
i (int) – Control index.
j (int) – Target index.
- Return type:
- CX(i, j)#
Apply CX gate to a circuit given qubit indices.
- Parameters:
i (int) – Control index.
j (int) – Target index.
- Return type:
- CY(i, j)#
Apply CY gate to a circuit given qubit indices.
- Parameters:
i (int) – Control index.
j (int) – Target index.
- Return type:
- CZ(i, j)#
Apply CZ gate to a circuit given qubit indices.
- Parameters:
i (int) – Control index.
j (int) – Target index.
- Return type:
- H(i)#
Apply H gate to a circuit given qubit index.
- Parameters:
i (int) – Target index.
- Return type:
- Rx(phi, i)#
Apply
Rx
to a circuit given phase and target index.- Parameters:
phi (float) – Phase.
i (int) – Target index.
- Return type:
- Ry(phi, i)#
Apply
Ry
to a circuit given phase and target index.- Parameters:
phi (float) – Phase.
i (int) – Target index.
- Return type:
- Rz(phi, i)#
Apply
Rz
to a circuit given phase and target index.- Parameters:
phi (float) – Phase.
i (int) – Target index.
- Return type:
- S(i)#
Apply S gate to a circuit given qubit index.
- Parameters:
i (int) – Target index.
- Return type:
- SWAP(i)#
Apply SWAP gate to a circuit given qubit index.
- Parameters:
i (int) – Target index.
- Return type:
- T(i)#
Apply T gate to a circuit given qubit index.
- Parameters:
i (int) – Target index.
- Return type:
- U1(phi, i)#
Apply
U1
to a circuit given phase and target index.- Parameters:
phi (float) – Phase.
i (int) – Target index.
- Return type:
- X(i)#
Apply X gate to a circuit given qubit index.
- Parameters:
i (int) – Target index.
- Return type:
- Y(i)#
Apply Y gate to a circuit given qubit index.
- Parameters:
i (int) – Target index.
- Return type:
- Z(i)#
Apply Z gate to a circuit given qubit index.
- Parameters:
i (int) – Target index.
- Return type: