merlin.algorithms.kernels module

class merlin.algorithms.kernels.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.kernels.Callable

Bases: object

class merlin.algorithms.kernels.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) similar to the legacy FeatureEncoder.

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.kernels.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.algorithms.kernels.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.kernels.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.kernels.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.kernels.KernelCircuitBuilder

Bases: object

Builder class for creating quantum kernel circuits with photonic backends.

This class provides a fluent interface for building quantum kernel circuits with various configurations, inspired by the core.layer architecture.

angle_encoding(*, scale=1.0)

Configure the angle encoding scale.

Return type:

KernelCircuitBuilder

bandwidth_tuning(enabled=True)

Enable or disable bandwidth tuning.

Return type:

KernelCircuitBuilder

build_feature_map()

Build and return a FeatureMap instance.

Return type:

FeatureMap

Returns:

Configured FeatureMap

Raises:

ValueError – If required parameters are missing

build_fidelity_kernel(input_state=None, *, shots=0, sampling_method='multinomial', no_bunching=False, force_psd=True)

Build and return a FidelityKernel instance.

Parameters:
  • input_state (Optional[list[int]]) – Input Fock state. If None, automatically generated

  • shots (int) – Number of sampling shots

  • sampling_method (str) – Sampling method for shots

  • no_bunching (bool) – Whether to exclude bunched states

  • force_psd (bool) – Whether to project to positive semi-definite

Return type:

FidelityKernel

Returns:

Configured FidelityKernel

device(device)

Set the computation device.

Return type:

KernelCircuitBuilder

dtype(dtype)

Set the data type for computations.

Return type:

KernelCircuitBuilder

input_size(size)

Set the input dimensionality.

Return type:

KernelCircuitBuilder

n_modes(modes)

Set the number of modes in the circuit.

Return type:

KernelCircuitBuilder

n_photons(photons)

Set the number of photons.

Return type:

KernelCircuitBuilder

trainable(enabled=True, *, prefix='phi')

Enable or disable trainable rotations generated by the helper.

Return type:

KernelCircuitBuilder

class merlin.algorithms.kernels.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.kernels.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.algorithms.kernels.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'>
class merlin.algorithms.kernels.Tensor

Bases: TensorBase

align_to(*names)

Permutes the dimensions of the self tensor to match the order specified in names, adding size-one dims for any new names.

All of the dims of self must be named in order to use this method. The resulting tensor is a view on the original tensor.

All dimension names of self must be present in names. names may contain additional names that are not in self.names; the output tensor has a size-one dimension for each of those new names.

names may contain up to one Ellipsis (...). The Ellipsis is expanded to be equal to all dimension names of self that are not mentioned in names, in the order that they appear in self.

Python 2 does not support Ellipsis but one may use a string literal instead ('...').

Args:
names (iterable of str): The desired dimension ordering of the

output tensor. May contain up to one Ellipsis that is expanded to all unmentioned dim names of self.

Examples:

>>> tensor = torch.randn(2, 2, 2, 2, 2, 2)
>>> named_tensor = tensor.refine_names('A', 'B', 'C', 'D', 'E', 'F')

# Move the F and E dims to the front while keeping the rest in order
>>> named_tensor.align_to('F', 'E', ...)

Warning

The named tensor API is experimental and subject to change.

backward(gradient=None, retain_graph=None, create_graph=False, inputs=None)

Computes the gradient of current tensor wrt graph leaves.

The graph is differentiated using the chain rule. If the tensor is non-scalar (i.e. its data has more than one element) and requires gradient, the function additionally requires specifying a gradient. It should be a tensor of matching type and shape, that represents the gradient of the differentiated function w.r.t. self.

This function accumulates gradients in the leaves - you might need to zero .grad attributes or set them to None before calling it. See Default gradient layouts for details on the memory layout of accumulated gradients.

Note

If you run any forward ops, create gradient, and/or call backward in a user-specified CUDA stream context, see Stream semantics of backward passes.

Note

When inputs are provided and a given input is not a leaf, the current implementation will call its grad_fn (though it is not strictly needed to get this gradients). It is an implementation detail on which the user should not rely. See https://github.com/pytorch/pytorch/pull/60521#issuecomment-867061780 for more details.

Args:
gradient (Tensor, optional): The gradient of the function

being differentiated w.r.t. self. This argument can be omitted if self is a scalar.

retain_graph (bool, optional): If False, the graph used to compute

the grads will be freed. Note that in nearly all cases setting this option to True is not needed and often can be worked around in a much more efficient way. Defaults to the value of create_graph.

create_graph (bool, optional): If True, graph of the derivative will

be constructed, allowing to compute higher order derivative products. Defaults to False.

inputs (sequence of Tensor, optional): Inputs w.r.t. which the gradient will be

