merlin.algorithms.layer module

Main QuantumLayer implementation

class merlin.algorithms.layer.Any(*args, **kwargs)

Bases: object

Special type indicating an unconstrained type.

  • Any is compatible with every type.

  • Any assumed to have all methods.

  • All values assumed to be instances of Any.

Note that all the above statements are true from the point of view of static type checkers. At runtime, Any should not be used with instance checks.

class merlin.algorithms.layer.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.algorithms.layer.CircuitBuilder(n_modes)

Bases: object

Builder for quantum circuits using a declarative API.

add_angle_encoding(modes=None, name=None, *, scale=1.0, subset_combinations=False, max_order=None)

Convenience method for angle-based input encoding.

Return type:

CircuitBuilder

Args:

modes: Optional list of circuit modes to target. Defaults to all modes. name: Prefix used for generated input parameters. Defaults to "px". scale: Global scaling factor applied before angle mapping. subset_combinations: When True, generate higher-order feature

combinations (up to max_order) matching the historical subset encoding utility.

max_order: Optional cap on the size of feature combinations when

subset_combinations is enabled. None uses all orders.

Returns:

CircuitBuilder: self for fluent chaining.

add_entangling_layer(modes=None, *, trainable=True, model='mzi', name=None, trainable_inner=None, trainable_outer=None)

Add an entangling layer spanning a range of modes.

Return type:

CircuitBuilder

Args:
modes: Optional list describing the span. None targets all modes;

one element targets modes[0] through the final mode; two elements target the inclusive range [modes[0], modes[1]].

trainable: Whether internal phase shifters should be trainable. model: "mzi" or "bell" to select the internal interferometer template. name: Optional prefix used for generated parameter names. trainable_inner: Override for the internal (between-beam splitter) phase shifters. trainable_outer: Override for the output phase shifters at the exit of the interferometer.

Raises:

ValueError: If the provided modes are invalid or span fewer than two modes.

Returns:

CircuitBuilder: self for fluent chaining.

add_rotations(modes=None, *, axis='z', trainable=False, as_input=False, angle=None, value=None, name=None, role=None)

Add one or multiple rotations across the provided modes.

Return type:

CircuitBuilder

Args:

modes: Single mode, list of modes, module group or None (all modes). axis: Axis of rotation for each inserted phase shifter. trainable: Promote the rotations to trainable parameters (legacy flag). as_input: Mark the rotations as input-driven parameters (legacy flag). angle: Optional fixed value for the rotations (alias of value). value: Optional fixed value for the rotations (alias of angle). name: Optional stem used for generated parameter names. role: Explicit ParameterRole taking precedence over other flags.

Returns:

CircuitBuilder: self for fluent chaining.

add_superpositions(targets=None, *, depth=1, theta=0.785398, phi=0.0, trainable=None, trainable_theta=None, trainable_phi=None, modes=None, name=None)

Add one or more superposition (beam splitter) components.

Return type:

CircuitBuilder

Args:
targets: Tuple or list of tuples describing explicit mode pairs. When

omitted, nearest neighbours over modes (or all modes) are used.

depth: Number of sequential passes to apply (>=1). theta: Baseline mixing angle for fixed beam splitters. phi: Baseline relative phase for fixed beam splitters. trainable: Convenience flag to mark both theta and phi trainable. trainable_theta: Whether the mixing angle should be trainable. trainable_phi: Whether the relative phase should be trainable. modes: Optional mode list/module group used when targets is omitted. name: Optional stem used for generated parameter names.

Returns:

CircuitBuilder: self for fluent chaining.

property angle_encoding_specs: dict[str, dict[str, Any]]

Return metadata describing configured angle encodings.

Returns:

Dict[str, Dict[str, Any]]: Mapping from encoding prefix to combination metadata.

build()

Build and return the circuit.

Return type:

Circuit

Returns:

Circuit: Circuit instance populated with components.

classmethod from_circuit(circuit)

Create a builder from an existing circuit.

Return type:

CircuitBuilder

Args:

circuit: Circuit object whose components should seed the builder.

Returns:

CircuitBuilder: A new builder instance wrapping the provided circuit.

property input_parameter_prefixes: list[str]

Expose the order-preserving set of input prefixes.

Returns:

List[str]: Input parameter stems emitted during encoding.

to_pcvl_circuit(pcvl_module=None)

Convert the constructed circuit into a Perceval circuit.

Args:

pcvl_module: Optional Perceval module. If None, attempts to import perceval.

Returns:

A pcvl.Circuit instance mirroring the components tracked by this builder.

Raises:

ImportError: If perceval is not installed and no module is provided.

property trainable_parameter_prefixes: list[str]

Expose the unique set of trainable prefixes in insertion order.

Returns:

List[str]: Trainable parameter stems discovered so far.

class merlin.algorithms.layer.ComputationProcessFactory

Bases: object

Factory for creating computation processes.

static create(circuit, input_state, trainable_parameters, input_parameters, computation_space=None, **kwargs)

Create a computation process.

Return type:

ComputationProcess

enum merlin.algorithms.layer.ComputationSpace(value)

Bases: str, Enum

Enumeration of supported computational subspaces.

Member Type:

str

Valid values are as follows:

FOCK = <ComputationSpace.FOCK: 'fock'>
UNBUNCHED = <ComputationSpace.UNBUNCHED: 'unbunched'>
DUAL_RAIL = <ComputationSpace.DUAL_RAIL: 'dual_rail'>

The Enum and its members also have the following methods:

classmethod default(*, no_bunching)

