Circuit Components

CircuitComponent

A base class for the circuit components (states, transformations, measurements, and any component made by combining CircuitComponents).

CircuitComponent

class mrmustard.lab.circuit_components.CircuitComponent(ansatz_factory=None, wires=None, name=None)[source]

A base class for the circuit components (states, transformations, measurements, and any component made by combining CircuitComponents). CircuitComponents are defined by their ansatz and wires.

Parameters:
  • ansatz_factory (AnsatzFactory | None) – The AnsatzFactory of this circuit component.

  • wires (Wires | None) – The wires of this circuit component.

  • name (str | None) – The name of this circuit component.

property adjoint: CircuitComponent

The adjoint of this component obtained by conjugating the ansatz and swapping the ket and bra wires.

>>> from mrmustard.lab import GaussianKet
>>> psi = GaussianKet.random([0])
>>> assert psi.dm() == psi.contract(psi.adjoint)

Note

The resulting CircuitComponent is not in standard order. To get the standard order, call to_standard_order() on the result.

property ansatz: Ansatz

The ansatz of this circuit component.

property ansatz_factory: AnsatzFactory

The ansatz factory of this component.

Raises:

AttributeError – If the component has no AnsatzFactory.

property dual: CircuitComponent

The dual of this component obtained by conjugating the ansatz and swapping the input and output wires.

>>> from mrmustard import math
>>> from mrmustard.lab import GaussianKet
>>> psi = GaussianKet.random([0])
>>> assert math.allclose(1.0, psi >> psi.dual)

Note

The resulting CircuitComponent is not in standard order. To get the standard order, call to_standard_order() on the result.

property manual_shape: tuple[int | None, ...]

The shape of this Component in the Fock representation in standard order. For each wire, the entry is either an integer or None. If it is an integer, it is the shape (dimension) of the corresponding Fock space. For a cutoff of n (maximum photon number n), the shape is n + 1 (includes \(\left\vert 0\right\rangle\) through \(\left\vert n\right\rangle\)). If it is None, it means the best shape is not known yet and will be determined by auto_shape.

property modes: tuple[int, ...]

The sorted tuple of modes of this component.

>>> from mrmustard.lab import GaussianKet
>>> ket = GaussianKet.random([0, 1])
>>> assert ket.modes == (0, 1)
property name: str

The name of this component.

>>> from mrmustard.lab import BtoPS
>>> assert BtoPS(modes=0, s=0).name == "BtoPS"
property n_modes: int

The number of modes spanned by this component across all wires.

>>> from mrmustard.lab import GaussianKet
>>> ket = GaussianKet.random([0, 1])
>>> assert ket.n_modes == 2
property parameters: ParameterDict

The parameters of this component.

property wires: Wires

The wires of this circuit component.

classmethod from_bargmann(triple, modes_out_bra=(), modes_in_bra=(), modes_out_ket=(), modes_in_ket=(), name=None)[source]

Initializes a CircuitComponent object from its Bargmann (A,b,c) parametrization.

>>> from mrmustard import math
>>> from mrmustard.lab import CircuitComponent, Identity
>>> from mrmustard.physics.ansatz import PolyExpAnsatz
>>> A = math.astensor([[0, 1], [1, 0]])
>>> b = math.astensor([0, 0])
>>> c = 1
>>> modes_out_bra = {}
>>> modes_in_bra = {}
>>> modes_out_ket = {0}
>>> modes_in_ket = {0}
>>> triple = (A, b, c)
>>> cc = CircuitComponent.from_bargmann(triple, modes_out_bra, modes_in_bra, modes_out_ket, modes_in_ket)
>>> assert isinstance(cc.ansatz, PolyExpAnsatz)
>>> assert cc == Identity(modes = 0)
Parameters:
  • triple (tuple) – The Bargmann representation of the component.

  • modes_out_bra (Sequence[int]) – The output modes on the bra side of this component.

  • modes_in_bra (Sequence[int]) – The input modes on the bra side of this component.

  • modes_out_ket (Sequence[int]) – The output modes on the ket side of this component.

  • modes_in_ket (Sequence[int]) – The input modes on the ket side of this component.

  • name (str | None) – The name of this component.

