merlin.builder.circuit_builder module

Circuit builder for constructing quantum circuits declaratively.

class merlin.builder.circuit_builder.ModuleGroup(modes)

Bases: object

Helper class for a group of circuit modes.

Parameters:

modes (list[int]) – Modes spanned by the grouped module.

class merlin.builder.circuit_builder.CircuitBuilder(n_modes)

Bases: object

Builder for quantum circuits using a declarative API.

Parameters:

n_modes (int) – Number of photonic modes available in the circuit.

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

Convenience method for angle-based input encoding.

Parameters:
  • modes (list[int] | None) – Optional list of circuit modes to target. Defaults to all modes.

  • name (str | None) – Prefix used for generated input parameters. Defaults to "px".

  • scale (float) – Global scaling factor applied before angle mapping.

  • subset_combinations (bool) – When True, generate higher-order feature combinations up to max_order.

  • max_order (int | None) – Optional cap on the size of feature combinations when subset_combinations is enabled. None uses all orders.

Returns:

self for fluent chaining.

Return type:

CircuitBuilder

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.

Parameters:
  • modes (list[int] | None) – 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 (bool) – Whether internal phase shifters should be trainable.

  • model (str) – "mzi" or "bell" to select the internal interferometer template.

  • name (str | None) – Optional prefix used for generated parameter names.

  • trainable_inner (bool | None) – Override for the internal phase shifters.

  • trainable_outer (bool | None) – Override for the output phase shifters.

Returns:

self for fluent chaining.

Return type:

CircuitBuilder

Raises:

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

add_memristive_ps(mode, update_rule, initial_state, name=None, detach_at_each_forward=True)

Add a memristive phase shifter that will update in regards to the update rule after each forward pass.

Parameters:
  • mode (int) – Circuit mode to target.

  • update_rule (Callable) – Callable with signature update_rule(state: torch.Tensor, output: torch.Tensor | StateVector | ProbabilityDistribution | PartialMeasurement) -> torch.Tensor. The update rule receives the current memristive state and the layer output, and must return a new state tensor of shape [batch_size].

  • initial_state (float) – The initial value of the phase shifter. This will be the value used after each reset() call.

  • name (str | None) – Prefix used for the generated memristive phase shifter parameter. Defaults to "mem".

  • detach_at_each_forward (bool) –

    Controls gradient flow through the memristive recurrence (state → new_state):

    • True (default): The new state is detached at each forward, preventing gradients from flowing through the recurrence chain. Earlier inputs receive zero gradients from the memristive state.

    • False: The new state retains gradients, allowing full gradient flow through the entire recurrence history. All inputs can receive gradients through the accumulated state chain.

Returns:

self for fluent chaining.

Return type:

CircuitBuilder

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.

Parameters:
  • modes (int | list[int] | ModuleGroup | None) – Single mode, list of modes, module group, or None to target all modes.

  • axis (str) – Axis of rotation for each inserted phase shifter.

  • trainable (bool) – Promote the rotations to trainable parameters.

  • as_input (bool) – Mark the rotations as input-driven parameters.

  • angle (float | None) – Optional fixed value for the rotations. Alias of value.

  • value (float | None) – Optional fixed value for the rotations. Alias of angle.

  • name (str | None) – Optional stem used for generated parameter names.

  • role (str | ParameterRole | None) – Explicit ParameterRole taking precedence over other flags.

Returns:

self for fluent chaining.

Return type:

CircuitBuilder

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.

Parameters:
  • targets (tuple[int, int] | list[tuple[int, int]] | None) – Explicit mode pairs. When omitted, nearest neighbours over modes or all modes are used.

  • depth (int) – Number of sequential passes to apply.

  • theta (float) – Baseline mixing angle for fixed beam splitters.

  • phi (float) – Baseline relative phase for fixed beam splitters.

  • trainable (bool | None) – Convenience flag to mark both theta and phi trainable.

  • trainable_theta (bool | None) – Whether the mixing angle should be trainable.

  • trainable_phi (bool | None) – Whether the relative phase should be trainable.

  • modes (list[int] | ModuleGroup | None) – Optional mode list or module group used when targets is omitted.

  • name (str | None) – Optional stem used for generated parameter names.

Returns:

self for fluent chaining.

Return type:

CircuitBuilder

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

Return metadata describing configured angle encodings.

Returns:

Mapping from encoding prefix to combination metadata.

Return type:

dict[str, dict[str, Any]]

build()

Build and return the circuit.

Returns:

Circuit instance populated with components.

Return type:

merlin.core.circuit.Circuit

classmethod from_circuit(circuit)

Create a builder from an existing circuit.

Parameters:

circuit (merlin.core.circuit.Circuit) – Circuit object whose components should seed the builder.

Returns:

A new builder instance wrapping the provided circuit.

Return type:

CircuitBuilder

property input_parameter_prefixes: list[str]

