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 and cod.

Parameters:
ty_factory#

alias of Ty

classmethod id(dom=None)[source]#

The identity circuit on a given domain.

Parameters:

dom (int | Ty) – The domain (and codomain) of the identity, or qubit ** dom if dom is an int.

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 a discopy.tensor.Functor.

init_and_discard()[source]#

Returns a circuit with empty domain and only bits as codomain.

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 or ChannelFunctor instead.

  • mixed (bool, optional) – Whether to apply discopy.tensor.Functor or ChannelFunctor.

  • 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 unitary discopy.tensor.Tensor or as a Channel:

>>> 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 a pytket.Backend one should set measure_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 a channel.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, then tk_circuit will hold attributes post_selection and scalar.

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 a Circuit, or a list of pytket circuits into a Sum.

Parameters:

tk_circuits (pytket.Circuit) – potentially with scalar and post_selection attributes.

Returns:

circuit – Such that Circuit.from_tk(circuit.to_tk()) == circuit.

Return type:

Circuit

Note

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 and circuit.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)
draw(**params)[source]#

We draw the labels of a circuit whenever it’s mixed.

apply_controlled(gate, *indices)[source]#

Post-compose with a controlled gate at given indices.

Parameters:
  • gates – The gate to control.

  • indices (int) – The indices on which to apply the gate.

  • gate (Circuit) –

Return type:

Circuit

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:

Circuit

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:

Circuit

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:

Circuit

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:

Circuit

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:

Circuit

CX(i, j)#

Apply CX gate to a circuit given qubit indices.

Parameters:
  • i (int) – Control index.

  • j (int) – Target index.

Return type:

Circuit

CY(i, j)#

Apply CY gate to a circuit given qubit indices.

Parameters:
  • i (int) – Control index.

  • j (int) – Target index.

Return type:

Circuit

CZ(i, j)#

Apply CZ gate to a circuit given qubit indices.

Parameters:
  • i (int) – Control index.

  • j (int) – Target index.

Return type:

Circuit

H(i)#

Apply H gate to a circuit given qubit index.

Parameters:

i (int) – Target index.

Return type:

Circuit

Rx(phi, i)#

Apply Rx to a circuit given phase and target index.

Parameters:
  • phi (float) – Phase.

  • i (int) – Target index.

Return type:

Circuit

Ry(phi, i)#

Apply Ry to a circuit given phase and target index.

Parameters:
  • phi (float) – Phase.

  • i (int) – Target index.

Return type:

Circuit

Rz(phi, i)#

Apply Rz to a circuit given phase and target index.

Parameters:
  • phi (float) – Phase.

  • i (int) – Target index.

Return type:

Circuit

S(i)#

Apply S gate to a circuit given qubit index.

Parameters:

i (int) – Target index.

Return type:

Circuit

SWAP(i)#

Apply SWAP gate to a circuit given qubit index.

Parameters:

i (int) – Target index.

Return type:

Circuit

T(i)#

Apply T gate to a circuit given qubit index.

Parameters:

i (int) – Target index.

Return type:

Circuit

U1(phi, i)#

Apply U1 to a circuit given phase and target index.

Parameters:
  • phi (float) – Phase.

  • i (int) – Target index.

Return type:

Circuit

X(i)#

Apply X gate to a circuit given qubit index.

Parameters:

i (int) – Target index.

Return type:

Circuit

Y(i)#

Apply Y gate to a circuit given qubit index.

Parameters:

i (int) – Target index.

Return type:

Circuit

Z(i)#

Apply Z gate to a circuit given qubit index.

Parameters:

i (int) – Target index.

Return type:

Circuit

braid_factory#

alias of Swap

factory#

alias of Circuit

sum_factory#

alias of Sum