Returns:

A circuit component with the given Bargmann representation.

Return type:

CircuitComponent

classmethod from_fock(array, batch_dims, modes_out_bra=(), modes_in_bra=(), modes_out_ket=(), modes_in_ket=(), name=None)[source]

Initializes a CircuitComponent object from its Fock parametrization.

Parameters:
  • array (ComplexTensor) – The Fock array of the component.

  • batch_dims (int) – The number of batch dimensions in the given array.

  • modes_out_bra (Sequence[int]) – The output modes on the bra side of this component.

  • modes_in_bra (Sequence[int]) – The input modes on the bra side of this component.

  • modes_out_ket (Sequence[int]) – The output modes on the ket side of this component.

  • modes_in_ket (Sequence[int]) – The input modes on the ket side of this component.

  • name (str | None) – The name of this component.

Returns:

A CircuitComponent with the given Fock representation.

Raises:

ValueError – If the given array has a shape that is inconsistent with the number of modes.

Return type:

CircuitComponent

classmethod from_quadrature(modes_out_bra, modes_in_bra, modes_out_ket, modes_in_ket, triple, phi=0.0, name=None)[source]

Returns a circuit component from the given triple (A,b,c) that parametrizes the quadrature wavefunction of this component in the form \(c * exp(1/2 x^T A x + b^T x)\).

Parameters:
  • modes_out_bra (Sequence[int]) – The output modes on the bra side of this component.

  • modes_in_bra (Sequence[int]) – The input modes on the bra side of this component.

  • modes_out_ket (Sequence[int]) – The output modes on the ket side of this component.

  • modes_in_ket (Sequence[int]) – The input modes on the ket side of this component.

  • triple (tuple) – The (A,b,c) triple that parametrizes the wave function.

  • phi (float) – The quadrature angle. 0 corresponds to the x quadrature, pi/2 to the p quadrature.

  • name (str | None) – The name of this component.

Returns:

A circuit component with the given quadrature representation.

Return type:

CircuitComponent

auto_shape(**_)[source]

The shape of the Fock representation of this component. If the component has a Fock representation then it is just the shape of the array. If the component is a State in Bargmann then the shape is calculated using autoshape using single-mode marginals. If the component is not a State then the shape is a tuple of settings.DEFAULT_FOCK_SIZE values except where the manual_shape attribute has been set.

Return type:

tuple[int, …]

bargmann_triple()[source]

The Bargmann parametrization of this component, if available. It returns a triple (A, b, c) such that the Bargmann function of this component is \(F(z) = c \exp\left(\frac{1}{2} z^T A z + b^T z\right)\).

>>> from mrmustard.lab import CircuitComponent, Coherent
>>> coh = Coherent(mode=0, alpha=1.0)
>>> coh_cc = CircuitComponent.from_bargmann(coh.bargmann_triple(), modes_out_ket=(0,))
>>> assert isinstance(coh_cc, CircuitComponent)
>>> assert coh == coh_cc  # equality looks at representation and wires
Returns:

The Bargmann triple of this component.

Raises:

AttributeError – If the component has no Bargmann data.

Return type:

tuple[mrmustard.utils.typing.ComplexMatrix, mrmustard.utils.typing.ComplexVector, mrmustard.utils.typing.ComplexScalar]

contract(other)[source]

Contracts self and other without adding adjoints. Core index selection is determined solely by the wires; batch dimensions are broadcast automatically by the underlying ansatz implementations.

For example, a coherent state can be input to an attenuator, but the attenuator has two inputs: on the ket and the bra side. The >> operator would automatically add the adjoint of the coherent state on the bra side of the input of the attenuator, but contract instead does not.