accumulated into .grad. All other tensors will be ignored. If not provided, the gradient is accumulated into all the leaf Tensors that were used to compute the tensors.

detach()

Returns a new Tensor, detached from the current graph.

The result will never require gradient.

This method also affects forward mode AD gradients and the result will never have forward mode AD gradients.

Note

Returned Tensor shares the same storage with the original one. In-place modifications on either of them will be seen, and may trigger errors in correctness checks.

detach_()

Detaches the Tensor from the graph that created it, making it a leaf. Views cannot be detached in-place.

This method also affects forward mode AD gradients and the result will never have forward mode AD gradients.

dim_order(*, ambiguity_check=False)

Returns the uniquely determined tuple of int describing the dim order or physical layout of self.

The dim order represents how dimensions are laid out in memory of dense tensors, starting from the outermost to the innermost dimension.

Note that the dim order may not always be uniquely determined. If ambiguity_check is True, this function raises a RuntimeError when the dim order cannot be uniquely determined; If ambiguity_check is a list of memory formats, this function raises a RuntimeError when tensor can not be interpreted into exactly one of the given memory formats, or it cannot be uniquely determined. If ambiguity_check is False, it will return one of legal dim order(s) without checking its uniqueness. Otherwise, it will raise TypeError.

Args:

ambiguity_check (bool or List[torch.memory_format]): The check method for ambiguity of dim order.

Examples:

>>> torch.empty((2, 3, 5, 7)).dim_order()
(0, 1, 2, 3)
>>> torch.empty((2, 3, 5, 7)).transpose(1, 2).dim_order()
(0, 2, 1, 3)
>>> torch.empty((2, 3, 5, 7), memory_format=torch.channels_last).dim_order()
(0, 2, 3, 1)
>>> torch.empty((1, 2, 3, 4)).dim_order()
(0, 1, 2, 3)
>>> try:
...     torch.empty((1, 2, 3, 4)).dim_order(ambiguity_check=True)
... except RuntimeError as e:
...     print(e)
The tensor does not have unique dim order, or cannot map to exact one of the given memory formats.
>>> torch.empty((1, 2, 3, 4)).dim_order(
...     ambiguity_check=[torch.contiguous_format, torch.channels_last]
... )  # It can be mapped to contiguous format
(0, 1, 2, 3)
>>> try:
...     torch.empty((1, 2, 3, 4)).dim_order(ambiguity_check="ILLEGAL")
... except TypeError as e:
...     print(e)
The ambiguity_check argument must be a bool or a list of memory formats.

Warning

The dim_order tensor API is experimental and subject to change.

eig(eigenvectors=False)
is_shared()

Checks if tensor is in shared memory.

This is always True for CUDA tensors.

istft(n_fft, hop_length=None, win_length=None, window=None, center=True, normalized=False, onesided=None, length=None, return_complex=False)

See torch.istft()

lstsq(other)
lu(pivot=True, get_infos=False)

See torch.lu()

module_load(other, assign=False)

Defines how to transform other when loading it into self in load_state_dict().

Used when get_swap_module_params_on_conversion() is True.

It is expected that self is a parameter or buffer in an nn.Module and other is the value in the state dictionary with the corresponding key, this method defines how other is remapped before being swapped with self via swap_tensors() in load_state_dict().

Note

This method should always return a new object that is not self or other. For example, the default implementation returns self.copy_(other).detach() if assign is False or other.detach() if assign is True.

Args:

other (Tensor): value in state dict with key corresponding to self assign (bool): the assign argument passed to nn.Module.load_state_dict()

norm(p='fro', dim=None, keepdim=False, dtype=None)

See torch.norm()

refine_names(*names)

Refines the dimension names of self according to names.

Refining is a special case of renaming that “lifts” unnamed dimensions. A None dim can be refined to have any name; a named dim can only be refined to have the same name.

Because named tensors can coexist with unnamed tensors, refining names gives a nice way to write named-tensor-aware code that works with both named and unnamed tensors.

names may contain up to one Ellipsis (...). The Ellipsis is expanded greedily; it is expanded in-place to fill names to the same length as self.dim() using names from the corresponding indices of self.names.

Python 2 does not support Ellipsis but one may use a string literal instead ('...').

Args:
names (iterable of str): The desired names of the output tensor. May

contain up to one Ellipsis.

Examples:

>>> imgs = torch.randn(32, 3, 128, 128)
>>> named_imgs = imgs.refine_names('N', 'C', 'H', 'W')
>>> named_imgs.names
('N', 'C', 'H', 'W')

>>> tensor = torch.randn(2, 3, 5, 7, 11)
>>> tensor = tensor.refine_names('A', ..., 'B', 'C')
>>> tensor.names
('A', None, None, 'B', 'C')

Warning

