symop.devices.models.beamsplitters.beamsplitter

Path beamsplitter device.

This module defines BeamSplitter, a path-based two-port mixing device that prepares mode-pair information for backend execution.

For each matched pair of modes on the two input paths, the planning stage records a pair specification in action.params["pairs"] for the backend kernel. The physical two-mode unitary is not applied during planning.

The high-level device parameter theta is interpreted through t = cos(theta) and r = sin(theta). Therefore theta = pi / 4 corresponds to a balanced 50/50 beamsplitter.

Notes

The backend kernel is expected to realize the full beamsplitter rewrite, including creation of output-path modes. Planning is purely semantic and does not modify amplitudes or apply label edits.

Classes

BeamSplitter(theta[, phi_t, phi_r])

Ideal path beamsplitter device.

class BeamSplitter(theta: float, phi_t: float = 0.0, phi_r: float = 0.0) None

Bases: DeviceBase

Ideal path beamsplitter device.

A beamsplitter acts on matched mode pairs drawn from two input paths. Planning records the participating input modes, output paths, and beamsplitter parameters for backend execution.

The device uses the package Heisenberg convention, where creation operators transform as

\[\hat a^\dagger_{\mathrm{out},k} = \sum_j U_{k j}\,\hat a^\dagger_{\mathrm{in},j}.\]

The angle theta determines the transmission and reflection amplitudes by

\[t = \cos(\theta), \qquad r = \sin(\theta).\]

With phases phi_t and phi_r, the two-mode unitary is

\[\begin{split}U = \begin{pmatrix} t e^{i\phi_t} & r e^{i\phi_r} \\ -r e^{-i\phi_r} & t e^{-i\phi_t} \end{pmatrix}.\end{split}\]

Thus theta = pi / 4 gives a balanced 50/50 beamsplitter.

In this implementation, transmission corresponds to remaining on the same path index, while reflection corresponds to switching to the opposite path:

  • in0 -> out0: transmission

  • in0 -> out1: reflection

  • in1 -> out1: transmission

  • in1 -> out0: reflection, with the phase determined by the lower-left

unitary element

Thus a fully transmitting beamsplitter (theta = 0) leaves paths unchanged, while a fully reflecting beamsplitter (theta = pi/2) swaps the two paths.

Parameters:
  • theta (float) – Mixing angle of the beamsplitter. The transmission and reflection amplitudes are cos(theta) and sin(theta), respectively.

  • phi_t (float) – Transmission phase.

  • phi_r (float) – Reflection phase.

Examples

Create a balanced (50/50) beamsplitter and apply it to two paths:

>>> import numpy as np
>>> bs = BeamSplitter(theta=np.pi / 4)
>>> state_out = bs(
...     state_in,
...     ports={
...         "in0": Path("a"),
...         "in1": Path("b"),
...         "out0": Path("c"),
...         "out1": Path("d"),
...     },
... )

Notes

Planning does not relabel existing modes. The actual two-mode rewrite, including construction of output-path modes, is delegated to the runtime kernel through action.params["pairs"].

_abc_impl = <_abc._abc_data object>
apply(state: State, *, ports: Mapping[str, Path], selection: object | None = None, runtime: DeviceRuntime | None = None, ctx: ApplyContext | None = None, out_kind: Literal['ket', 'density'] | None = None) State

Apply the device to a state through a runtime.

Parameters:
  • state (State)

  • ports (Mapping[str, Path])

  • selection (object | None)

  • runtime (DeviceRuntime | None)

  • ctx (ApplyContext | None)

  • out_kind (Literal['ket', 'density'] | None)

Return type:

State

property kind: DeviceKind

Return the device kind identifier.

phi_r: float = 0.0
phi_t: float = 0.0
plan(*, state: State, ports: Mapping[str, Path], selection: object | None = None, ctx: ApplyContext | None = None) DeviceAction

Plan beamsplitter mixing on matched mode pairs.

Parameters:
  • state (State) – Input state whose mode labels are inspected.

  • ports (Mapping[str, Path]) – Mapping from device-port names to path labels. Must contain "in0", "in1", "out0", and "out1".

  • selection (object | None) – Optional selection object forwarded by the runtime. It is not used by this device.

  • ctx (ApplyContext | None) – Optional apply context forwarded by the runtime. It is not used by this device.

Returns:

Planned action containing:

  • params["pairs"]: tuple of beamsplitter pair specifications

  • edits: always empty for this device

Return type:

DeviceAction

Raises:
  • TypeError – If the state does not support path-based mode lookup.

  • ValueError – If both inputs are populated but contain incompatible numbers of modes.

Notes

The unitary itself is not applied here. This method only prepares semantic pairing and kernel parameters. If one side is missing, the backend kernel is expected to synthesize a matching vacuum partner.

property port_specs: tuple[PortSpec, ...]

Return the port specification of the device.

theta: float