Circuit#

class discopy.quantum.circuit.Circuit(inside, dom, cod, _scan=True)[source]#

Bases: discopy.tensor.Diagram

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 (discopy.tensor.Tensor) – If backend is not None or 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:

>>> 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[complex](dom=Dim(1), cod=Dim(2), array=[0., 1.])
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)[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.

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 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

braid_factory#

alias of Swap

factory#

alias of Circuit

sum_factory#

alias of Sum