DAG system

AtomTwin builds system models from a list of nodes — each node encodes the recipe for one physical quantity (a coupling, a detuning, a decay channel, a beam, …) together with its parametric dependencies. This list is called the computation graph, or DAG (directed acyclic graph), of the system.

How it works

When you call add_coupling!, add_detuning!, etc., AtomTwin creates one or more nodes and appends them to sys.nodes. Each node holds the physical parameters (which may be Parameter or ParametricExpression objects) and a reference to the compiled Dynamiq object it produces.

Node lifecycle

Every node goes through three stages:

StageFunctionWhen called
Buildbuild_node!(node, basis)At add_*! time, using default parameter values.
Compilecompile_node!(node, basis, rng, param_values)Once per compile call (per play). Resolves overrides and samples noise.
Recompilerecompile_node!(node, obj, rng, param_values)Once per Monte Carlo shot on the thread-local job copy.

Nodes are compiled in insertion order. Dependencies (e.g. BeamNode) must be added before the nodes that depend on them (e.g. the CouplingNode that reads the resolved beam).

Value resolution protocol

Any value stored inside a node - including values stored inside the node's sub-objects - follows the same two-method protocol:

  • _resolve_node_default(x): evaluates x using parameter defaults (called at build time)
  • _resolve_node_value(x, param_values, rng): evaluates x with override lookup and optional noise sampling (called at compile/recompile time)

This protocol is implemented for Number, Parameter, ParametricExpression, and extended by types such as GaussianPosition, MaxwellBoltzmann, MixedPolarization, and BeamRabiFrequency.


Node types

BeamNode

Resolves a ParametricBeam (or a concrete beam) to a concrete AbstractBeam at compile time. Must be added to sys.nodes before any CouplingNode that depends on it. Created automatically by add_coupling! when a beam argument is provided.


CouplingNode

Created by add_coupling! for uniform (spatially flat) couplings. Holds a parametric Rabi frequency Ω and maps to a GlobalCoupling Hamiltonian term.


NoisyCouplingNode

Created by add_coupling! when a LaserPhaseNoiseModel is supplied. Wraps the GlobalCoupling in a NoisyField so that per-shot phase noise is applied during the modifier loop in recompile!.


PlanarCouplingNode

Created by add_coupling! for position-dependent couplings driven by a PlanarBeam. Produces a PlanarCoupling Hamiltonian term.


DetuningNode

Created by add_detuning!. Holds a parametric detuning delta (rad/s) and maps to a Detuning diagonal Hamiltonian term.


DecayNode

Created by add_decay! and add_dephasing!. Holds a parametric rate Gamma and maps to a Jump Lindblad operator.


InteractionNode

Created by add_interaction!. Holds a parametric interaction strength V and maps to an Interaction two-atom Hamiltonian term.


Value types

Value types implement the resolution protocol and can be used anywhere a parametric quantity is accepted (e.g. as a field inside a node or as a constructor argument).

GaussianPosition

AtomTwin.GaussianPositionType
GaussianPosition

Position sampler that draws from a 3D Gaussian distribution. Each axis standard deviation may be a Number, Parameter, or ParametricExpression, enabling parametric position noise.

Implements _resolve_node_default (returns zeros(3)) and _resolve_node_value (samples position from the distribution).

Example

σ = Parameter(:σ_pos, 1e-6; std = 0.1e-6)
atom = Ytterbium171Atom(levels=[g,e], x_init = GaussianPosition(σ, σ, 0))
AtomTwin.gaussianFunction
gaussian(σx, σy, σz) -> GaussianPosition
gaussian(σ)          -> GaussianPosition(σ, σ, σ)

Construct a GaussianPosition sampler. Arguments may be Number, Parameter, or ParametricExpression.

MaxwellBoltzmann

AtomTwin.MaxwellBoltzmannType
MaxwellBoltzmann

Velocity sampler that draws from a Maxwell-Boltzmann distribution at temperature T. T may be a Number, Parameter, or ParametricExpression.

Implements _resolve_node_default (returns zeros(3)) and is resolved in initialize! using the atom's mass so that _resolve_node_value has access to m (required for σ = √(kB T / m)).

Example

T = Parameter(:T_mot, 1e-6; std = 0.1e-6)  # motional temperature
atom = Ytterbium171Atom(levels=[g,e], v_init = MaxwellBoltzmann(T))
AtomTwin.maxwellboltzmannFunction
maxwellboltzmann(; T) -> MaxwellBoltzmann

Construct a MaxwellBoltzmann velocity sampler. T may be a Number, Parameter, or ParametricExpression.

MixedPolarization

AtomTwin.MixedPolarizationType
MixedPolarization

A parametric polarization that models a dominant component with a small admixture of an orthogonal contamination. Used as the pol keyword argument to GeneralGaussianBeam to represent realistic polarization impurity.

The mixing amplitude ε may be a Number, Parameter, or ParametricExpression, enabling parametric sweeps or Monte Carlo sampling of the polarization extinction ratio. At compile time the polarization vector pol_main + ε * pol_perp is computed and normalized.

Example

epol = Parameter(:epol, 0.0; std = 1e-3)
pol  = MixedPolarization([0, 1im, 1]/√2, epol; k=[1,0,0])  # pol_perp = k × pol_main
pol  = MixedPolarization([1, 0, 0], [0, 1, 0], epol)        # explicit pol_perp
beam = GeneralGaussianBeam(λ, w0x, w0y, P, [1,0,0], pol; r0=r0)

Future derived-value types (e.g. BeamACStarkShift for light-shift detunings) follow the same pattern: a struct with _resolve_node_default and _resolve_node_value methods.

AtomTwin.estimate_PERFunction
estimate_PER(mp::MixedPolarization) -> Float64

Estimate the polarization extinction ratio (PER) from a MixedPolarization model, using the default value of the mixing amplitude ε.

PER = power in main polarization / power in contamination
    = 1 / |ε|²

Returns Inf for a pure polarization (ε = 0). For a stochastic ε (nonzero std), the default value gives the design-point PER; use Monte Carlo sampling over play(; epol = ...) calls to estimate the PER distribution.

estimate_PER(pol, pol_main) -> Float64

Estimate the PER from a concrete polarization vector pol relative to the intended polarization direction pol_main.

PER = |⟨pol_main | pol⟩|² / (|pol|² - |⟨pol_main | pol⟩|²)

Returns Inf if the polarization is pure (no orthogonal component).

estimate_PER(beam::ParametricBeam) -> Float64

Extract the MixedPolarization from a ParametricBeam and return its PER.

AtomTwin.estimate_PER_dBFunction
estimate_PER_dB(args...) -> Float64

Return the polarization extinction ratio in dB: 10 log₁₀(PER).