The named tensor API is experimental and subject to change.

register_hook(hook)

Registers a backward hook.

The hook will be called every time a gradient with respect to the Tensor is computed. The hook should have the following signature:

hook(grad) -> Tensor or None

The hook should not modify its argument, but it can optionally return a new gradient which will be used in place of grad.

This function returns a handle with a method handle.remove() that removes the hook from the module.

Note

See backward-hooks-execution for more information on how when this hook is executed, and how its execution is ordered relative to other hooks.

Example:

>>> v = torch.tensor([0., 0., 0.], requires_grad=True)
>>> h = v.register_hook(lambda grad: grad * 2)  # double the gradient
>>> v.backward(torch.tensor([1., 2., 3.]))
>>> v.grad

 2
 4
 6
[torch.FloatTensor of size (3,)]

>>> h.remove()  # removes the hook
register_post_accumulate_grad_hook(hook)

Registers a backward hook that runs after grad accumulation.

The hook will be called after all gradients for a tensor have been accumulated, meaning that the .grad field has been updated on that tensor. The post accumulate grad hook is ONLY applicable for leaf tensors (tensors without a .grad_fn field). Registering this hook on a non-leaf tensor will error!

The hook should have the following signature:

hook(param: Tensor) -> None

Note that, unlike other autograd hooks, this hook operates on the tensor that requires grad and not the grad itself. The hook can in-place modify and access its Tensor argument, including its .grad field.

This function returns a handle with a method handle.remove() that removes the hook from the module.

Note

See backward-hooks-execution for more information on how when this hook is executed, and how its execution is ordered relative to other hooks. Since this hook runs during the backward pass, it will run in no_grad mode (unless create_graph is True). You can use torch.enable_grad() to re-enable autograd within the hook if you need it.

Example:

>>> v = torch.tensor([0., 0., 0.], requires_grad=True)
>>> lr = 0.01
>>> # simulate a simple SGD update
>>> h = v.register_post_accumulate_grad_hook(lambda p: p.add_(p.grad, alpha=-lr))
>>> v.backward(torch.tensor([1., 2., 3.]))
>>> v
tensor([-0.0100, -0.0200, -0.0300], requires_grad=True)

>>> h.remove()  # removes the hook
reinforce(reward)
rename(*names, **rename_map)

Renames dimension names of self.

There are two main usages:

self.rename(**rename_map) returns a view on tensor that has dims renamed as specified in the mapping rename_map.

self.rename(*names) returns a view on tensor, renaming all dimensions positionally using names. Use self.rename(None) to drop names on a tensor.

One cannot specify both positional args names and keyword args rename_map.

Examples:

>>> imgs = torch.rand(2, 3, 5, 7, names=('N', 'C', 'H', 'W'))
>>> renamed_imgs = imgs.rename(N='batch', C='channels')
>>> renamed_imgs.names
('batch', 'channels', 'H', 'W')

>>> renamed_imgs = imgs.rename(None)
>>> renamed_imgs.names
(None, None, None, None)

>>> renamed_imgs = imgs.rename('batch', 'channel', 'height', 'width')
>>> renamed_imgs.names
('batch', 'channel', 'height', 'width')

Warning

The named tensor API is experimental and subject to change.

rename_(*names, **rename_map)

In-place version of rename().

resize(*sizes)
resize_as(tensor)
share_memory_()

Moves the underlying storage to shared memory.

This is a no-op if the underlying storage is already in shared memory and for CUDA tensors. Tensors in shared memory cannot be resized.

See torch.UntypedStorage.share_memory_() for more details.

solve(other)
split(split_size, dim=0)

See torch.split()

stft(n_fft, hop_length=None, win_length=None, window=None, center=True, pad_mode='reflect', normalized=False, onesided=None, return_complex=None, align_to_window=None)

See torch.stft()

Warning

This function changed signature at version 0.4.1. Calling with the previous signature may cause error or return incorrect result.

storage() torch.TypedStorage

Returns the underlying TypedStorage.

Warning

TypedStorage is deprecated. It will be removed in the future, and UntypedStorage will be the only storage class. To access the UntypedStorage directly, use Tensor.untyped_storage().

storage_type() type

Returns the type of the underlying storage.

symeig(eigenvectors=False)
to_sparse_coo()

Convert a tensor to coordinate format.

Examples:

>>> dense = torch.randn(5, 5)
>>> sparse = dense.to_sparse_coo()
>>> sparse._nnz()
25
unflatten(dim, sizes) Tensor

See torch.unflatten().

unique(sorted=True, return_inverse=False, return_counts=False, dim=None)

Returns the unique elements of the input tensor.

See torch.unique()

unique_consecutive(return_inverse=False, return_counts=False, dim=None)

Eliminates all but the first element from every consecutive group of equivalent elements.

