Circuit

The basic usage and definition of Circuit can be found in the tutorial.

Accessing the components

Circuits can be iterated over to retrieve their components. Each component is a tuple (r, c) where r is a tuple of integers corresponding to the modes of the component, in ascending order, and c is the component instance itself.

>>> import perceval as pcvl
>>> circuit = pcvl.Circuit(3) // pcvl.BS() // (1, pcvl.PS(1)) // (1, pcvl.BS())
>>> for r, c in circuit:
>>>     print(r, c.name)
(0, 1) BS.Rx
(1,) PS
(1, 2) BS.Rx

Note

The iterator on a Circuit flattens the circuit structure, so only basic components will be returned when using a for loop on a circuit.

It is also possible to access directly a component from a circuit using row and column indices - note that a same component may have different column indices for the different rows it spans over:

>>> c = Circuit(2) // comp.BS.H() // comp.PS(P("phi1")) // comp.BS.Rx() // comp.PS(P("phi2"))
>>> print(c[1, 1].describe())
BS(convention=BSConvention.Rx)
>>> print(c[0, 2].describe())
BS(convention=BSConvention.Rx)

Circuit and parameters

For circuits or components using symbolic parameters (see Parameter), some convenient ways to access them exist. Note that two parameters in the same circuit can’t have the same name. If a parameter’s name appears in more than one component, it can only be the same Parameter instance.

Probably the most useful of them is assign(), as it allows setting the value for all variable parameters of a circuit at once, even without having to store them somewhere.

>>> p = pcvl.P("phi0")
>>> c = pcvl.PS(p)
>>> c.assign({"phi0": 2.53})
>>> print(float(p))
2.53

Note

The assign argument of compute_unitary() does exactly that if you want to compute the unitary with values. Beware however that this has the side-effect of changing the values of the parameters even outside compute_unitary(). You can remove the values for the variable parameters to get back sympy expressions using the circuit’s reset_parameters() method.

The names of the parameters can be obtained using the params() property. Note that this includes the fixed parameters if there are any.

To get the Parameter itself, there are three ways:

  • use the param("param name")() method to retrieve a single parameter from its name.

  • use the get_parameters() method that gives all the parameters defined by the arguments (variable or all, with or without expressions). This is the preferred method for getting all parameters.

    >>> c = BS(theta=pcvl.P("alpha1")) // PS(pcvl.P("phi")) // BS(theta=pcvl.P("alpha2"))
    >>> for params in c.get_parameters():
    >>>     print(param)
    Parameter(name='alpha1', value=None, min_v=0.0, max_v=12.566370614359172)
    Parameter(name='phi', value=None, min_v=0.0, max_v=6.283185307179586)
    Parameter(name='alpha2', value=None, min_v=0.0, max_v=12.566370614359172)
    
  • use the vars() property to get a dictionary mapping the name of the variable parameters and their instances.

class perceval.components.linear_circuit.Circuit(m, name=None)

Class to represent any circuit composed of one or multiple components

Parameters:
  • m (int) – The number of port of the circuit

  • name (Optional[str]) – Name of the circuit

property U

get the symbolic unitary matrix

__floordiv__(component)

Build a new circuit by adding component to the current circuit

>>> c = a // b   # equivalent to: `Circuit(n) // self // component`
Parameters:

component (ACircuit | tuple[int, ACircuit]) – the component to add, or a tuple (first_port, component)

Return type:

Circuit

__ifloordiv__(component)

Shortcut for .add

>>> c //= b       # equivalent to: `c.add((0:b.m),b)`
>>> c //= (i, b)  # equivalent to: `c.add((i:i+b.m), b)`
Parameters:

component (ACircuit | tuple[int, ACircuit]) – the component to add, or a tuple (first_port, component)

Return type:

Circuit

__imatmul__(component)

Add a barrier and a component to the current circuit

Parameters:

component (ACircuit | tuple[int, ACircuit]) – the component to add, or a tuple (first_port, component)

Return type:

Circuit

__iter__()

Iterator on a circuit, recursively returns modes and components in the order they were added to the circuit. Flattens the circuit if there are sub-circuits.

Returns:

generator of tuples (r, c) where r is the tuple containing the modes of the component in ascending order, and c is the component itself.

__matmul__(component)

Build a new circuit by adding a barrier and then component to the current circuit

Parameters:

component (ACircuit | tuple[int, ACircuit]) – the component to add, or a tuple (first_port, component)

Return type:

Circuit

add(port_range, component, merge=False)

Add a component in a circuit

Parameters:
  • port_range (int | tuple[int, ...]) – the port range as a tuple of consecutive ports, or the initial port where to add the component

  • component (ACircuit) – the component to add, must be a linear component or circuit

  • merge (bool) – when the component is a complex circuit, if True, flatten the added circuit. Otherwise, keep the nested structure (default False)

Return type:

Circuit

Returns:

the circuit itself, allowing to add multiple components in a same line

Raise:

AssertionError if parameters are not valid

assign(assign=None)

Assign values to parameters referenced in assign

Parameters:

assign (dict[str, float | int]) – A dictionary mapping parameter_name -> value. Set the value to the parameter whose name is parameter_name for each key of the dictionary.

Raises:

KeyError – If parameter_name is not an existing variable parameter name of the circuit.

barrier()

Add a barrier to a circuit

The barrier is a visual marker to break down a circuit into sections. Behind the scenes, it is implemented as a Barrier unitary operating on all modes.

At the moment, the barrier behaves exactly like a component with a unitary equal to identity.

compute_unitary(use_symbolic=False, assign=None, use_polarization=None)

Compute the unitary matrix corresponding to the current circuit