Derive the default computation space from the legacy no_bunching flag.

Return type:

ComputationSpace

classmethod coerce(value)

Normalize user-provided values (enum instances or case-insensitive strings).

Return type:

ComputationSpace

class merlin.algorithms.layer.DetectorTransform(simulation_keys, detectors, *, dtype=None, device=None, partial_measurement=False)

Bases: Module

Linear map applying per-mode detector rules to a Fock probability vector.

Args:
simulation_keys: Iterable describing the raw Fock states produced by the

simulator (as tuples or lists of integers).

detectors: One detector per optical mode. Each detector must expose the

detect() method from perceval.Detector.

dtype: Optional torch dtype for the transform matrix. Defaults to

torch.float32.

device: Optional device used to stage the transform matrix. partial_measurement: When True, only the modes whose detector entry is

not None are measured. The transform then operates on complex amplitudes and returns per-outcome dictionaries (see forward()).

forward(tensor)

Apply the detector transform.

Return type:

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

Args:
tensor: Probability distribution (complete mode) or amplitudes

(partial measurement). The last dimension must match the simulator basis.

Returns:
  • Complete mode: real probability tensor expressed in the detector basis.

  • Partial mode: list indexed by remaining photon count. Each entry is a dictionary whose keys are full-length mode tuples (unmeasured modes set to None) and whose values are lists of (probability, normalized remaining-mode amplitudes) pairs – one per perfect measurement branch.

property is_identity: bool

Whether the transform reduces to the identity (ideal PNR detectors).

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

Return the classical detection outcome keys.

property output_size: int

Number of classical outcomes produced by the detectors.

property partial_measurement: bool

Return True when the transform runs in partial measurement mode.

remaining_basis(remaining_n=None)

Return the ordered Fock-state basis for the unmeasured modes.

Return type:

list[tuple[int, ...]]

Args:
remaining_n: Optional photon count used to select a specific block.

When omitted, the method returns the concatenation of every remaining-mode basis enumerated during detector initialisation.

Returns:

List of tuples describing the photon distribution over the unmeasured modes.

row(index, *, dtype=None, device=None)

Return a single detector transform row as a dense tensor.

Return type:

Tensor

class merlin.algorithms.layer.InitializationContext(device, dtype, complex_dtype, amplitude_encoding, input_size, circuit, experiment, noise_model, has_custom_noise, input_state, n_photons, trainable_parameters, input_parameters, angle_encoding_specs, photon_survival_probs, detectors, has_custom_detectors, computation_space, measurement_strategy, warnings, return_object)

Bases: object

Immutable context for QuantumLayer initialization.

amplitude_encoding: bool
angle_encoding_specs: dict[str, dict[str, Any]]
circuit: Circuit
complex_dtype: dtype
computation_space: ComputationSpace
detectors: list[Detector]
device: Optional[device]
dtype: dtype
experiment: Experiment
has_custom_detectors: bool
has_custom_noise: bool
input_parameters: list[str]
input_size: Optional[int]
input_state: UnionType[StateVector, BasicState, Tensor, None]
measurement_strategy: MeasurementStrategy | _LegacyMeasurementStrategy
n_photons: Optional[int]
noise_model: Optional[Any]
photon_survival_probs: list[float]
return_object: bool
trainable_parameters: list[str]
warnings: list[str]
class merlin.algorithms.layer.Iterable

Bases: object

enum merlin.algorithms.layer.MeasurementKind(value)

Bases: Enum

New API: internal measurement kinds used by MeasurementStrategy.

Valid values are as follows:

PROBABILITIES = <MeasurementKind.PROBABILITIES: 'PROBABILITIES'>
MODE_EXPECTATIONS = <MeasurementKind.MODE_EXPECTATIONS: 'MODE_EXPECTATIONS'>
AMPLITUDES = <MeasurementKind.AMPLITUDES: 'AMPLITUDES'>
PARTIAL = <MeasurementKind.PARTIAL: 'PARTIAL'>
class merlin.algorithms.layer.MeasurementStrategy(type, measured_modes=(), computation_space=None, grouping=None)

Bases: object

New API: immutable definition of a measurement strategy for output post-processing.

AMPLITUDES: ClassVar[_LegacyMeasurementStrategy] = 'amplitudes'
MODE_EXPECTATIONS: ClassVar[_LegacyMeasurementStrategy] = 'mode_expectations'
NONE: ClassVar[MeasurementStrategy] = MeasurementStrategy(type=<MeasurementKind.AMPLITUDES: 'AMPLITUDES'>, measured_modes=(), computation_space=<ComputationSpace.UNBUNCHED: 'unbunched'>, grouping=None)
PROBABILITIES: ClassVar[_LegacyMeasurementStrategy] = 'probabilities'
static amplitudes(computation_space=ComputationSpace.UNBUNCHED)
Return type:

MeasurementStrategy

computation_space: Optional[ComputationSpace]
get_unmeasured_modes(n_modes)

Return the complement of the measured modes after validation.

Return type:

tuple[int, ...]

grouping: UnionType[LexGrouping, ModGrouping, None]
measured_modes: tuple[int, ...]
static mode_expectations(computation_space=ComputationSpace.UNBUNCHED)
Return type:

MeasurementStrategy

static partial(modes, computation_space=ComputationSpace.UNBUNCHED, grouping=None)

Create a partial measurement on the given mode indices. Note that the specified grouping only applies on the resulting probabilities, not on the amplitudes.

Return type:

MeasurementStrategy

static probs(computation_space=ComputationSpace.UNBUNCHED, grouping=None)
Return type:

