merlin.algorithms package

Merlin algorithms package containing various quantum machine learning algorithms.

class merlin.algorithms.FeatureMap(circuit=None, input_size=None, *, builder=None, experiment=None, input_parameters, trainable_parameters=None, dtype=torch.float32, device=None, encoder=None)

Bases: object

Quantum Feature Map

FeatureMap embeds a datapoint within a quantum circuit and computes the associated unitary for quantum kernel methods.

Args:

circuit: Pre-compiled pcvl.Circuit to encode features. input_size: Dimension of incoming classical data (required). builder: Optional CircuitBuilder to compile into a circuit. experiment: Optional pcvl.Experiment providing both the circuit and detector configuration.

Exactly one of circuit, builder, or experiment must be supplied.

input_parameters: Parameter prefix(es) that host the classical data. dtype: Torch dtype used when constructing the unitary. device: Torch device on which unitaries are evaluated.

compute_unitary(x, *training_parameters)

Generate the circuit unitary after encoding x and applying trainables.

Return type:

Tensor

Args:

x: Single datapoint to embed; accepts scalars, numpy arrays, or tensors. *training_parameters: Optional overriding trainable tensors.

Returns:

Tensor: Complex unitary matrix representing the prepared circuit.

is_datapoint(x)

Determine if x describes one sample or a batch.

Return type:

bool

Args:

x: Candidate input data.

Returns:

bool: True when x corresponds to a single datapoint.

classmethod simple(input_size, n_modes, n_photons=None, *, dtype=torch.float32, device=None, angle_encoding_scale=1.0, trainable=True, trainable_prefix='phi')

Simple factory method to create a FeatureMap with minimal configuration.

Return type:

FeatureMap

Args:

input_size: Classical feature dimension. n_modes: Number of photonic modes used by the helper circuit. n_photons: Optional photon count (defaults to input_size). dtype: Target dtype for internal tensors. device: Optional torch device handle. angle_encoding_scale: Global scaling applied to angle encoding features. trainable: Whether to expose a trainable rotation layer. trainable_prefix: Prefix used for the generated trainable parameter names.

Returns:

FeatureMap: Configured feature-map instance.

class merlin.algorithms.FeedForwardBlock(experiment, *, input_state=None, trainable_parameters=None, input_parameters=None, computation_space=ComputationSpace.FOCK, measurement_strategy=MeasurementStrategy.PROBABILITIES, device=None, dtype=None)

Bases: Module

Feed-forward photonic block constructed directly from a Perceval experiment.

The block introspects the provided pcvl.Experiment, splits it into unitary / detector / FFCircuitProvider stages and turns each segment into one or more QuantumLayer instances. At run time the block executes every stage, branching on every partial measurement outcome and accumulating the classical probability for each branch.

Parameters

experiment:

Perceval experiment containing the full feed-forward definition. The current implementation requires noise-free experiments (NoiseModel() or None).

input_state:

Initial quantum state. May be provided as a Fock occupation list, pcvl.BasicState, pcvl.StateVector, or a tensor whose components represent amplitudes in the experiment Fock basis (the tensor is only required for amplitude-encoding inputs).

trainable_parameters:

Optional list of Perceval parameter prefixes that should remain learnable across all stages.

input_parameters:

Perceval parameter prefixes that receive classical inputs. They are consumed by the first stage only; once the first detection happens all branches switch to amplitude encoding and the classical tensor is ignored.

computation_space:

Currently restricted to FOCK.

measurement_strategy:

Controls how classical outputs are produced:

  • MeasurementStrategy.PROBABILITIES (default) returns a tensor of shape (batch_size, num_output_keys) whose columns match the fully specified Fock states stored in :pyattr:`output_keys`.

  • MeasurementStrategy.MODE_EXPECTATIONS collapses every branch into a single tensor of shape (batch_size, num_modes) that contains the per-mode photon expectations aggregated across all measurement keys. The :pyattr:`output_keys` attribute is retained for metadata while :pyattr:`output_state_sizes` reports num_modes for every key.

  • MeasurementStrategy.AMPLITUDES yields a list of tuples (measurement_key, branch_probability, remaining_photons, amplitudes) so callers can reason about the mixed state left by each branch.