Parameters:
  • use_polarization (Optional[bool]) – ask for polarized circuit to double size unitary matrix

  • assign (Optional[dict]) – optional mapping between parameter names and their corresponding values

  • use_symbolic (bool) – if the matrix should use symbolic calculation

Return type:

Matrix

Returns:

the unitary matrix, will be a MatrixS if symbolic, or a ~`MatrixN` if not.

copy(subs=None)

Return a deep copy of the current circuit

static decomposition(U, component, phase_shifter_fn=None, shape=InterferometerShape.TRIANGLE, permutation=None, inverse_v=False, inverse_h=False, constraints=None, merge=True, precision=1e-06, max_try=10, allow_error=False, ignore_identity_block=True)

Decompose a given unitary matrix U into a circuit with a specified component type

Parameters:
  • U (MatrixN) – the matrix to decompose

  • component (ACircuit) – a circuit, to solve any decomposition must have up to 2 independent parameters

  • phase_shifter_fn (Callable[[int], ACircuit]) – a function generating a phase_shifter circuit. If None, residual phase will be ignored

  • shape (str | InterferometerShape) – shape of the decomposition (triangle is natively supported in Perceval)

  • permutation (type[ACircuit]) – if provided, type of permutation operator to avoid unnecessary operators

  • inverse_v (bool) – inverse the decomposition vertically

  • inverse_h (bool) – inverse the decomposition horizontally

  • constraints – constraints to apply on both parameters, it is a list of individual constraints. Each constraint should have the numbers of free parameters of the system.

  • merge (bool) – don’t use sub-circuits

  • precision (float) – for intermediate values - norm below precision are considered 0. If not - use global_params

  • max_try (int) – number of times to try the decomposition

  • allow_error (bool) – allow decomposition error - when the actual solution is not locally reachable

  • ignore_identity_block (bool) – If true, do not insert a component when it’s not needed (component is an identity) Otherwise, insert a component everytime (default True).

Returns:

a circuit

property defined: bool

check if all parameters of the circuit are fully defined

definition()

Gives mathematical definition of the circuit

Only defined for elementary circuits

Return type:

Matrix

depths()
Returns:

the depth of the circuit for each mode

describe()

Describe a circuit

Return type:

str

Returns:

a string describing the circuit that be re-used to define the circuit

find_subnodes(pos)

find the subnodes of a given component (Udef for pos==None)

Parameters:

pos (int) – the position of the current node

Return type:

list[int]

Returns:

get_parameters(all_params=False, expressions=False)

Return the parameters of the circuit

Parameters:

all_params (bool) – if False, only returns the variable parameters

Expressions:

if True, returns highest level Expressions and parameters only. If False, returns the raw parameters that make up the expressions only. Default False.

Return type:

list[Parameter]

Returns:

the list of parameters

getitem(idx, only_parameterized=False)

Direct access to components of the circuit :type idx: tuple[int, int] :param idx: index of the component as (row, col) :type only_parameterized: bool :param only_parameterized: if True, only count components with parameters :rtype: ACircuit :return: the component

identify(unitary_matrix, phases, precision=None, max_try=10, allow_error=False)

Identify an instance of the current circuit (should be parameterized) such as \(Q.C=U.P\) where \(Q\) and \(P\) are single-mode phase shifts (resp. \([q1, q2, ..., qn]\), and \([p1, p2, ...,pn]\)). This is solved through \(n^2\) equations: \(q_i * C_{i,j}(x,y, ...) = UP_{i,j} * p_j\)

Parameters:
  • unitary_matrix – the matrix to identify

  • phases – phase shift parameters

  • max_try – the resolution is using parameter search starting on a random points, it might fail, this parameter sets the maximal number of times to try

Return type:

None

inverse(v=False, h=False)

Inverts a circuit in place, depending on the values of h and v.

Parameters:
  • h – Stands for horizontal. If True, the circuit will be reversed such that its final unitary matrix is the inverse of the original one.

  • v – Stands for vertical. If True, the circuit will be reversed such that the mode 0 becomes the mode \(m-1\), the mode 1 becomes the mode \(m - 2\)

is_composite()
Returns:

True if the component is itself composed of subcomponents

match(pattern, pos=None, pattern_pos=0, browse=False, match=None, actual_pos=None, actual_pattern_pos=None, reverse=False)

match a sub-circuit at a given position

Parameters:
  • match (Match) – the partial match

  • browse (bool) – true if we want to search the pattern at any location in the current circuit, if true, pos should be None

  • pattern (ACircuit) – the pattern to search for

  • pos (int) – the start position in the current circuit

  • pattern_pos (int) – the start position in the pattern

  • actual_pos (int) – unused, parameter only used by parent class

  • actual_pattern_pos (int) – unused, parameter only used by parent class

  • reverse (bool) – true if we want to search the pattern from the end of the circuit to pos (or the 0 if browse)

Return type:

Match | None

Returns:

property name: str

Returns component name

ncomponents()
Returns:

number of actual components in the circuit

param(param_name)

Extract a Parameter object from its name :type param_name: str :param param_name: The name of the parameter :rtype: Parameter :return: A Parameter object

property params: Iterable[str]
Returns:

a list of all variable parameter names in the component

property requires_polarization: bool

Does the circuit require polarization?

Returns:

is True if the circuit has a polarization component

transfer_from(source, force=False)

Transfer parameters of a circuit to the current one

Parameters:
  • source (ACircuit) – the circuit to transfer the parameters from. The shape of the circuit to transfer from should be a subset of the current circuit.

  • force (bool) – force changing fixed parameter if necessary

property vars: dict[str, perceval.utils.parameter.Parameter]
Returns:

A dictionary mapping parameter names to parameters for all variable parameters of the circuit