See torch.unique_consecutive()

merlin.algorithms.kernels.build_slos_graph(m, n_photons, output_map_func=None, computation_space=None, no_bunching=None, keep_keys=True, device=None, dtype=torch.float32, index_photons=None)

Construct a reusable SLOS computation graph.

Return type:

SLOSComputeGraph

Parameters

mint

Number of modes in the circuit.

n_photonsint

Total number of photons injected in the circuit.

output_map_funccallable, optional

Mapping applied to each output Fock state, allowing post-processing.

computation_space : ComputationSpace, optional keep_keys : bool, optional

Whether to keep the list of mapped Fock states.

devicetorch.device, optional

Device on which tensors should be allocated.

dtypetorch.dtype, optional

Real dtype controlling numerical precision.

index_photonslist[tuple[int, …]], optional

Bounds for each photon placement.

Returns

SLOSComputeGraph

Pre-built computation graph ready for repeated evaluations.

merlin.algorithms.kernels.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.kernels.resolve_detectors(experiment, n_modes)

Build a per-mode detector list from a Perceval experiment.

Return type:

tuple[list[Detector], bool]

Args:

experiment: Perceval experiment carrying detector configuration. n_modes: Number of photonic modes to cover.

Returns:
normalized: list[pcvl.Detector]

List of detectors (defaulting to ideal PNR where unspecified),

empty_detectors: bool

If True, no Detector was defined in experiment. If False, at least one Detector was defined in experiement.

merlin.algorithms.kernels.resolve_photon_loss(experiment, n_modes)

Resolve photon loss from the experiment’s noise model.

Return type:

tuple[list[float], bool]

Args:

experiment: The quantum experiment carrying the noise model. n_modes: Number of photonic modes to cover.

Returns:

Tuple containing the per-mode survival probabilities and a flag indicating whether an effective noise model was provided.

merlin.algorithms.kernels.to_torch_dtype(dtype_like, *, default=None)

Convert common dtype representations (strings, numpy dtypes, torch dtypes) into torch dtypes.

Return type:

dtype

Args:

dtype_like: Input representation to convert. default: Fallback dtype if the representation is unknown. Defaults to torch.float32.

Returns:

torch.dtype corresponding to the requested representation.

Raises:

TypeError: If the value cannot be mapped and no default is provided.

Note

When the wrapped FeatureMap exposes a perceval.Experiment, fidelity kernels compose the attached perceval.NoiseModel (photon loss) before applying any detector transforms. The resulting kernel values therefore reflect both survival probabilities and detector post-processing.

Examples

Quickstart: Fidelity kernel in a few lines

import torch
from merlin.algorithms.kernels import FidelityKernel

# Build a kernel where inputs of size 2 are encoded in a 4-mode circuit
kernel = FidelityKernel.simple(
    input_size=2,
    n_modes=4,
    shots=0,                 # exact probabilities (no sampling)
    no_bunching=False,       # allow bunched outcomes if needed
    dtype=torch.float32,
    device=torch.device("cpu"),
)

# X_train: (N, 2), X_test: (M, 2)
X_train = torch.rand(10, 2)
X_test = torch.rand(5, 2)

K_train = kernel(X_train)               # (N, N)
K_test = kernel(X_test, X_train)        # (M, N)

Custom experiment with FeatureMap

import torch
import perceval as pcvl
from merlin.algorithms.kernels import FeatureMap, FidelityKernel

# Define a photonic circuit
circuit = pcvl.Circuit(6)
# Add whatever to the circuit...

# Define the Experiment
experiment = pcvl.Experiment(circuit)
# Add noise models, detectors, etc...
experiment.noise = pcvl.NoiseModel(brightness=0.9)
experiment.detectors[0] = pcvl.Detector.threshold()
experiment.detectors[5] = pcvl.Detector.ppnr(n_wires=3)

# Use the experiment to create a FeatureMap automatically
feature_map = FeatureMap.from_photonic_backend(
    input_size=0,
    experiment=experiment,
)

# Build the kernel with a specific input state
kernel = FidelityKernel(
    feature_map=feature_map,
    input_state=[2, 0, 2, 0, 2, 0],
    no_bunching=False,
)

X = torch.rand(8, 3)
K = kernel(X)  # (8, 8)

Use with scikit-learn (precomputed kernel)

import torch
from sklearn.svm import SVC
from merlin.algorithms.kernels import FidelityKernel

# Build kernel and compute Gram matrices
kernel = FidelityKernel.simple(input_size=4, n_modes=6)
K_train = kernel(X_train)
K_test = kernel(X_test, X_train)

# Train a precomputed-kernel SVM
clf = SVC(kernel="precomputed")
clf.fit(K_train, y_train)
y_pred = clf.predict(K_test)