describe()

Return a multi-line description of the feed-forward stages.

The summary lists, in order, the global modes that remain active at each step, the subset of measured modes, and the type of feed-forward configurator attached to the stage. It is primarily intended for debugging or for logging experiment structure.

Return type:

str

forward(x=None)

Execute the feed-forward experiment.

Return type:

Tensor | list[tuple[tuple[int, ...], Tensor, int, Tensor]]

Parameters

x:

Classical feature tensor. Only the first stage consumes classical inputs; subsequent stages operate purely in amplitude-encoding mode. When the experiment does not expose classical inputs this argument may be omitted (or None), in which case an empty tensor is automatically supplied.

Returns

torch.Tensor | list

PROBABILITIES returns a tensor of shape (batch_size, len(output_keys)) aligned with the fully specified Fock states in :pyattr:`output_keys`. MODE_EXPECTATIONS produces a tensor of shape (batch_size, total_modes) where the columns already encode the per-mode expectations aggregated across all measurement keys (:pyattr:`output_state_sizes` stores total_modes for every key). AMPLITUDES yields a list of tuples (measurement_key, branch_probability, remaining_photons, amplitudes) describing every branch of the resulting mixed state.

property output_keys: list[tuple[int, ...]]

Return the measurement keys associated with the most recent classical forward pass.

The list is populated after forward() completes. For the PROBABILITIES strategy the list lines up with the tensor columns. For MODE_EXPECTATIONS it is retained for reference even though the returned tensor already aggregates all measurement outcomes. Calling the property before running the block raises RuntimeError.

property output_state_sizes: dict[tuple[int, ...], int]

Return the number of remaining Fock states represented by each entry in output_keys.

Only available when measurement_strategy is PROBABILITIES or MODE_EXPECTATIONS. For PROBABILITIES the value is always 1 because each key now denotes a fully specified Fock state, while for MODE_EXPECTATIONS it equals the total number of modes contributing to the expectation vector.

class merlin.algorithms.FeedForwardBlockLegacy(input_size, n, m, depth=None, state_injection=False, conditional_modes=None, layers=None, circuit_type=None, device=None)

Bases: Module

Feed-forward quantum neural network for photonic computation.

This class models a conditional feed-forward architecture used in quantum photonic circuits. It connects multiple quantum layers in a branching tree structure — where each branch corresponds to a sequence of photon-detection outcomes on designated conditional modes.

Each node in this feedforward tree represents a QuantumLayer that acts on a quantum state conditioned on measurement results of previous layers.

The recursion continues until a specified depth, allowing the model to simulate complex conditional evolution of quantum systems.

Detector support:

The current feed-forward implementation expects amplitude access for every intermediate layer (MeasurementStrategy.AMPLITUDES) and therefore assumes ideal PNR detectors. Custom detector transforms or Perceval experiments with threshold / hybrid detectors are not yet supported inside this block.

— Args:

input_size (int):

Number of classical input features (used for hybrid quantum-classical computation).

n (int):

Number of photons in the system.

m (int):

Total number of photonic modes.

depth (int, optional):

Maximum depth of feed-forward recursion. Defaults to m - 1 if not specified.

state_injection (bool, optional):

If True, allows re-injecting quantum states at intermediate steps (useful for simulating sources or ancilla modes). Default = False.

conditional_modes (list[int], optional):

List of mode indices on which photon detection is performed. Determines the branching structure. Defaults to [0].

layers (list[QuantumLayer], optional):

Predefined list of quantum layers (if any). If not provided, layers are automatically generated.

circuit_type (str, optional):

Type of quantum circuit architecture used to build each layer. Acts as a “template selector” for circuit structure generation.

define_ff_layer(k, layers)

Replace quantum layers at a specific depth k.

Args:

k (int): Feed-forward layer depth index. layers (list[QuantumLayer]): List of replacement layers.

define_layers(circuit_type)

Define and instantiate all quantum layers for each measurement outcome path.

Each tuple (representing a branch of the feedforward tree) is mapped to a QuantumLayer object. Depending on whether the state injection mode is active, the number of modes/photons and the input size differ.

Args:

circuit_type (str): Template name or circuit architecture type.

Raises:

