merlin.models.qcnn module

Quantum convolutional neural network model definitions.

class merlin.models.qcnn.QCNNClassifier(input_shape, num_classes, stages=None)

Bases: Module

Quantum convolutional neural network classifier.

QCNNClassifier builds a trainable PyTorch model from a sequence of quantum convolution and quantum pooling stages followed by a quantum dense and a readout stage. The model is designed for square, single-channel image-like tensors. Each input image is amplitude-encoded into a two-photon StateVector: one photon occupies a mode in the first register, the other occupies a mode in the second register, and each pixel value scales the basis state identified by its row and column.

The resulting state vector is propagated through a torch.nn.Sequential pipeline. QConv stages apply trainable Perceval beam-splitter circuits independently to each register. Within a given register, all convolution windows share the same trainable parameter; the two registers keep distinct trainable parameters. QPool stages partially measure selected modes, reinsert measured photons in the reduced register, continue the remaining QCNN pipeline on each valid measurement branch, and combine branch logits weighted by their probabilities. The final QDense stage applies a dense trainable photonic circuit, maps output probabilities to a tensor, and feeds them to a classical linear readout that returns class logits.

Parameters:
  • input_shape (tuple | list) – Spatial shape of the input image, written as (height, width). The current implementation requires a positive square shape.

  • num_classes (int) – Number of output classes produced by the final readout layer.

  • stages (list[QCNNClassifier.QConv | QCNNClassifier.QPool | QCNNClassifier.QDense] | None) – Optional stage specification. If omitted, the classifier uses QConv(kernel_size=2, stride=2), QPool(kernel_size=2), then QDense(). Default is None. An empty list is invalid and raises ValueError.

num_classes

Number of output classes.

Type:

int

stages

User-provided stage specification.

Type:

list[QCNNClassifier.QConv | QCNNClassifier.QPool | QCNNClassifier.QDense] | None

layers

Executable PyTorch module containing the named quantum stages and readout. Individual layers can be inspected with integer indexing, for example classifier.layers[0].

Type:

torch.nn.Sequential

Raises:
  • TypeError – If input_shape is not a tuple or list of integers, if num_classes is not an integer, or if stages is neither None nor a list.

  • ValueError – If input_shape is not positive and square, if num_classes is not positive, or if stages does not satisfy the QCNN stage constraints.

class QConv(kernel_size, stride)

Bases: _Stage

Quantum convolution stage specification.

A convolution stage applies the same construction rule to both quantum registers. Each sliding window is represented by a trainable beam-splitter mesh spanning kernel_size adjacent modes, and windows advance by stride modes. The convolution windows within one register share a single trainable beam-splitter parameter.

Parameters:
  • kernel_size (int) – Number of adjacent modes included in each quantum convolution window.

  • stride (int) – Distance between consecutive convolution windows.

Raises:
  • TypeError – If kernel_size or stride is not an integer.

  • ValueError – If kernel_size or stride is not positive.

class QDense

Bases: _Stage

Dense quantum readout stage specification.

The dense stage is mandatory and must appear as the final QCNN stage. It applies a trainable beam-splitter circuit across all remaining modes before the classical readout layer.

class QPool(kernel_size)

Bases: _Stage

Quantum pooling stage specification.

A pooling stage partially measures one mode per pooling window in each register. Valid measurement branches are propagated through the remaining stages and recombined by their probabilities.

Parameters:

kernel_size (int) – Number of adjacent modes covered by each pooling window.

Raises:
  • TypeError – If kernel_size is not an integer.

  • ValueError – If kernel_size is less than or equal to 1.

class _Stage(type)

Bases: object

Base container for a QCNN stage.

Parameters:

type (QCNNClassifier._QCNNStageTypes) – Internal identifier of the stage kind.

amplitude_encode(x)

Encode image pixels as amplitudes of a two-photon state vector.

Pixel x[:, :, i, j] scales the basis state with one photon in mode i of the first register and one photon in mode input_shape[0] + j of the second register. The resulting state vector is normalized before being passed to the quantum layers.