>>> from mrmustard.lab import Coherent, Attenuator
>>> coh = Coherent(0, 1.0)
>>> att = Attenuator(0, 0.5)
>>> assert coh.contract(att).wires.input.bra  # the input bra is still uncontracted
Parameters:

other (CircuitComponent | Scalar) – The other component to contract with.

Returns:

The contracted component.

Return type:

CircuitComponent

concat(other, axis=0)[source]

Concatenates this component with another along the specified batch axis.

The ansatze are concatenated along the given batch axis. The wires must match. Returns an instance of the closest common superclass.

>>> from mrmustard.lab import Coherent
>>> state1 = Coherent(mode=0, alpha=1.0)
>>> state2 = Coherent(mode=0, alpha=2.0)
>>> # Add batch dimension first
>>> state1_batched = state1[None]
>>> state2_batched = state2[None]
>>> concatenated = state1_batched.concat(state2_batched, axis=0)
>>> assert concatenated.ansatz.batch_shape == (2,)
Parameters:
  • other (CircuitComponent) – The other circuit component to concatenate with.

  • axis (int) – The batch axis along which to concatenate.

Returns:

A new CircuitComponent with concatenated ansatze.

Raises:

ValueError – If wires don’t match or if ansatz types don’t match.

Return type:

CircuitComponent

fock_array(shape=None)[source]

Returns an array representation of this component in the Fock basis with the given shape. If the shape is not given, it defaults to the auto_shape of the component.

Parameters:

shape (int | Sequence[int] | None) – The shape of the returned representation. If shape is given as an int, it is broadcasted to all the dimensions. If not given, it is generated via auto_shape.

Returns:

The Fock representation of this component.

Return type:

array

Raises:

ValueError – If the shape is not valid for the component.

on(modes)[source]

Creates a light copy of this component that acts on the given modes instead of the original modes. It only works if the component’s wires are all defined on the same modes. As a light copy, the returned component shares the representation with the original one.

If a more general rewiring is needed, while maintaining a light copy to the original, use ._light_copy(new_wires) and pass the desired wires.

Parameters:

modes (int | Sequence[int]) – The new modes that this component acts on.

Returns:

The component acting on the specified modes.

Raises:

ValueError – If the component’s wires are not all defined on the same modes or if the length of the given modes is different from the length of the original modes.

Return type:

CircuitComponent

quadrature(*quad, phi=0.0)[source]

The (discretized) quadrature basis representation of the circuit component. This method considers the same basis in all the wires. For more fine-grained control, use the BtoQ transformation or a combination of transformations.

Parameters:
  • quad (RealVector) – discretized quadrature points to evaluate over in the quadrature representation. One vector of points per wire.

  • phi (float) – The quadrature angle. 0 corresponds to the x quadrature, pi/2 to the p quadrature.

Returns:

A circuit component with the given quadrature representation.

Return type:

ComplexTensor

quadrature_triple(phi=0.0)[source]

The quadrature representation triple A,b,c of this circuit component.

Parameters:

phi (float) – The quadrature angle. phi=0 corresponds to the x quadrature, phi=pi/2 to the p quadrature.

Returns:

A,b,c triple of the quadrature representation

Return type:

tuple[mrmustard.utils.typing.ComplexMatrix, mrmustard.utils.typing.ComplexVector, mrmustard.utils.typing.ComplexScalar]

stack(other, axis=0)[source]

Stacks this component with another along a new batch axis.

The two components must have the same wires and compatible ansatze. A new batch axis is inserted at the specified position.

>>> from mrmustard.lab import Coherent
>>> state1 = Coherent(mode=0, alpha=1.0)
>>> state2 = Coherent(mode=0, alpha=2.0)
>>> stacked = state1.stack(state2, axis=0)
>>> assert stacked.ansatz.batch_shape == (2,)
Parameters:
  • other (CircuitComponent) – The other circuit component to stack with.

  • axis (int) – The position where the new batch axis will be inserted.