MeasurementStrategy

type: MeasurementKind
validate_modes(n_modes)

Validate mode indices and warn when the selection covers all modes.

Return type:

None

class merlin.algorithms.layer.MerlinModule

Bases: Module

Generic MerLin module with shared utility functions

Merlin remote execution policy:
  • _force_simulation (bool) defaults to False. When True, the layer MUST run locally. The variable is set with property (getter and setter): force_local.

  • 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

  • as_simulation() provide local context forcing use as simulation

as_simulation()

Temporarily force local simulation within the context.

property force_local: bool

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

static setup_device_and_dtype(device, dtype)

Normalize device/dtype to final forms.

Return type:

tuple[Optional[device], dtype, dtype]

should_offload()

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

Return type:

bool

supports_offload()

Return True if this layer is technically offloadable.

Return type:

bool

class merlin.algorithms.layer.ModGrouping(input_size, output_size)

Bases: Module

Maps tensor to a modulo grouping of its components.

This mapper groups elements of the input tensor based on their index modulo the output size. Elements with the same modulo value are summed together to produce the output.

forward(x)

Map the input tensor to the desired output_size utilizing modulo grouping.

Args:

x: Input tensor of shape (n_batch, input_size) or (input_size,)

Returns:

Grouped tensor of shape (batch_size, output_size) or (output_size,)

class merlin.algorithms.layer.OutputMapper

Bases: object

Handles mapping quantum state amplitudes or probabilities to classical outputs.

This class provides factory methods for creating different types of output mappers that convert quantum state amplitudes or probabilities to classical outputs.

static create_mapping(strategy, computation_space=ComputationSpace.FOCK, keys=None, dtype=None)

Create an output mapping based on the specified strategy.

Args:

strategy: The measurement mapping strategy to use computation_space: The computation space for the measurement. keys: (Only used for ModeExpectations measurement strategy) List of tuples that represent the possible quantum Fock states.

For example, keys = [(0,1,0,2), (1,0,1,0), …]

dtype: Target dtype for internal tensors. Defaults to torch.float32.

Returns:

A PyTorch module that maps the per state amplitudes or probabilities to the desired format.

Raises:

ValueError: If strategy is unknown

class merlin.algorithms.layer.PartialMeasurement(branches, measured_modes, unmeasured_modes, grouping=None)

Bases: object

Collection of measurement branches along with measured/unmeasured mode metadata.

Args:

branches: Tuple of branches, ordered lexicographically by outcome. measured_modes: Indices of measured modes in the full system. unmeasured_modes: Indices of unmeasured modes in the full system. grouping: Optional grouping callable to group probabilities.

property amplitudes
static from_detector_transform_output(detector_output, *, grouping=None)

Branch-based PartialMeasurement wrapper from DetectorTransform(partial_measurement=True) output.

Return type:

PartialMeasurement

property n_measured_modes: int
property n_unmeasured_modes: int
property outcomes
property probabilities: Tensor

Return the probabilities of all branches as a tensor of shape (batch, n_branches) unless a grouping was set in which case, the probabilities are grouped and the returned tensor has shape (batch, grouping_output_size).

Same property as self.tensor.

property probability_tensor_shape: tuple[int, int]

Return the expected (batch, n_outcomes) shape for the probability tensor.

reorder_branches()

Reorder branches lexicographically by their outcomes.

Return type:

None

set_grouping(grouping)

Set the grouping used to group probabilities.

Once the grouping is set, the properties probabilities and tensor return grouped probabilities.

Return type:

None

Args:

grouping: Grouping object used to group probabilities.

property tensor: Tensor

Return the probabilities of all branches as a tensor of shape (batch, n_branches). unless a grouping was set in which case, the probabilities are grouped and the returned tensor has shape (batch, grouping_output_size).

This property assumes that all branches are ordered lexicographically by their outcomes so the stacking of probabilities follows the same order.

verify_branches_order()

Verify that branches are ordered lexicographically by their outcomes.

Return type:

None

class merlin.algorithms.layer.PhotonLossTransform(simulation_keys, survival_probs, *, dtype=None, device=None)

Bases: Module

Linear map applying per-mode photon loss to a Fock probability vector.

Args:
simulation_keys: Iterable describing the raw Fock states produced by the

simulator (as tuples or lists of integers).

survival_probs: One survival probability per optical mode. dtype: Optional torch dtype for the transform matrix. Defaults to

torch.float32.

device: Optional device used to stage the transform matrix.

forward(distribution)

Apply the photon loss transform to a Fock probability vector.

Return type:

Tensor

Args:

distribution: A Fock probability vector as a 1D torch tensor.

Returns:

A Fock probability vector after photon loss.

property is_identity: bool

Whether the transform corresponds to perfect transmission.

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

Classical Fock keys after photon loss.

property output_size: int

Number of classical outcomes after photon loss.

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)
class merlin.algorithms.layer.ProbabilityDistribution(tensor, n_modes, n_photons, computation_space=ComputationSpace.FOCK, logical_performance=None, _custom_basis=None)

Bases: object

Probability tensor bundled with Fock metadata and post-filter tracking.

Parameters

tensor:

Dense or sparse probabilities; leading dimensions are treated as batch axes.

n_modes:

Number of modes in the Fock space.

n_photons:

Total photon number represented by the distribution.

computation_space:

Basis enumeration used to order amplitudes (fock, unbunched, dual_rail).

logical_performance:

Optional per-batch scalar tracking kept/total probability after filtering.

Notes

