merlin.pcvl_pytorch.locirc_to_tensor module
- class merlin.pcvl_pytorch.locirc_to_tensor.AComponent(m, name=None)
Bases:
ABC
- DEFAULT_NAME = None
- is_composite()
Returns True if the component is itself composed of subcomponents
- Return type:
bool
- property m: int
- property name: str
Returns component name
- class merlin.pcvl_pytorch.locirc_to_tensor.BS(theta=pi / 2, phi_tl=0, phi_bl=0, phi_tr=0, phi_br=0, convention=BSConvention.Rx)
Bases:
ACircuit
Beam splitter
- DEFAULT_NAME = 'BS'
- static H(theta=pi / 2, phi_tl=0, phi_bl=0, phi_tr=0, phi_br=0)
- static Rx(theta=pi / 2, phi_tl=0, phi_bl=0, phi_tr=0, phi_br=0)
- static Ry(theta=pi / 2, phi_tl=0, phi_bl=0, phi_tr=0, phi_br=0)
- property convention
- definition()
- describe()
- get_variables()
- inverse(v=False, h=False)
- property name
Returns component name
- static r_to_theta(r)
Compute theta given a reflectivity value
- Parameters:
r (
float
|Parameter
) – reflectivity value (can be variable)- Return type:
float
|Parameter
- property reflectivity
- static theta_to_r(theta)
Compute reflectivity given a theta value
- Parameters:
theta (
float
|Parameter
) – theta angle (can be variable)- Return type:
float
|Parameter
- enum merlin.pcvl_pytorch.locirc_to_tensor.BSConvention(value)
Bases:
Enum
Valid values are as follows:
- Rx = <BSConvention.Rx: 0>
- Ry = <BSConvention.Ry: 1>
- H = <BSConvention.H: 2>
- class merlin.pcvl_pytorch.locirc_to_tensor.Barrier(m, visible=True)
Bases:
ACircuit
Behaves like an identity unitary, visually represented as a barrier.
- DEFAULT_NAME = 'I'
- apply(r, sv)
- definition()
- describe()
- inverse(v=False, h=False)
- property visible
- class merlin.pcvl_pytorch.locirc_to_tensor.Circuit(m, name=None)
Bases:
ACircuit
Class to represent any circuit composed of one or multiple components
- Parameters:
m (
int
) – The number of port of the circuitname (
str
) – Name of the circuit
- DEFAULT_NAME = 'CPLX'
- 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 componentcomponent (
ACircuit
) – the component to add, must be a linear component or circuitmerge (
bool
) – when the component is a complex circuit, if True, flatten the added circuit. Otherwise, keep the nested structure (default False)
- Return type:
- Returns:
the circuit itself, allowing to add multiple components in a same line
- Raise:
AssertionError
if parameters are not valid
- 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 circuit
- Parameters:
use_symbolic (
bool
) – True to compute a symbolic matrix, False to compute a numerical matrix (default False)assign (
dict
) – optional mapping between parameter names and their corresponding valuesuse_polarization (
bool
) – ask for polarized circuit to double size unitary matrix
- Return type:
Matrix
- Returns:
The circuit unitary matrix
- 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 decomposecomponent (
ACircuit
) – a circuit, to solve any decomposition must have up to 2 independent parametersphase_shifter_fn (
Callable
[[int
],ACircuit
]) – a function generating a phase_shifter circuit. If None, residual phase will be ignoredshape (
str
|InterferometerShape
) – shape of the decomposition (triangle is natively supported in Perceval)permutation (
type
[ACircuit
]) – if provided, type of permutation operator to avoid unnecessary operatorsinverse_v (
bool
) – inverse the decomposition verticallyinverse_h (
bool
) – inverse the decomposition horizontallyconstraints – 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-circuitsprecision (
float
) – for intermediate values - norm below precision are considered 0. If not - use global_paramsmax_try (
int
) – number of times to try the decompositionallow_error (
bool
) – allow decomposition error - when the actual solution is not locally reachableignore_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
- definition()
Gives mathematical definition of the circuit
Only defined for elementary circuits
- Return type:
Matrix
- depths()
Return 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:
- 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
- inverse(v=False, h=False)
- is_composite()
Returns True if the component is itself composed of subcomponents
- isolate(lc, name=None, color=None)
- 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 matchbrowse (
bool
) – true if we want to search the pattern at any location in the current circuit, if true, pos should be Nonepattern (
ACircuit
) – the pattern to search forpos (
int
) – the start position in the current circuitpattern_pos (
int
) – the start position in the patternactual_pos (
int
) – unused, parameter only used by parent classactual_pattern_pos (
int
) – unused, parameter only used by parent classreverse (
bool
) – true if we want to search the pattern from the end of the circuit to pos (or the 0 if browse)
- Return type:
Optional
[Match
]- Returns:
- ncomponents()
Return number of actual components in the circuit
- replace(p, pattern, merge=False)
- 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
- class merlin.pcvl_pytorch.locirc_to_tensor.CircuitConverter(circuit, input_specs=None, dtype=torch.complex64, device=device(type='cpu'))
Bases:
object
Convert a parameterized Perceval circuit into a differentiable PyTorch unitary matrix.
This class converts Perceval quantum circuits into PyTorch tensors that can be used in neural network training with automatic differentiation. It supports batch processing for efficient training and handles various quantum components like beam splitters, phase shifters, and unitary operations.
- Supported Components:
PS (Phase Shifter)
BS (Beam Splitter)
PERM (Permutation)
Unitary (Generic unitary matrix)
Barrier (no-op, removed during compilation)
- Attributes:
circuit: The Perceval circuit to convert param_mapping: Maps parameter names to tensor indices device: PyTorch device for tensor operations tensor_cdtype: Complex tensor dtype tensor_fdtype: Float tensor dtype
- Example:
Basic usage with a single phase shifter:
>>> import torch >>> import perceval as pcvl >>> from merlin.pcvl_pytorch.locirc_to_tensor import CircuitConverter >>> >>> # Create a simple circuit with one phase shifter >>> circuit = pcvl.Circuit(1) // pcvl.PS(pcvl.P("phi")) >>> >>> # Convert to PyTorch with gradient tracking >>> converter = CircuitConverter(circuit, input_specs=["phi"]) >>> phi_params = torch.tensor([0.5], requires_grad=True) >>> unitary = converter.to_tensor(phi_params) >>> print(unitary.shape) # torch.Size([1, 1])
Multiple parameters with grouping:
>>> # Circuit with multiple phase shifters >>> circuit = (pcvl.Circuit(2) ... // pcvl.PS(pcvl.P("theta1")) ... // (1, pcvl.PS(pcvl.P("theta2")))) >>> >>> converter = CircuitConverter(circuit, input_specs=["theta"]) >>> theta_params = torch.tensor([0.1, 0.2], requires_grad=True) >>> unitary = converter.to_tensor(theta_params) >>> print(unitary.shape) # torch.Size([2, 2])
Batch processing for training:
>>> # Batch of parameter values >>> batch_params = torch.tensor([[0.1], [0.2], [0.3]], requires_grad=True) >>> converter = CircuitConverter(circuit, input_specs=["phi"]) >>> batch_unitary = converter.to_tensor(batch_params) >>> print(batch_unitary.shape) # torch.Size([3, 1, 1])
Training integration:
>>> # Training loop with beam splitter >>> circuit = pcvl.Circuit(2) // pcvl.BS.Rx(pcvl.P("theta")) >>> converter = CircuitConverter(circuit, ["theta"]) >>> theta = torch.tensor([0.5], requires_grad=True) >>> optimizer = torch.optim.Adam([theta], lr=0.01) >>> >>> for step in range(10): ... optimizer.zero_grad() ... unitary = converter.to_tensor(theta) ... loss = some_loss_function(unitary) ... loss.backward() ... optimizer.step()
- set_dtype(dtype)
Set the tensor data types for float and complex operations.
- Args:
dtype: Target dtype (float32/complex64 or float64/complex128)
- Raises:
TypeError: If dtype is not supported
- to(dtype, device)
Move the converter to a specific device and dtype.
- Args:
dtype: Target tensor dtype (float32/complex64 or float64/complex128) device: Target device (string or torch.device)
- Returns:
Self for method chaining
- Raises:
TypeError: If device type or dtype is not supported
- to_tensor(*input_params, batch_size=None)
Convert the parameterized circuit to a PyTorch unitary tensor.
- Return type:
Tensor
- Args:
*input_params: Variable number of parameter tensors. Each tensor has shape (num_params,) or (batch_size, num_params) corresponding to input_specs order. batch_size: Explicit batch size. If None, inferred from input tensors.
- Returns:
- Complex unitary tensor of shape (circuit.m, circuit.m) for single samples
or (batch_size, circuit.m, circuit.m) for batched inputs.
- Raises:
ValueError: If wrong number of input tensors provided. TypeError: If input_params is not a list or tuple.
- class merlin.pcvl_pytorch.locirc_to_tensor.PERM(perm)
Bases:
Unitary
Permutation
- DEFAULT_NAME = 'PERM'
- apply(r, sv)
- break_in_2_mode_perms()
Breaks any n-mode PERM into an equivalent circuit with only 2 mode PERMs
- Returns:
An equivalent Circuit with only 2 mode PERM components
- definition()
- describe()
- property perm_vector
- class merlin.pcvl_pytorch.locirc_to_tensor.PS(phi, max_error=0)
Bases:
ACircuit
Phase shifter
- DEFAULT_NAME = 'PS'
- describe()
- get_variables()
- inverse(v=False, h=False)
- merlin.pcvl_pytorch.locirc_to_tensor.SUPPORTED_COMPONENTS = (<class 'perceval.components.unitary_components.PS'>, <class 'perceval.components.unitary_components.BS'>, <class 'perceval.components.unitary_components.PERM'>, <class 'perceval.components.unitary_components.Unitary'>, <class 'perceval.components.unitary_components.Barrier'>)
Tuple of quantum components supported by CircuitConverter.
- Components:
PS: Phase shifter with single phi parameter BS: Beam splitter with theta and four phi parameters PERM: Mode permutation (no parameters) Unitary: Generic unitary matrix (no parameters) Barrier: Synchronization barrier (removed during compilation)
- class merlin.pcvl_pytorch.locirc_to_tensor.Unitary(U, name=None, use_polarization=False)
Bases:
ACircuit
Generic component defined by a unitary matrix
- DEFAULT_NAME = 'Unitary'
- describe()
- inverse(v=False, h=False)
- merlin.pcvl_pytorch.locirc_to_tensor.dispatch(*types, **kwargs)
Dispatch function on the types of the inputs
Supports dispatch on all non-keyword arguments.
Collects implementations based on the function name. Ignores namespaces.
If ambiguous type signatures occur a warning is raised when the function is defined suggesting the additional method to break the ambiguity.
Examples
>>> @dispatch(int) ... def f(x): ... return x + 1
>>> @dispatch(float) ... def f(x): ... return x - 1
>>> f(3) 4 >>> f(3.0) 2.0
Specify an isolated namespace with the namespace keyword argument
>>> my_namespace = dict() >>> @dispatch(int, namespace=my_namespace) ... def foo(x): ... return x + 1
Dispatch on instance methods within classes
>>> class MyClass(object): ... @dispatch(list) ... def __init__(self, data): ... self.data = data ... @dispatch(int) ... def __init__(self, datum): ... self.data = [datum]