merlin package
Merlin - Photonic Quantum Neural Networks for PyTorch
A comprehensive framework for integrating photonic quantum circuits into PyTorch neural networks with automatic differentiation support.
- class merlin.Ansatz(PhotonicBackend, input_size, output_size=None, output_mapping_strategy=OutputMappingStrategy.LINEAR, device=None, dtype=None)
Bases:
object
Complete configuration for a quantum neural network layer.
- class merlin.AnsatzFactory
Bases:
object
Factory for creating quantum layer ansatzes (complete configurations).
- static create(PhotonicBackend, input_size, output_size=None, output_mapping_strategy=OutputMappingStrategy.LINEAR, device=None, dtype=None)
Create a complete ansatz configuration.
- Return type:
- Args:
PhotonicBackend (PhotonicBackend): The backend configuration to use. input_size (int): Size of the input feature vector. output_size (int | None): Size of the output vector. If None, it is defined by the backend. output_mapping_strategy (OutputMappingStrategy): Strategy for mapping outputs. device (torch.device | None): Device to run computations on. dtype (torch.dtype | None): Data type for computations.
- Returns:
Ansatz: A complete ansatz configuration for the quantum layer.
- class merlin.AutoDiffProcess(sampling_method='multinomial')
Bases:
object
Handles automatic differentiation backend and sampling noise integration.
- autodiff_backend(needs_gradient, apply_sampling, shots)
Determine sampling configuration based on gradient requirements.
- Return type:
tuple
[bool
,int
]
- class merlin.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.CircuitGenerator
Bases:
object
Utility class for generating quantum photonic circuits.
- static generate_circuit(circuit_type, n_modes, n_features)
Generate a quantum circuit based on specified type.
- enum merlin.CircuitType(value)
Bases:
Enum
Quantum circuit topology types.
Valid values are as follows:
- PARALLEL_COLUMNS = <CircuitType.PARALLEL_COLUMNS: 'parallel_columns'>
- SERIES = <CircuitType.SERIES: 'series'>
- PARALLEL = <CircuitType.PARALLEL: 'parallel'>
- class merlin.FeatureEncoder(feature_count)
Bases:
object
Utility class for encoding classical features into quantum circuit parameters.
This class provides methods to encode normalized classical features into parameters that can be used to configure quantum circuits. Different encoding strategies are supported depending on the circuit type.
- encode(X_norm, circuit_type, n_modes, bandwidth_coeffs=None, total_shifters=None)
Encode normalized features into quantum circuit parameters.
- Return type:
Tensor
- Args:
X_norm: Normalized input features of shape (batch_size, num_features) circuit_type: Type of quantum circuit architecture n_modes: Number of modes in the quantum circuit bandwidth_coeffs: Optional bandwidth tuning coefficients for each feature dimension total_shifters: Optional total number of phase shifters (unused in current implementation)
- Returns:
Encoded parameters tensor of shape (batch_size, num_parameters)
- Raises:
ValueError: If circuit_type is not supported
- class merlin.LexGroupingMapper(input_size, output_size)
Bases:
Module
Maps probability distributions using lexicographical grouping.
This mapper groups consecutive elements of the probability distribution into equal-sized buckets and sums them to produce the output. If the input size is not evenly divisible by the output size, padding is applied.
- forward(probability_distribution)
Group probability distribution into equal-sized buckets.
- Return type:
Tensor
- Args:
probability_distribution: Input probability tensor of shape (batch_size, input_size) or (input_size,)
- Returns:
Grouped probability tensor of shape (batch_size, output_size) or (output_size,)
- class merlin.ModGroupingMapper(input_size, output_size)
Bases:
Module
Maps probability distributions using modulo-based grouping.
This mapper groups elements of the probability distribution based on their index modulo the output size. Elements with the same modulo value are summed together to produce the output.
- forward(probability_distribution)
Group probability distribution based on indices modulo output_size.
- Return type:
Tensor
- Args:
probability_distribution: Input probability tensor of shape (batch_size, input_size) or (input_size,)
- Returns:
Grouped probability tensor of shape (batch_size, output_size) or (output_size,)
- class merlin.OutputMapper
Bases:
object
Handles mapping quantum probability distributions to classical outputs.
This class provides factory methods for creating different types of output mappers that convert quantum probability distributions to classical neural network outputs.
- static create_mapping(strategy, input_size, output_size)
Create an output mapping based on the specified strategy.
- Args:
strategy: The output mapping strategy to use input_size: Size of the input probability distribution output_size: Desired size of the output tensor
- Returns:
A PyTorch module that maps input_size to output_size
- Raises:
ValueError: If strategy is unknown or sizes are incompatible for ‘none’ strategy
- enum merlin.OutputMappingStrategy(value)
Bases:
Enum
Strategy for mapping quantum probability distributions to classical outputs.
Valid values are as follows:
- LINEAR = <OutputMappingStrategy.LINEAR: 'linear'>
- GROUPING = <OutputMappingStrategy.GROUPING: 'grouping'>
- LEXGROUPING = <OutputMappingStrategy.LEXGROUPING: 'lexgrouping'>
- MODGROUPING = <OutputMappingStrategy.MODGROUPING: 'modgrouping'>
- NONE = <OutputMappingStrategy.NONE: 'none'>
- class merlin.PhotonicBackend(circuit_type, n_modes, n_photons, state_pattern=StatePattern.PERIODIC, use_bandwidth_tuning=False, reservoir_mode=False)
Bases:
object
Configuration container for quantum layer experiments.
- class merlin.QuantumLayer(input_size, output_size=None, ansatz=None, circuit=None, input_state=None, n_photons=None, trainable_parameters=(), input_parameters=(), output_mapping_strategy=OutputMappingStrategy.LINEAR, device=None, dtype=None, shots=0, sampling_method='multinomial', no_bunching=True, index_photons=None)
Bases:
Module
Enhanced Quantum Neural Network Layer with factory-based architecture.
This layer can be created either from: 1. An Ansatz object (from AnsatzFactory) - for auto-generated circuits 2. Direct parameters - for custom circuits (backward compatible)
- Args:
- index_photons (List[Tuple[int, int]], optional): List of tuples (min_mode, max_mode)
constraining where each photon can be placed. The first_integer is the lowest index layer a photon can take and the second_integer is the highest index. If None, photons can be placed in any mode from 0 to m-1.
- forward(*input_parameters, apply_sampling=None, shots=None)
Forward pass through the quantum layer.
- Return type:
Tensor
- get_index_photons_info()
Get information about index_photons constraints.
- Return type:
dict
- Returns:
dict: Information about photon placement constraints
- prepare_parameters(input_parameters)
Prepare parameter list for circuit evaluation.
- Return type:
list
[Tensor
]
- set_sampling_config(shots=None, method=None)
Update sampling configuration.
- classmethod simple(input_size, n_params=100, shots=0, reservoir_mode=False, output_size=None, output_mapping_strategy=OutputMappingStrategy.NONE, device=None, dtype=None, no_bunching=True)
Simplified interface for creating a QuantumLayer.
Uses SERIES circuit type with PERIODIC state pattern as defaults. Automatically calculates the number of modes based on n_params.
- Args:
input_size (int): Size of the input vector n_params (int): Total number of parameters desired (default: 100). Formula: n_params = 2 * n_modes^2 shots (int): Number of shots for sampling (default: 0) reservoir_mode (bool): Whether to use reservoir mode (default: False) output_size (int, optional): Output dimension. If None, uses distribution size output_mapping_strategy: How to map quantum output to classical output device: PyTorch device dtype: PyTorch dtype no_bunching: Whether to exclude states with multiple photons per mode
- Returns:
QuantumLayer: Configured quantum layer instance
- to(*args, **kwargs)
Move and/or cast the parameters and buffers.
This can be called as
- to(device=None, dtype=None, non_blocking=False)
- to(dtype, non_blocking=False)
- to(tensor, non_blocking=False)
- to(memory_format=torch.channels_last)
Its signature is similar to
torch.Tensor.to()
, but only accepts floating point or complexdtype
s. In addition, this method will only cast the floating point or complex parameters and buffers todtype
(if given). The integral parameters and buffers will be moveddevice
, if that is given, but with dtypes unchanged. Whennon_blocking
is set, it tries to convert/move asynchronously with respect to the host if possible, e.g., moving CPU Tensors with pinned memory to CUDA devices.See below for examples.
Note
This method modifies the module in-place.
- Args:
- device (
torch.device
): the desired device of the parameters and buffers in this module
- dtype (
torch.dtype
): the desired floating point or complex dtype of the parameters and buffers in this module
- tensor (torch.Tensor): Tensor whose dtype and device are the desired
dtype and device for all parameters and buffers in this module
- memory_format (
torch.memory_format
): the desired memory format for 4D parameters and buffers in this module (keyword only argument)
- device (
- Returns:
Module: self
Examples:
>>> # xdoctest: +IGNORE_WANT("non-deterministic") >>> linear = nn.Linear(2, 2) >>> linear.weight Parameter containing: tensor([[ 0.1913, -0.3420], [-0.5113, -0.2325]]) >>> linear.to(torch.double) Linear(in_features=2, out_features=2, bias=True) >>> linear.weight Parameter containing: tensor([[ 0.1913, -0.3420], [-0.5113, -0.2325]], dtype=torch.float64) >>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_CUDA1) >>> gpu1 = torch.device("cuda:1") >>> linear.to(gpu1, dtype=torch.half, non_blocking=True) Linear(in_features=2, out_features=2, bias=True) >>> linear.weight Parameter containing: tensor([[ 0.1914, -0.3420], [-0.5112, -0.2324]], dtype=torch.float16, device='cuda:1') >>> cpu = torch.device("cpu") >>> linear.to(cpu) Linear(in_features=2, out_features=2, bias=True) >>> linear.weight Parameter containing: tensor([[ 0.1914, -0.3420], [-0.5112, -0.2324]], dtype=torch.float16) >>> linear = nn.Linear(2, 2, bias=None).to(torch.cdouble) >>> linear.weight Parameter containing: tensor([[ 0.3741+0.j, 0.2382+0.j], [ 0.5593+0.j, -0.4443+0.j]], dtype=torch.complex128) >>> linear(torch.ones(3, 2, dtype=torch.cdouble)) tensor([[0.6122+0.j, 0.1150+0.j], [0.6122+0.j, 0.1150+0.j], [0.6122+0.j, 0.1150+0.j]], dtype=torch.complex128)
- class merlin.SamplingProcess(method='multinomial')
Bases:
object
Handles quantum measurement sampling with different methods.
This class provides functionality to simulate quantum measurement noise by applying different sampling strategies to probability distributions.
- pcvl_sampler(distribution, shots, method=None)
Apply sampling noise to a probability distribution.
- Return type:
Tensor
- Args:
distribution: Input probability distribution tensor method: Sampling method to use (‘multinomial’, ‘binomial’, or ‘gaussian’), defaults to the initialized method shots: Number of measurement shots to simulate
- Returns:
Noisy probability distribution after sampling
- Raises:
ValueError: If method is not one of the valid options
- class merlin.StateGenerator
Bases:
object
Utility class for generating photonic input states.
- static generate_state(n_modes, n_photons, state_pattern)
Generate an input state based on specified pattern.
- enum merlin.StatePattern(value)
Bases:
Enum
Input photon state patterns.
Valid values are as follows:
- DEFAULT = <StatePattern.DEFAULT: 'default'>
- SPACED = <StatePattern.SPACED: 'spaced'>
- SEQUENTIAL = <StatePattern.SEQUENTIAL: 'sequential'>
- PERIODIC = <StatePattern.PERIODIC: 'periodic'>
- merlin.build_slos_distribution_computegraph(m, n_photons, output_map_func=None, no_bunching=False, keep_keys=True, device=None, dtype=torch.float32, index_photons=None)
Build a computation graph for Strong Linear Optical Simulation (SLOS) algorithm that can be reused for multiple unitaries.
[existing docstring…]
- Return type:
Subpackages
- merlin.core package
AbstractComputationProcess
Ansatz
AnsatzFactory
CircuitGenerator
CircuitType
ComputationProcess
ComputationProcessFactory
PhotonicBackend
QuantumLayer
StateGenerator
StatePattern
- Submodules
- merlin.datasets package
DatasetMetadata
DatasetMetadata.characteristics
DatasetMetadata.citation
DatasetMetadata.creators
DatasetMetadata.description
DatasetMetadata.feature_relationships
DatasetMetadata.features
DatasetMetadata.from_dict()
DatasetMetadata.homepage
DatasetMetadata.license
DatasetMetadata.name
DatasetMetadata.normalization
DatasetMetadata.num_classes
DatasetMetadata.num_features
DatasetMetadata.num_instances
DatasetMetadata.subset
DatasetMetadata.task_type
DatasetMetadata.to_dict()
DatasetMetadata.year
- Submodules
- merlin.pcvl_pytorch package
- merlin.sampling package
- merlin.torch_utils package