Instances are normalized on construction; arithmetic-style temporary unnormalized states are not supported (unlike StateVector). Only shape, device, dtype, and requires_grad are delegated to the underlying torch.Tensor; tensor-like helpers to, clone, detach, and requires_grad_ mirror tensor semantics while keeping metadata and logical performance aligned. Layout-changing tensor operations should be done on tensor directly, then wrapped again via from_tensor to maintain a consistent basis.

property basis: Combinadics | FilteredBasis | tuple[tuple[int, ...], ...]
property basis_size: int
clone()

Return a cloned ProbabilityDistribution with metadata and logical performance copied.

Return type:

ProbabilityDistribution

computation_space: ComputationSpace = 'fock'
detach()

Return a detached ProbabilityDistribution sharing data without gradients.

Return type:

ProbabilityDistribution

filter(rule)

Apply post-selection filter and renormalize probabilities.

logical_performance records kept_mass / original_mass per batch.

Return type:

ProbabilityDistribution

Parameters

rule:

Computation space alias (fock, unbunched, dual_rail), a predicate, an explicit iterable of allowed states, or a tuple (space, predicate) to combine a computation-space constraint with an additional predicate.

Returns

ProbabilityDistribution

A new, normalized distribution; may shrink its basis when filtering to unbunched or dual_rail in the dense case.

Raises

ValueError

If dual_rail is selected with incompatible n_modes/n_photons or an unknown computation space is requested.

classmethod from_perceval(distribution, *, dtype=None, device=None, sparse=None)

Construct from a Perceval BSDistribution.

Validates that all entries share the same photon number and mode count.

Return type:

ProbabilityDistribution

Parameters

distribution:

Input Perceval distribution.

dtype / device:

Optional overrides for output tensor placement and precision.

sparse:

Force dense or sparse output; default auto-selects based on fill ratio.

Raises

ValueError

If the distribution is empty or inconsistent in shape/photon number.

classmethod from_state_vector(state_vector, *, dtype=None, device=None, computation_space=None)

Convert a StateVector to a probability distribution.

Return type:

ProbabilityDistribution

Parameters

state_vector:

Source amplitudes; must expose to_dense, n_modes, and n_photons.

dtype / device:

Optional overrides for output tensor placement and precision.

computation_space:

Optional basis scheme; defaults to fock.

classmethod from_tensor(tensor, *, n_modes, n_photons, computation_space=None, dtype=None, device=None)

Build a distribution from an explicit probability tensor.

Return type:

ProbabilityDistribution

Parameters

tensor:

Dense or sparse probability tensor; last dimension must match the basis size.

n_modes / n_photons:

Metadata for basis construction.

computation_space:

Optional basis scheme; defaults to fock.

dtype / device:

Optional overrides for output tensor placement and precision.

Raises

ValueError

If the last dimension does not match the expected basis size.

property is_normalized: bool
property is_sparse: bool
logical_performance: Optional[Tensor] = None
memory_bytes()

Return the tensor’s approximate memory footprint in bytes.

Return type:

int

n_modes: int
n_photons: int
normalize()

In-place normalization; safe for zero-mass batches.

Return type:

ProbabilityDistribution

Returns

ProbabilityDistribution

The same instance, normalized along the basis dimension.

probabilities()

Alias for to_dense() for readability.

Return type:

Tensor

requires_grad_(requires_grad=True)

Set requires_grad on underlying tensors and return self.

Return type:

ProbabilityDistribution

tensor: Tensor
to(*args, **kwargs)

Return a new ProbabilityDistribution with tensor (and logical_performance) moved/cast via torch.Tensor.to.

Return type:

ProbabilityDistribution

to_dense()

Return a dense, normalized tensor representation.

Return type:

Tensor

to_perceval()

Convert to Perceval BSDistribution (single) or list for batches.

class merlin.algorithms.layer.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=None, return_object=False, device=None, dtype=None)

Bases: MerlinModule

Quantum Neural Network Layer with factory-based architecture.