Parameters:

x (torch.Tensor) – Input tensor with shape (batch_size, 1, input_shape[0], input_shape[1]).

Returns:

Normalized amplitude-encoded batch with shape (batch_size, basis_size). The basis size is determined by sum(input_shape) modes and two photons.

Return type:

merlin.core.state_vector.StateVector

build_conv_circuit(shape, stage)

Build a quantum convolution circuit for both registers.

The two registers remain separate: no beam splitter is added between modes belonging to different registers. Each register receives the same sliding-window convolution pattern. All convolution windows in the first register share px_first_register as their beam-splitter parameter, and all convolution windows in the second register share px_second_register.

Parameters:
Returns:

Perceval circuit implementing the convolution stage.

Return type:

pcvl.Circuit

build_dense_circuit(shape)

Build the dense quantum readout circuit.

The circuit is a trainable beam-splitter mesh spanning all remaining modes, following the dense QCNN construction presented by Monbroussou et al.

Parameters:

shape (tuple[int, int]) – Current dimensions of the two quantum registers.

Returns:

Perceval circuit used by the final quantum dense layer.

Return type:

pcvl.Circuit

build_qcnn_model()

Build the executable quantum-classical QCNN pipeline.

Each resolved stage is converted into a QuantumLayer. Quantum convolution layers return amplitudes so that later quantum stages can continue operating on state vectors. Quantum pooling layers return PartialMeasurement objects for branch processing. The final dense quantum layer returns probabilities that are consumed by a classical torch.nn.Linear readout.

Returns:

Sequential module containing all quantum stages followed by the linear readout.

Return type:

torch.nn.Sequential

Raises:

TypeError – If an unknown stage type is encountered while building the model.

build_single_conv(i, kernel_size, circuit, parameter)

Add one trainable convolution window to a circuit.

A single window is implemented as a down-and-up beam-splitter mesh so a photon can mix across the modes covered by kernel_size. Every beam splitter inserted by this window uses the supplied parameter so the caller can share trainable parameters across several windows.

Parameters:
  • i (int) – First mode of the convolution window.

  • kernel_size (int) – Number of modes covered by the window.

  • circuit (pcvl.Circuit) – Circuit to mutate with the trainable beam splitters.

  • parameter (pcvl.P) – Shared Perceval parameter used as the theta value for every beam splitter in the convolution window.

Returns:

Circuit with the added convolution window.

Return type:

pcvl.Circuit

export_config()

Export a serializable architecture configuration.

Weights are intentionally excluded. The returned dictionary can be used to reconstruct an equivalent QCNNClassifier architecture by rebuilding the stage objects from their serialized type entries.

Returns:

Serializable architecture metadata containing input_shape, num_classes, and the resolved stage list under stages.

Return type:

dict

forward(x)

Evaluate the classifier on a batch of images.

The input tensor is amplitude-encoded, propagated through the resolved quantum stages, and finally mapped to class logits by the readout layer. If a pooling stage is encountered, its measurement branches are handled by postprocess_pooling(), which continues the remaining stages on each valid branch and returns the probability-weighted logits.

Parameters:

x (torch.Tensor) – Input tensor with shape (batch_size, 1, input_shape[0], input_shape[1]). The second dimension is the channel dimension; only one channel is currently supported.

Returns:

Logits with shape (batch_size, num_classes).

Return type:

torch.Tensor

Raises:
  • TypeError – If the model pipeline does not return a tensor.

  • ValueError – If x does not have the expected rank, channel count, or spatial dimensions, or if the output logits do not have the expected shape.

classmethod from_config(config)

Build a classifier from export_config() metadata.

Parameters:

config (dict) – Serialized architecture metadata containing input_shape, num_classes, and stages.

Returns:

Reconstructed classifier with the same architecture.

Return type:

QCNNClassifier

Raises:

ValueError – If a serialized stage type is unknown.

