Measurement Strategy Guide

The type of output from a QuantumLayer depends on the measurement strategy. The quantum-to-classical conversion relies on two orthogonal concepts:

The quantum-to-classical conversion can also be handled by the user with a typed object being returned by a forward() call. Take a look at merlin.algorithms.layer module for more details about returned typed objects per measurement strategy.

PROBABILITIES

Produces the probability of observing each Fock state. In the case of strong simulation (as detailed in the SLOS: Strong Linear Optical Simulator documentation), the SLOS algorithm returns complex amplitudes, which are internally converted into probabilities using the standard quantum operation: \(a \rightarrow |a|^2\).

The output vector contains real numbers and has a size of combination(m + n - 1, n) in the Fock space (many photons can arrive at the same mode), combination(m, n) in the unbunched space, and \(2^{(m/2)}\) in the dual rail computation space where n is the number of photons and m the number of modes. See Computation Spaces for more details on computation spaces.

builder = CircuitBuilder(n_modes=5)
# Add whatever to your builder

quantum_layer = QuantumLayer(.
    input_size=4,
    builder=builder,
    n_photons=2,
    measurement_strategy=MeasurementStrategy.probs(),  # Optional because this is its default value
)

Characteristics:

  • Output shape matches the number of Fock states generated by the experiment.

  • Values are real, non-negative, and sum to one.

  • Ideal to exploit nearly all the quantum layer expressivity.

Return type (forward call):

  • return_object=False: torch.Tensor

  • return_object=True: merlin.ProbabilityDistribution

Extensions:

  • Add torch.nn.Linear or any classical layer after the quantum layer to map the probabilities to logits/regression targets.

  • Combine with LexGrouping or ModGrouping when you need fewer output features and don’t want to increase the number of parameters.

MODE_EXPECTATIONS

The output vector is real and has a size equal to the number of modes. Each element represents the expected number of photons in the corresponding mode. In the unbunched computation space, the vector instead provides the probability of detecting at least one photon in each respective mode.

builder = CircuitBuilder(n_modes=5)
# Add whatever to your builder

quantum_layer = QuantumLayer(.
    input_size=4,
    builder=builder,
    n_photons=2,
    measurement_strategy=MeasurementStrategy.mode_expectations(),
)

Use mode_expectations if you only need an m-sized vector that summarizes the quantum layer output by providing expectations values.

Key properties:

  • ComputationSpace.FOCK returns the expected photon count per mode.

  • ComputationSpace.UNBUNCHED reports occupancy probabilities per mode.

  • Output size always equals the number of modes.

Return type (forward call):

  • return_object=False: torch.Tensor

  • return_object=True: torch.Tensor

AMPLITUDES

Returns the complex amplitudes directly. The output size is the same as with MeasurementStrategy.probs(), which is dependent on the computation space. This strategy is only meaningful in simulation, because amplitudes cannot be measured or obtained on hardware. Indeed, returning amplitudes is physically non-realizable and should mainly be used for connecting two quantum layers (e.g. when the second one uses amplitude encoding) or for analytical purposes when studying quantum systems.

builder = CircuitBuilder(n_modes=5)
# Add whatever to your builder

quantum_layer = QuantumLayer(.
    input_size=4,
    builder=builder,
    n_photons=2,
    measurement_strategy=MeasurementStrategy.amplitudes(),
)

Use this strategy for debugging, algorithmic research, or when a classical routine manipulates complex amplitudes directly. The output is a complex tensor normalised to unit norm.

Return type (forward call):

  • return_object=False: torch.Tensor

  • return_object=True: merlin.StateVector

Note

Adding photon loss or detectors corresponds to performing a measurement of the quantum state, which collapses it and is therefore incompatible with amplitude retrieval. Thus, this measurement strategy requires that no noise model or detectors are defined.

PARTIAL MEASUREMENT

Partial measurement is a specific kind of measurement strategy. With this measurement strategy, only some modes are measured while amplitudes are preserved on the other modes. For more details, checkout the Partial Measurement page.

Photon Loss-aware & Detector-aware execution

When a pcvl.Experiment is provided, the QuantumLayer derives a photon loss transform and a detector transform that remaps raw Fock-state probabilities to the classical outcomes defined by the experiment. The photon loss mapping is applied first. Then, the detector mapping is applied. Lastly, the measurement strategy converts the distribution into classical features. As a consequence:

  • MeasurementStrategy.probs() and MeasurementStrategy.mode_expectations() transparently work with any noise model and detector setup.

  • MeasurementStrategy.amplitudes() requires direct access to the complex amplitudes and therefore cannot be used when a noise model or custom detectors are defined (the layer will raise a RuntimeError).

Computation space and grouping

The measurement strategy is now a first class object that contains all of the informations of the output of the layer for SLOS optimizations. The MeasurementStrategy now gets two arguments in input.

  • computation_space: A ComputationSpace object that defines the output computation space.

    It will be the only way to define the computation space as the no_bunching flag is deprecated.

    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.

  • grouping: The grouping strategy to use between LexGrouping and ModGrouping. By default, no grouping is applied.

Returning typed objects

When return_object is set to True in the constructor of QuantumLayer, the output of a forward() call depends of the measurement_strategy. By default, it is set to False. See the following output matrix to see 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 are interoperable with Perceval, enabling seamless interaction between the two libraries.

For more information on the typed output capabilities, follow the following links:

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, ProbabilityDistribution
from merlin.measurement.strategies import MeasurementStrategy

circuit = ML.CircuitBuilder(n_modes=4)
circuit.add_entangling_layer()

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(
    builder=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)

Migrating from OutputMappingStrategy (legacy)

Earlier releases exposed QuantumLayer through OutputMappingStrategy. Newc code should rely on MeasurementStrategy instead. The mapping is:

  • OutputMappingStrategy.NONEMeasurementStrategy.probs()

  • OutputMappingStrategy.LINEARMeasurementStrategy.probs() followed by a torch.nn.Linear layer

  • OutputMappingStrategy.LEXGROUPING or GROUPINGMeasurementStrategy.probs() + LexGrouping

  • OutputMappingStrategy.MODGROUPINGMeasurementStrategy.probs() + ModGrouping

Selection Cheat Sheet

Scenario

Recommended Pipeline

Notes

Classification / Regression

PROBABILITIES + classical linear-probing head

Obtain logits or arbitrary ranges with nn.Linear or deeper networks.

Structured probability outputs

PROBABILITIES + LexGrouping / ModGrouping

Preserves probability mass while reducing dimensionality.

Hardware-friendly analytics

MODE_EXPECTATIONS (ComputationSpace.UNBUNCHED to taste)

Outputs one feature per mode, easy to interpret.

Algorithm debugging

AMPLITUDES

Complex amplitudes, simulation only.

Next steps