Returns:

A new CircuitComponent with stacked ansatze.

Raises:

ValueError – If wires don’t match, ansatz types don’t match, or axis is out of range.

Return type:

CircuitComponent

to_bargmann()[source]

Returns a new CircuitComponent in the Bargmann representation.

>>> from mrmustard.lab import Dgate, Number
>>> from mrmustard.physics.ansatz import ArrayAnsatz, PolyExpAnsatz
>>> d = Dgate(1, alpha=0.1 + 0.1j)
>>> d_fock = d.to_fock(shape=3)
>>> d_bargmann = d_fock.to_bargmann()
>>> num = Number(0, n=2)
>>> assert isinstance(num.ansatz, ArrayAnsatz) # in Fock representation
>>> num_bargmann = num.to_bargmann()
>>> assert isinstance(num_bargmann.ansatz, PolyExpAnsatz) # in Bargmann representation
Returns:

A new CircuitComponent in the Bargmann representation.

Return type:

CircuitComponent

to_fock(shape=None)[source]

Returns a new CircuitComponent in the Fock representation.

>>> from mrmustard.lab import Dgate
>>> from mrmustard.physics.ansatz import ArrayAnsatz, PolyExpAnsatz
>>> d = Dgate(1, alpha=0.1 + 0.1j)
>>> d_fock = d.to_fock(shape=3)
>>> assert d_fock.name == d.name
>>> assert isinstance(d.ansatz, PolyExpAnsatz) # in Bargmann representation
>>> assert isinstance(d_fock.ansatz, ArrayAnsatz) # in Fock representation
Parameters:

shape (int | Sequence[int] | None) – The shape of the returned representation. If shape is given as an int, it is broadcasted to all dimensions. If None, it is generated via auto_shape.

Returns:

A new CircuitComponent in the Fock representation.

Return type:

CircuitComponent

to_quadrature(phi=0.0)[source]

Returns a circuit component with the quadrature representation of this component in terms of A,b,c.

Parameters:

phi (float) – The quadrature angle. 0 corresponds to the x quadrature, pi/2 to the p quadrature.

Returns:

A circuit component with the given quadrature representation.

Return type:

CircuitComponent

to_standard_order()[source]

Reorders the ansatz and wires to the standard order.

Returns:

A circuit component in the standard order.

Return type:

CircuitComponent

__rshift__(other)[source]

Contracts self and other (output of self going into input of other). It adds the adjoints when they are missing (e.g. if self is a Ket and other is a Channel). An error is raised if these cannot be deduced from the wires of the components. For example this allows Ket to be right-shifted into Channel and automatically the result is a DM. If the result has no wires left, it returns the (batched) scalar value of the representation. Note that a CircuitComponent is allowed to right-shift into scalars because the scalar part may result from an automated contraction subroutine that involves several components).

Note that the resulting component type is coerced based on the wires of the result:

  • Ket: only output ket wires remain

  • DM: only output bra and ket on the same modes remain

  • Unitary: only bra wires remain

  • Channel: input bra and ket on the same modes and output bra and ket on the same modes

  • CircuitComponent: otherwise

>>> from mrmustard.lab import Coherent, Attenuator, Ket, DM, Channel
>>> state = Coherent(0, 1.0)
>>> assert issubclass(Coherent, Ket)
>>> assert issubclass(Attenuator, Channel)
>>> assert isinstance(state >> Attenuator(0, 0.5), DM)
>>> assert math.allclose(state >> state.dual, 1+0j)
Parameters:

other (Scalar | CircuitComponent) – The other component or (batchable) scalar to contract with.

Returns:

The contracted component or (batched) scalar value of the representation.

Raises:

ValueError – If the component wires are incompatible with the other component.

Return type:

Scalar | CircuitComponent