AssertionError: If total input size does not match after allocation.

forward(x)

Perform the full quantum-classical feedforward computation.

Args:

x (torch.Tensor): Classical input tensor of shape (batch_size, input_size).

Returns:
torch.Tensor: Final output tensor containing probabilities for each

terminal measurement configuration.

generate_possible_tuples()

Generate all possible conditional outcome tuples.

Each tuple represents one possible sequence of photon detection results across all conditional modes up to a given depth. For example, with n_cond = 2 and depth = 3, tuples correspond to binary sequences of length depth * n_cond.

Returns:
list[tuple[int]]:

List of tuples containing binary measurement outcomes (0/1).

get_output_size()

Compute the number of output channels (post-measurement outcomes).

input_size_ff_layer(k)

Return the list of input sizes for all layers at depth k.

iterate_feedforward(current_tuple, remaining_amplitudes, keys, accumulated_prob, intermediary, outputs, depth=0, x=None)

Recursive feedforward traversal of the quantum circuit tree.

At each step:
  1. Evaluate photon detection outcomes (0/1) on conditional modes.

  2. For each possible combination, compute probabilities.

  3. Apply the corresponding quantum layer and recurse deeper.

Args:

current_tuple (tuple[int]): Current measurement sequence path. remaining_amplitudes (torch.Tensor): Quantum amplitudes of current state. keys (list[tuple[int]]): Fock basis keys for amplitudes. accumulated_prob (torch.Tensor or float): Product of probabilities so far. intermediary (dict): Stores intermediate probabilities. outputs (dict): Stores final output probabilities for all branches. depth (int): Current recursion depth. x (torch.Tensor, optional): Classical input features.

property output_keys

Return cached output keys, or compute them via a dummy forward pass.

parameters()

Iterate over all trainable parameters from every quantum layer.

size_ff_layer(k)

Return number of feed-forward branches at layer depth k.

to(device)

Moves the FeedForwardBlock and all its QuantumLayers to the specified device.

Args:

device (str or torch.device): Target device (‘cpu’, ‘cuda’, ‘mps’, etc.)

class merlin.algorithms.FidelityKernel(feature_map, input_state, *, shots=None, sampling_method='multinomial', no_bunching=False, force_psd=True, device=None, dtype=None)

Bases: Module

Fidelity Quantum Kernel

For a given input Fock state, \(|s \rangle\) and feature map, \(U\), the fidelity quantum kernel estimates the following inner product using SLOS: .. math:

|\langle s | U^{\dagger}(x_2) U(x_1) | s \rangle|^{2}

Transition probabilities are computed in parallel for each pair of datapoints in the input datasets.

Parameters:
  • feature_map (FeatureMap) – Feature map object that encodes a given datapoint within its circuit

  • input_state (list[int]) – Input state into circuit.

  • shots (Optional[int]) – Number of circuit shots. If None, the exact transition probabilities are returned. Default: None.

  • sampling_method (str) – Probability distributions are post- processed with some pseudo-sampling method: ‘multinomial’, ‘binomial’ or ‘gaussian’.

  • no_bunching (bool) – Whether or not to post-select out results with bunching. Default: False.

  • force_psd (bool) – Projects training kernel matrix to closest positive semi-definite. Default: True.

  • device (Optional[device]) – Device on which to perform SLOS

  • dtype (UnionType[str, dtype, None]) – Datatype with which to perform SLOS

Examples

For a given training and test datasets, one can construct the training and test kernel matrices in the following structure: .. code-block:: python

>>> circuit = Circuit(2) // PS(P("X0") // BS() // PS(P("X1") // BS()
>>> feature_map = FeatureMap(circuit, ["X"])
>>>
>>> quantum_kernel = FidelityKernel(
>>>     feature_map,
>>>     input_state=[0, 4],
>>>     no_bunching=False,
>>> )
>>> # Construct the training & test kernel matrices
>>> K_train = quantum_kernel(X_train)
>>> K_test = quantum_kernel(X_test, X_train)

Use with scikit-learn for kernel-based machine learning:. .. code-block:: python

>>> from sklearn import SVC
>>> # For a support vector classification problem
>>> svc = SVC(kernel='precomputed')
>>> svc.fit(K_train, y_train)
>>> y_pred = svc.predict(K_test)
forward(x1, x2=None)