property input_shape: tuple[int, int]

Validated input image shape.

postprocess_pooling(x, layer_index)

Process pooling measurement branches and combine their logits.

Each valid measurement branch is checked against the two-register QCNN constraints, measured photons are reinserted into the reduced state, and the branch is propagated through the remaining layers. Branch outputs are weighted by their measurement probabilities and summed.

Parameters:
  • x (PartialMeasurement) – Partial measurement returned by a quantum pooling layer.

  • layer_index (int) – Index of the next layer to execute after the pooling layer.

Returns:

Probability-weighted logits with shape (batch_size, num_classes).

Return type:

torch.Tensor

Raises:
  • TypeError – If a remaining pipeline branch does not return a tensor.

  • ValueError – If a branch state has an unexpected photon count, if branch logits or probabilities have incompatible shapes, or if a forbidden measurement outcome has non-zero probability.

recursive_forward(x, layer_index)

Continue the QCNN pipeline from a specific layer index.

This helper is used after pooling, where each valid measurement branch contains a separate state vector. The branch state is propagated through the remaining layers, and nested pooling stages are post-processed in the same way as in forward().

Parameters:
Returns:

Result produced by the remaining pipeline. In normal classifier use this resolves to logits with shape (batch_size, num_classes).

Return type:

torch.Tensor | merlin.core.state_vector.StateVector | PartialMeasurement

Raises:

TypeError – If a pooling layer does not return a PartialMeasurement, or if pooling post-processing does not return a tensor.

reinsert_photon(state_vector, reinsert_mode)

Insert one photon into a state-vector basis.

The operation maps amplitudes from the current basis to the basis with one additional photon while preserving the PyTorch computation graph. Amplitudes whose source states already contain a photon in reinsert_mode are skipped because inserting there would violate the QCNN branch structure.

Parameters:
Returns:

New state vector with the same number of modes and one additional photon.

Return type:

merlin.core.state_vector.StateVector

resolve_pooling_modes(shape, stage)

Resolve measured modes and output shape for a pooling stage.

The first mode of every pooling window is measured. A photon detected in that mode is reinserted into the following mode after the measurement branch is reduced, which preserves the two-photon QCNN representation for downstream layers.

Parameters:
Returns:

Measured modes, reinsertion modes, and the reduced register shape.

Return type:

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

Raises:

ValueError – If pooling would produce registers with different dimensions.

resolve_stages()

Validate the stage specification and return the executable sequence.

When no stage sequence is supplied, a default three-stage architecture is created. Custom stage sequences must end with exactly one dense stage, must use convolution kernels that fit the current register dimension, and must use pooling kernels that evenly reduce the current register dimension.

Returns:

Validated stage sequence used to build the QCNN.

Return type:

list[QCNNClassifier.QConv | QCNNClassifier.QPool | QCNNClassifier.QDense]

Raises:

ValueError – If the stage order or stage parameters are incompatible with the current register dimensions, or if an unknown stage type is present.

property resolved_stages: list[merlin.models.qcnn.QCNNClassifier._Stage]

Validated stage sequence used to build the model.

summary()

Return a concise, human-readable description of the architecture.

Returns:

String containing input shape, number of classes, and resolved stage sequence.

Return type:

str

to(*args, **kwargs)

Move the classifier and quantum-layer runtime state to a device or dtype.

verify_outcome(outcome, probabilities)

Validate whether a pooling measurement outcome is physically usable.

A valid QCNN pooling outcome measures at most one photon per register and only contains 0 or 1 occupation values. Forbidden outcomes are expected to have zero probability and raise a ValueError if they do not.

Parameters:
  • outcome (Sequence[int]) – Measured occupation pattern for the pooled modes.

  • probabilities (torch.Tensor) – Branch probabilities for every batch item.

Returns:

Whether the outcome can be propagated through the QCNN pipeline.

Return type:

bool

Raises:

ValueError – If outcome does not contain one measured pattern per register, or if a forbidden outcome has non-zero probability.