Expose the order-preserving set of input prefixes.

Returns:

Input parameter stems emitted during encoding.

Return type:

list[str]

to_pcvl_circuit(pcvl_module=None)

Convert the constructed circuit into a Perceval circuit.

Parameters:

pcvl_module (Any) – Optional Perceval module. If None, attempts to import perceval.

Returns:

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

Return type:

Any

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:

Trainable parameter stems discovered so far.

Return type:

list[str]

The different components of the builder with their arguments and example of code

Below are the components available in the CircuitBuilder class.

add_rotations

Adds one or multiple phase shifters across the specified modes.

Arguments:

  • modes (int | list[int] | ModuleGroup | None): Modes receiving the rotations. Defaults to all modes.

  • axis (str): Axis of rotation. Default: "z".

  • angle (float): Fixed rotation angle for non-trainable cases.

  • trainable (bool): Promote the rotations to trainable parameters.

  • name (str): Optional stem used for generated parameter names.

  • role (ParameterRole): Explicitly set the parameter role.

builder = CircuitBuilder(n_modes=6)
builder.add_rotations(modes=3, angle=np.pi / 4)
A Rotation component built with CircuitBuilder
builder = CircuitBuilder(n_modes=6)
builder.add_rotations(trainable=True, name="rotation")
A Rotation layer built with CircuitBuilder

add_angle_encoding

Adds angle-based input encoding to the circuit.

Arguments:

  • modes (list[int]): Modes to target for encoding.

  • name (str): Prefix for generated input parameters.

  • scale (float): Scaling factor for angle mapping.

builder = CircuitBuilder(n_modes=6)
builder.add_angle_encoding(modes=list(range(6)), name="input", scale=np.pi)

This will show as a rotation layer as data is encoded in phase shifters.

add_entangling_layer

Adds an entangling layer spanning a range of modes using a generic interferometer.

Arguments:

  • modes (list[int]): Modes to span.

  • trainable (bool): Whether the entangling layer is trainable.

  • model (str): Choose "mzi" (default) or "bell" to select the interferometer template.

  • name (str): Optional prefix for parameter names.

  • trainable_inner (bool | None): Override to control whether the internal phases of the MZIs remain trainable.

  • trainable_outer (bool | None): Override to control the output phases at the end of the interferometer.

builder = CircuitBuilder(n_modes=6)
builder.add_entangling_layer(trainable=True, name="U1")
An entangling layer built with CircuitBuilder

To span it on different modes:

builder = CircuitBuilder(n_modes=6)
builder.add_entangling_layer(trainable=True, name="U1", modes=[0, 3])

Switching to a Bell-style interferometer is as simple as:

builder.add_entangling_layer(model="bell", name="bell_block")
An entangling layer built with CircuitBuilder

add_superposition

Adds one or more beam splitters with optional depth.

Arguments:

  • targets (tuple[int, int] | list[tuple[int, int]]): Explicit mode pairs receiving beam splitters. When omitted, nearest neighbours across modes (or all modes) are used.

  • depth (int): Number of successive passes to apply.

  • theta (float): Mixing angle for fixed beam splitters.

  • phi (float): Relative phase for fixed beam splitters.

  • trainable (bool): Convenience flag marking both parameters trainable.

  • trainable_theta (bool): Whether the mixing angle is trainable.

  • trainable_phi (bool): Whether the relative phase is trainable.

  • modes (list[int] or ModuleGroup): Mode span used when targets is omitted.

  • name (str): Optional prefix for generated parameter names.

builder = CircuitBuilder(n_modes=6)
builder.add_superpositions(targets=(0, 1), trainable_theta=True, name="bs")
A Superposition component (beam splitter)
builder = CircuitBuilder(n_modes=6)
builder.add_superpositions(depth=2, name="mix")
An entangling layer of depth 2

add_memristive_ps

Adds a memristive phase-shifter.

Arguments:

  • mode (int): Circuit mode to target.

  • update_rule (callable): Update rule to change the angle of the phase shifter after each forward pass of associated the QuantumLayer object. The function must take two

    positional arguments: update_rule(state,output). Here is the expected signature: .. code-block:: python

    def update_rule(state: torch.Tensor,output: torch.Tensor | StateVector | ProbabilityDistribution | PartialMeasurement)-> torch.Tensor

    The update rule must also handle batch inputs and return a tensor of size [batch_size], just like the state parameter. The output will be the same as the corresponding QuantumLayer forward.

  • initial_state (float): The initial value of the phase shifter. This will be the value used after each reset() call.

  • name (str): Prefix used for the generated memristive phase shifter parameter. Defaults to "mem".

  • detach_at_each_forward (bool): Controls gradient flow through memristive state recurrence. When True (default), new states are detached after computation, blocking gradients through the state chain. When False, gradients flow through the entire state history. Default value is True.

Build

Finalizes and returns the constructed circuit.