Calculate the quantum kernel for input data x1 and x2. If x1 and x2 are datapoints, a scalar value is returned. For input datasets the kernel matrix is computed.

classmethod simple(input_size, n_modes, n_photons=None, input_state=None, *, shots=0, sampling_method='multinomial', no_bunching=False, force_psd=True, trainable=True, dtype=torch.float32, device=None, angle_encoding_scale=1.0)

Simple factory method to create a FidelityKernel with minimal configuration.

Return type:

FidelityKernel

class merlin.algorithms.NKernelAlignment

Bases: _Loss

Negative kernel-target alignment loss function for quantum kernel training.

Within quantum kernel alignment, the goal is to maximize the alignment between the quantum kernel matrix and the ideal target matrix given by \(K^{*} = y y^T\), where \(y \in \{-1, +1\}\) are the target labels.

The negative kernel alignment loss is given as:

\[\text{NKA}(K, K^{*}) = -\frac{\operatorname{Tr}(K K^{*})}{ \sqrt{\operatorname{Tr}(K^2)\operatorname{Tr}(K^{*2})}}\]
forward(input, target)

Define the computation performed at every call.

Should be overridden by all subclasses. :rtype: Tensor

Note

Although the recipe for forward pass needs to be defined within this function, one should call the Module instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.

class merlin.algorithms.PoolingFeedForwardLegacy(n_modes, n_photons, n_output_modes, pooling_modes=None, no_bunching=True)

Bases: Module

A quantum-inspired pooling module that aggregates amplitude information from an input quantum state representation into a lower-dimensional output space.

This module computes mappings between input and output Fock states (defined by keys_in and keys_out) based on a specified pooling scheme. It then aggregates the amplitudes according to these mappings, normalizing the result to preserve probabilistic consistency.

Parameters

n_modesint

Number of input modes in the quantum circuit.

n_photonsint

Number of photons used in the quantum simulation.

n_output_modesint

Number of output modes after pooling.

pooling_modeslist of list of int, optional

Specifies how input modes are grouped (pooled) into output modes. Each sublist contains the indices of input modes to pool together for one output mode. If None, an even pooling scheme is automatically generated.

no_bunchingbool, default=True

Whether to restrict to Fock states without photon bunching (i.e., no two photons occupy the same mode).

Attributes

match_indicestorch.Tensor

Tensor containing the indices mapping input states to output states.

exclude_indicestorch.Tensor

Tensor containing indices of input states that have no valid mapping to an output state.

keys_outlist

List of output Fock state keys (from Perceval simulation graph).

n_modesint

Number of input modes.

forward(amplitudes)

Forward pass that pools input quantum amplitudes into output modes.

Return type:

Tensor

Parameters

amplitudestorch.Tensor

Input tensor of shape (batch_size, n_input_states) containing the complex amplitudes (or real/imag parts) of quantum states.

Returns

torch.Tensor

Normalized pooled amplitudes of shape (batch_size, n_output_states).

match_tuples(keys_in, keys_out, pooling_modes)

Matches input and output Fock state tuples based on pooling configuration.

For each input Fock state (key_in), the corresponding pooled output state (key_out) is computed by summing the photon counts over each pooling group. Input states that do not correspond to a valid output state are marked for exclusion.

Parameters

keys_inlist

List of Fock state tuples representing input configurations.

keys_outlist

List of Fock state tuples representing output configurations.

pooling_modeslist of list of int

Grouping of input modes into output modes.

Returns

tuple[list[int], list[int]]

A pair (indices, exclude_indices) where: - indices are the matched indices from input to output keys. - exclude_indices are input indices with no valid match.

class merlin.algorithms.QuantumLayer(input_size=None, builder=None, circuit=None, experiment=None, input_state=None, n_photons=None, trainable_parameters=None, input_parameters=None, amplitude_encoding=False, computation_space=None, measurement_strategy=MeasurementStrategy.PROBABILITIES, device=None, dtype=None, **kwargs)

Bases: Module

Enhanced Quantum Neural Network Layer with factory-based architecture.