This layer can be created either from a CircuitBuilder instance, a pre-compiled pcvl.Circuit, or an :class:Experiment`.

export_config()

Export a standalone configuration for remote execution.

Return type:

dict

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

Forward pass through the quantum layer.

Encoding is inferred from the input type: :rtype: Tensor | PartialMeasurement | StateVector | ProbabilityDistribution

  • torch.Tensor (float): angle encoding (compatible with nn.Sequential)

  • torch.Tensor (complex): amplitude encoding

  • StateVector: amplitude encoding (preferred for quantum state injection)

Parameters

*input_parameterstorch.Tensor | StateVector

Input data. For angle encoding, pass float tensors. For amplitude encoding, pass a single StateVector or complex tensor.

shotsint | None, optional

Number of samples; if 0 or None, return exact amplitudes/probabilities.

sampling_methodstr | None, optional

Sampling method, e.g. “multinomial”.

simultaneous_processesint | None, optional

Batch size hint for parallel computation.

Returns

torch.Tensor | PartialMeasurement | StateVector | ProbabilityDistribution

Output after measurement mapping. Depending on the return_object argument and measurement strategy defined in the input, the output type will be different. Check the constructor for more details.

Raises

TypeError

If inputs mix torch.Tensor and StateVector, or if an unsupported input type is provided.

ValueError

If multiple StateVector inputs are provided.

property has_custom_detectors: bool
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_input_state(input_state)
set_sampling_config(shots=None, sampling_method=None)

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

classmethod simple(cls, input_size, output_size=None, device=None, dtype=None, computation_space=ComputationSpace.UNBUNCHED)

Create a ready-to-train layer with a input_size-mode, (input_size//2)-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 fully trainable entangling layer acting on all modes.

Args:

input_size: Size of the classical input vector. Must be 20 or lower. output_size: Optional classical output width. device: Optional target device for tensors. dtype: Optional tensor dtype. computation_space: Logical computation subspace; one of {“fock”, “unbunched”, “dual_rail”}.

Returns:

QuantumLayer configured with the described architecture.

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)
class merlin.algorithms.layer.Sequence

Bases: Reversible, Collection

All the operations on a read-only sequence.

Concrete subclasses must override __new__ or __init__, __getitem__, and __len__.

count(value) integer -- return number of occurrences of value
index(value[, start[, stop]]) integer -- return first index of value.

Raises ValueError if the value is not present.

Supporting start and stop arguments is optional, but recommended.

enum merlin.algorithms.layer.StatePattern(value)

Bases: str, Enum

Input photon state patterns.

Member Type:

str

Valid values are as follows:

DEFAULT = <StatePattern.DEFAULT: 'default'>
SPACED = <StatePattern.SPACED: 'spaced'>
SEQUENTIAL = <StatePattern.SEQUENTIAL: 'sequential'>
PERIODIC = <StatePattern.PERIODIC: 'periodic'>
class merlin.algorithms.layer.StateVector(tensor, n_modes, n_photons, _normalized=False)

Bases: object

Amplitude tensor bundled with its Fock metadata.

Keeps n_modes / n_photons and combinadics basis ordering alongside the underlying PyTorch tensor (dense or sparse).

Parameters

tensor:

Dense or sparse amplitude tensor; leading dimensions (if any) are treated as batch axes.

n_modes:

Number of modes in the Fock space.

n_photons:

Total photon number represented by the state.

_normalized:

Internal flag tracking whether the stored tensor is normalized.

Notes

This is a thin wrapper over a torch.Tensor: only shape, device, dtype, and requires_grad are delegated automatically, and tensor-like helpers to, clone, detach, and requires_grad_ are provided to mirror common tensor workflows while preserving metadata. Layout-changing operations (e.g., reshape/view) are intentionally not exposed; perform those on tensor explicitly if needed and rebuild via from_tensor.

property basis: Combinadics

Lazy combinadics basis for (n_modes, n_photons) in Fock ordering.

property basis_size: int

Return the number of basis states for (n_modes, n_photons).

clone()

Return a cloned StateVector with identical metadata and normalization flag.

Return type:

StateVector

detach()

Return a detached StateVector sharing data without gradients.

Return type:

StateVector

classmethod from_basic_state(state, *, dtype=None, device=None, sparse=True)

Create a one-hot state from a Fock occupation list/BasicState.

Return type:

StateVector

Args:

state: Occupation numbers per mode. dtype: Optional target dtype. device: Optional target device. sparse: Build sparse layout when True.

Returns:

StateVector: One-hot state.

classmethod from_perceval(state_vector, *, dtype=None, device=None, sparse=None)

Build from a pcvl.StateVector.

Return type:

StateVector

Args:

state_vector: Perceval state to wrap. dtype: Optional target dtype. device: Optional target device. sparse: Force sparse/dense; if None use density heuristic (<=30%).

Returns:

StateVector: Merlin wrapper with metadata and preserved amplitudes.

Raises:

ValueError: If the Perceval state is empty or has inconsistent photon/mode counts.

classmethod from_tensor(tensor, *, n_modes, n_photons, dtype=None, device=None)

Wrap an existing tensor with explicit metadata.

Return type:

StateVector

Args:

tensor: Dense or sparse amplitude tensor. n_modes: Number of modes. n_photons: Total photons. dtype: Optional target dtype. device: Optional target device.

Returns:

StateVector: Wrapped tensor.

Raises:

ValueError: If the last dimension does not match the basis size.

index(state)

Return basis index for the given Fock state.

Return type:

Optional[int]

Args:

state: Occupation list or BasicState.

Returns:

int | None: Basis index, or None if not present (or zero in sparse tensor).

property is_normalized: bool
property is_sparse: bool

Return True if the underlying tensor uses a sparse layout.

memory_bytes()

Approximate memory footprint (bytes) of the underlying tensor data.

Return type:

int

n_modes: int
n_photons: int
normalize()

Normalize this state in-place and return self.

Return type:

StateVector

normalized_str()

Human-friendly string of the normalized state (forces normalization for display).

Return type:

str

requires_grad_(requires_grad=True)

Set requires_grad on the underlying tensor and return self.

Return type:

StateVector

tensor: Tensor
tensor_product(other, *, sparse=None)

Tensor product of two states with metadata propagation.

If any operand is dense, the result is dense. Supports one-hot fast path. The resulting state is normalized before returning.

Return type:

StateVector

Args:

other: Another StateVector or a BasicState/occupation list. sparse: Override sparsity of the result; default keeps dense if any input dense.

Returns:

StateVector: Combined state with summed modes/photons (normalized).

Raises:

ValueError: If tensors are not 1D.

to(*args, **kwargs)

Return a new StateVector with the tensor moved/cast via torch.Tensor.to.

Return type:

StateVector

to_dense()

Return a dense, normalized tensor view of the amplitudes.

Return type:

Tensor

to_perceval()

Convert to pcvl.StateVector.

Return type:

StateVector | list[StateVector]

Args:

None

Returns:

pcvl.StateVector | list[pcvl.StateVector]: A Perceval state for 1D tensors, or a list for batched tensors, with amplitudes preserved (no extra renormalization).

merlin.algorithms.layer.apply_angle_encoding(x, spec)

Apply custom angle encoding using stored metadata.

Return type:

Tensor

merlin.algorithms.layer.cast(typ, val)

Cast a value to a type.

This returns the value unchanged. To the type checker this signals that the return value has the designated type, but at runtime we intentionally don’t check anything (we want this to be as fast as possible).

merlin.algorithms.layer.contextmanager(func)

@contextmanager decorator.

Typical usage:

@contextmanager def some_generator(<arguments>):

<setup> try:

yield <value>

finally:

<cleanup>

This makes this:

with some_generator(<arguments>) as <variable>:

<body>

equivalent to this:

<setup> try:

<variable> = <value> <body>

finally:

<cleanup>

merlin.algorithms.layer.feature_count_for_prefix(prefix, angle_encoding_specs, spec_mappings=None)

Infer the number of raw features associated with an encoding prefix.

Return type:

Optional[int]

merlin.algorithms.layer.generate_state(n_modes, n_photons, state_pattern=StatePattern.DEFAULT)

Generate a Perceval Fock input state as a BasicState.

Return type:

BasicState

Args:

n_modes: Number of photonic modes. n_photons: Total number of photons. state_pattern: Placement strategy for the photons.

Returns:

A perceval.BasicState instance.

Raises:

ValueError: If the inputs are inconsistent or the pattern is unknown.

merlin.algorithms.layer.normalize_measurement_strategy(measurement_strategy, computation_space)

Normalize measurement strategy + computation space with deprecation warnings.

Enforces the v0.3 requirement that computation_space must live inside MeasurementStrategy when using the new factory methods (probs, mode_expectations, partial).

Return type:

tuple[MeasurementStrategy | _LegacyMeasurementStrategy, ComputationSpace]

Rules: 1. If MeasurementStrategy instance (new API) + constructor computation_space provided

→ ERROR: user must move computation_space into the factory method

  1. If measurement_strategy is None and computation_space provided → OK with deprecation warning (default to MeasurementStrategy.probs(computation_space))

  2. If legacy enum (PROBABILITIES, etc) + constructor computation_space → OK with deprecation warning (backward compat)

  3. If MeasurementStrategy instance only → use its computation_space

  4. If legacy enum only → wrap with computation_space param

merlin.algorithms.layer.normalize_probabilities_and_amplitudes(amplitudes, computation_space)

Return probabilities and renormalized amplitudes when required.

Return type:

tuple[Tensor, Tensor]

merlin.algorithms.layer.prepare_input_encoding(x, prefix=None, angle_encoding_specs=None)

Prepare input encoding based on mode.

Return type:

Tensor

merlin.algorithms.layer.prepare_input_state(input_state, n_photons, computation_space, device, complex_dtype, experiment=None, circuit_m=None, amplitude_encoding=False)

Normalize input_state to canonical form.

Return type:

tuple[UnionType[StateVector, BasicState, Tensor, None], Optional[int]]

Parameters

input_stateStateVector | pcvl.StateVector | pcvl.BasicState | list | tuple | torch.Tensor | None

The input state in various formats. StateVector is the canonical type. Legacy formats are auto-converted with deprecation warnings where appropriate.

n_photonsint | None

Number of photons (used for default state generation).

computation_spaceComputationSpace

The computation space configuration.

devicetorch.device | None

Target device for tensors.

complex_dtypetorch.dtype

Complex dtype for tensor conversion.

experimentpcvl.Experiment | None

Optional experiment whose input_state takes precedence.

circuit_mint | None

Number of modes in the circuit (for default state generation).

amplitude_encodingbool

Whether amplitude encoding is enabled.

Returns

tuple[StateVector | pcvl.BasicState | torch.Tensor | None, int | None]

The normalized input state and resolved photon count.

Raises

ValueError

If neither input_state nor n_photons is provided, or if StateVector is empty.

Warns

DeprecationWarning

When torch.Tensor is passed as input_state (deprecated in favor of StateVector).

UserWarning

When both experiment.input_state and input_state are provided.

merlin.algorithms.layer.resolve_circuit(circuit_source, pcvl_module)

Convert builder/circuit/experiment to unified circuit form.

Return type:

ResolvedCircuit

merlin.algorithms.layer.resolve_measurement_strategy(measurement_strategy)

Return the concrete strategy implementation for the enum value.

Return type:

BaseMeasurementStrategy

merlin.algorithms.layer.sanitize_parameters(*args, **_kw)

Decorator to centralize parameter sanitization for method calls.

Usage: - As a plain decorator: @sanitize_parameters (no parentheses) - As a factory with processors: @sanitize_parameters(proc1, proc2, …)

Behavior: - Emits standardized warnings/errors based on the global deprecation registry. - Applies converter functions registered for present deprecated params. - Applies any additional processors(qual, kwargs) provided, sequentially, each receiving and returning kwargs.

Return type:

Any

merlin.algorithms.layer.setup_noise_and_detectors(experiment, circuit, computation_space, measurement_strategy)

Extract and validate noise/detectors.

Return type:

NoiseAndDetectorConfig

merlin.algorithms.layer.split_inputs_by_prefix(prefixes, tensor, angle_encoding_specs, spec_mappings=None)

Split a single logical input tensor into per-prefix chunks when possible.

Return type:

Optional[list[Tensor]]

merlin.algorithms.layer.validate_and_resolve_circuit_source(builder, circuit, experiment, trainable_parameters, input_parameters)

Enforce exactly one of (builder, circuit, experiment) is provided.

Return type:

CircuitSource

merlin.algorithms.layer.validate_encoding_mode(amplitude_encoding, input_size, n_photons, input_parameters)

Fail-fast validation for amplitude encoding constraints.

Return type:

EncodingModeConfig

merlin.algorithms.layer.vet_experiment(experiment)

Check experiment constraints.

Return type:

dict[str, bool]

Note

Quantum layers built from a perceval.Experiment now apply the experiment’s per-mode detector configuration before returning classical outputs. When no detectors are specified, ideal photon-number resolving detectors are used by default.

If the experiment carries a perceval.NoiseModel (via experiment.noise), MerLin inserts a PhotonLossTransform ahead of any detector transform. The resulting output_keys and output_size therefore include every survival/loss configuration implied by the model, and amplitude read-out is disabled whenever custom detectors or photon loss are present.

Example: Quickstart QuantumLayer

import torch.nn as nn
from merlin import QuantumLayer

simple_layer = QuantumLayer.simple(
    input_size=4,
)

model = nn.Sequential(
    simple_layer,
    nn.Linear(simple_layer.output_size, 3),
)
# Train and evaluate as a standard torch.nn.Module

Note

QuantumLayer.simple() returns a thin SimpleSequential wrapper that behaves like a standard PyTorch module while exposing the inner quantum layer as .quantum_layer and any post-processing (ModGrouping or Identity) as .post_processing. The wrapper also forwards .circuit and .output_size so existing code that inspects these attributes continues to work.

A Perceval Circuit built with QuantumLayer.simple

The simple quantum layer above implements a circuit of (input_size) modes and (input_size//2) photons. This circuit is made of: - A fully trainable entangling layer acting on all modes; - A full input encoding layer spanning all encoded features; - A fully trainable entangling layer acting on all modes.

Example: Declarative builder API

import torch.nn as nn
from merlin import LexGrouping, MeasurementStrategy, QuantumLayer
from merlin.builder import CircuitBuilder
builder = CircuitBuilder(n_modes=6)
builder.add_entangling_layer(trainable=True, name="U1")
builder.add_angle_encoding(modes=list(range(4)), name="input")
builder.add_rotations(trainable=True, name="theta")
builder.add_superpositions(depth=1)

builder_layer = QuantumLayer(
    input_size=4,
    builder=builder,
    n_photons=3,  # is equivalent to input_state=[1,1,1,0,0,0]
    measurement_strategy=MeasurementStrategy.probs(),
)

model = nn.Sequential(
    builder_layer,
    LexGrouping(builder_layer.output_size, 3),
)
# Train and evaluate as a standard torch.nn.Module
A Perceval Circuit built with the CircuitBuilder

The circuit builder allows you to build your circuit layer by layer, with a high-level API. The example above implements a circuit of 6 modes and 3 photons. This circuit is made of: - A first entangling layer (trainable) - Angle encoding on the first 4 modes (for 4 input parameters with the name “input”) - A trainable rotation layer to add more trainable parameters - An entangling layer to add more expressivity

Other building blocks in the CircuitBuilder include:

  • add_rotations: Add single or multiple phase shifters (rotations) to specific modes. Rotations can be fixed, trainable, or data-driven (input-encoded).

  • add_angle_encoding: Encode classical data as quantum rotation angles, supporting higher-order feature combinations for expressive input encoding.

  • add_entangling_layer: Insert a multi-mode entangling layer (implemented via a generic interferometer), optionally trainable, and tune its internal template with the model argument ("mzi" or "bell") for different mixing behaviours.

  • add_superpositions: Add one or more beam splitters (superposition layers) with configurable targets, depth, and trainability.

Example: Manual Perceval circuit (more control)

import torch.nn as nn
import perceval as pcvl
from merlin import LexGrouping, MeasurementStrategy, QuantumLayer
modes = 6
wl = pcvl.GenericInterferometer(
    modes,
    lambda i: pcvl.BS() // pcvl.PS(pcvl.P(f"theta_li{i}")) //
    pcvl.BS() // pcvl.PS(pcvl.P(f"theta_lo{i}")),
    shape=pcvl.InterferometerShape.RECTANGLE,
)
circuit = pcvl.Circuit(modes)
circuit.add(0, wl)
for mode in range(4):
    circuit.add(mode, pcvl.PS(pcvl.P(f"input{mode}")))
wr = pcvl.GenericInterferometer(
    modes,
    lambda i: pcvl.BS() // pcvl.PS(pcvl.P(f"theta_ri{i}")) //
    pcvl.BS() // pcvl.PS(pcvl.P(f"theta_ro{i}")),
    shape=pcvl.InterferometerShape.RECTANGLE,
)
circuit.add(0, wr)

manual_layer = QuantumLayer(
    input_size=4,  # matches the number of phase shifters named "input{mode}"
    circuit=circuit,
    input_state=[1, 0, 1, 0, 1, 0],
    trainable_parameters=["theta"],
    input_parameters=["input"],
    measurement_strategy=MeasurementStrategy.probs(),
)

model = nn.Sequential(
    manual_layer,
    LexGrouping(manual_layer.output_size, 3),
)
# Train and evaluate as a standard torch.nn.Module
A Perceval Circuit built with the Perceval API

Here, the grouping can also be directly added to the MeasurementStrategy object used in the measurement_strategy parameter.

See the User guide and Notebooks for more advanced usage and training routines !

Input states and amplitude encoding

The input state of a photonic circuit specifies how the photons enter the device. Physically this can be a single Fock state (a precise configuration of n_photons over m modes) or a superposed/entangled state within the same computation space (for example Bell pairs or GHZ states). QuantumLayer accepts the following representations:

  • perceval.BasicState – a single configuration such as pcvl.BasicState([1, 0, 1, 0]);

  • perceval.StateVector – an arbitrary superposition of basic states with complex amplitudes;

  • Python lists/tuples, e.g. [1, 0, 1, 0]. These are accepted as convenience inputs and are immediately converted

    to a Perceval perceval.BasicState.

Note

For Fock/occupation inputs, QuantumLayer stores .input_state as a Perceval perceval.BasicState. If you need the raw occupation vector, use list(layer.input_state).

When input_state is passed, the layer always injects that photonic state. In more elaborate pipelines you may want to cascade circuits and let the output amplitudes of the previous layer become the input state of the next. Merlin calls this amplitude encoding: the probability amplitudes themselves carry information and are passed to the next layer as a tensor. Enabling this behaviour is done with amplitude_encoding=True; in that mode the forward input of QuantumLayer is the complex photonic state.

The snippet below prepares a dual-rail Bell state as the initial condition and evaluates a batch of classical parameters:

import torch
import perceval as pcvl
from merlin.algorithms.layer import QuantumLayer
from merlin.core import ComputationSpace
from merlin.measurement.strategies import MeasurementStrategy
from merlin.measurement.

circuit = pcvl.Unitary(pcvl.Matrix.random_unitary(4))  # some haar-random 4-mode circuit

bell = pcvl.StateVector()
bell += pcvl.BasicState([1, 0, 1, 0])
bell += pcvl.BasicState([0, 1, 0, 1])
print(bell) # bell is a state vector of 2 photons in 4 modes

layer = QuantumLayer(
    circuit=circuit,
    n_photons=2,
    input_state=bell,
    measurement_strategy=MeasurementStrategy.probs(computation_space=ComputationSpace.DUAL_RAIL),
)

x = torch.rand(10, circuit.m)  # batch of classical parameters
amplitudes = layer(x)
assert amplitudes.shape == (10, 2**2)

For comparison, the amplitude_encoding variant supplies the photonic state during the forward pass:

import torch
import perceval as pcvl
from merlin.algorithms.layer import QuantumLayer
from merlin.core import MeasurementStrategy,ComputationSpace

circuit = pcvl.Circuit(3)

layer = QuantumLayer(
    circuit=circuit,
    n_photons=2,
    amplitude_encoding=True,
    measurement_strategy=MeasurementStrategy.probs(computation_space=ComputationSpace.UNBUNCHED),
    dtype=torch.cdouble,
)

prepared_states = torch.tensor(
    [[1.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j],
     [0.0 + 0.0j, 0.0 + 0.0j, 1.0 + 0.0j]],
    dtype=torch.cdouble,
)

out = layer(prepared_states)

In the first example the circuit always starts from bell; in the second, each row of prepared_states represents a different logical photonic state that flows through the layer. This separation allows you to mix classical angle encoding with fully quantum, amplitude-based data pipelines.

Returning typed objects

When return_object is set to True, the output of a forward() call depends of the measurement_strategy. By default, it is set to False. See the following output matrix to size what to expect as the return of a forward call.

measurement_strategy | return_object=False | return_object=True |
:——————- | :—————— | :—————-: |
AMPLTITUDES | torch.Tensor | StateVector |
PROBABILITIES | torch.Tensor | ProbabilityDistribution |
PARTIAL_MEASUREMENT | PartialMeasurement | PartialMeasurement |
MODE_EXPECTATIONS | torch.Tensor | torch.Tensor |

Most of the typed objects can give the torch.Tensor as an output with the .tensor parameter. Only the PartialMeasurement object is a little different. See its according documentation.

These object could be quite useful to access metadata like the number of photons, modes and measurement_strategy behind the output tensors. For example, a better access to specific states is available with StateVector and ProbabilityDistribution by indexing the desired state. The objects also have an interoperability with Perceval making it easy interations to have an easy crossplay between the two libraries.

For more information on the typed output capabilities, follow the following links: - StateVector : /api_reference/api/merlin.algorithms.core.state_vector - ProbabilityDistribution : /api_reference/api/merlin.algorithms.core.probability_distribution - PartialMeasurement : /api_reference/api/merlin.algorithms.core.partial_measurement

The snippet below prepares a basic quantum layer and returns a ProbabilityDistribution object:

import torch
import perceval as pcvl
from merlin.algorithms.layer import QuantumLayer
from merlin.core import ComputationSpace
from merlin.measurement.strategies import MeasurementStrategy
from merlin import ProbabilityDistribution

circuit = pcvl.Unitary(pcvl.Matrix.random_unitary(4))  # some haar-random 4-mode circuit

bell = pcvl.StateVector()
bell += pcvl.BasicState([1, 0, 1, 0])
bell += pcvl.BasicState([0, 1, 0, 1])
print(bell) # bell is a state vector of 2 photons in 4 modes

layer = QuantumLayer(
    circuit=circuit,
    n_photons=2,
    input_state=bell,
    measurement_strategy=MeasurementStrategy.probs(computation_space=ComputationSpace.DUAL_RAIL),
    return_object=True,
)

x = torch.rand(10, circuit.m)  # batch of classical parameters
probs = layer(x)
assert isinstance(probs,ProbabilityDistribution)
assert isinstance(probs.tensor,torch.Tensor)

Deprecations

Warning

Deprecated since version 0.3: The use of the no_bunching flag is deprecated and is removed since version 0.3.0. Use the computation_space flag inside measurement_strategy instead. See Migration guide.