This layer can be created either from a CircuitBuilder instance or a pre-compiled pcvl.Circuit.

Merlin integration (optimal design):
  • merlin_leaf = True marks this module as an indivisible execution leaf.

  • force_simulation (bool) defaults to False. When True, the layer MUST run locally.

  • supports_offload() reports whether remote offload is possible (via export_config()).

  • should_offload(processor, shots) encapsulates the current offload policy:

    return supports_offload() and not force_local

angle_encoding_specs: dict[str, dict[str, Any]]
as_simulation()

Temporarily force local simulation within the context.

experiment: pcvl.Experiment | None
export_config()

Export a standalone configuration for remote execution.

Return type:

dict

property force_local: bool

When True, this layer must run locally (Merlin will not offload it).

forward(*input_parameters, shots=None, sampling_method=None, simultaneous_processes=None)

Forward pass through the quantum layer.

When self.amplitude_encoding is True the first positional argument must contain the amplitude-encoded input state (either [num_states] or [batch_size, num_states]). Remaining positional arguments are treated as classical inputs and processed via the standard encoding pipeline.

Return type:

tuple[Tensor, Tensor] | Tensor

Sampling is controlled by:
  • shots (int): number of samples; if 0 or None, return exact amplitudes/probabilities.

  • sampling_method (str): e.g. “multinomial”.

get_experiment()
Return type:

Optional[Experiment]

property has_custom_detectors: bool
input_parameters: list[str]
merlin_leaf: bool = True
noise_model: Any | None
property output_keys

Return the Fock basis associated with the layer outputs.

property output_size: int
prepare_parameters(input_parameters)

Prepare parameter list for circuit evaluation.

Return type:

list[Tensor]

set_force_simulation(value)
Return type:

None

set_input_state(input_state)
set_sampling_config(shots=None, method=None)

Deprecated: sampling configuration must be provided at call time in forward.

should_offload(_processor=None, _shots=None)

Return True if this layer should be offloaded under current policy.

Return type:

bool

classmethod simple(input_size, n_params=90, output_size=None, device=None, dtype=None, no_bunching=True, **kwargs)

Create a ready-to-train layer with a 10-mode, 5-photon architecture.

The circuit is assembled via CircuitBuilder with the following layout:

  1. A fully trainable entangling layer acting on all modes;

  2. A full input encoding layer spanning all encoded features;

  3. A non-trainable entangling layer that redistributes encoded information;

  4. Optional trainable Mach-Zehnder blocks (two parameters each) to reach the requested n_params budget;

  5. A final entangling layer prior to measurement.

Args:

input_size: Size of the classical input vector. n_params: Number of trainable parameters to allocate across the additional MZI blocks. Values

below the default entangling budget trigger a warning; values above it must differ by an even amount because each added MZI exposes two parameters.

output_size: Optional classical output width. device: Optional target device for tensors. dtype: Optional tensor dtype. no_bunching: Whether to restrict to states without photon bunching.

Returns:

QuantumLayer configured with the described architecture.

supports_offload()

Return True if this layer is technically offloadable.

Return type:

bool

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 complex dtypes. In addition, this method will only cast the floating point or complex parameters and buffers to dtype (if given). The integral parameters and buffers will be moved device, if that is given, but with dtypes unchanged. When non_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)

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)
trainable_parameters: list[str]
training: bool
merlin.algorithms.create_circuit(M, input_size)

Create a quantum photonic circuit with beam splitters and phase shifters.

Args:

M (int): Number of modes in the circuit.

Returns:

pcvl.Circuit: A quantum photonic circuit with alternating beam splitter layers and phase shifters.

merlin.algorithms.define_layer_no_input(n_modes, n_photons, circuit_type=None)

Define a quantum layer for feed-forward processing.

Args:

n_modes (int): Number of optical modes. n_photons (int): Number of photons in the layer.

Returns:

QuantumLayer: A configured quantum layer with trainable parameters.

merlin.algorithms.define_layer_with_input(M, N, input_size, circuit_type=None)

Define the first layers of the feed-forward block, those with an input size > 0.

Args:

M (int): Number of modes in the circuit. N (int): Number of photons.

Returns:

QuantumLayer: The first quantum layer with input parameters.

Submodules