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:
| Stage | Function | When called |
|---|---|---|
| Build | build_node!(node, basis) | At add_*! time, using default parameter values. |
| Compile | compile_node!(node, basis, rng, param_values) | Once per compile call (per play). Resolves overrides and samples noise. |
| Recompile | recompile_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): evaluatesxusing parameter defaults (called at build time)_resolve_node_value(x, param_values, rng): evaluatesxwith 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.GaussianPosition — Type
GaussianPositionPosition 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.gaussian — Function
gaussian(σx, σy, σz) -> GaussianPosition
gaussian(σ) -> GaussianPosition(σ, σ, σ)Construct a GaussianPosition sampler. Arguments may be Number, Parameter, or ParametricExpression.
MaxwellBoltzmann
AtomTwin.MaxwellBoltzmann — Type
MaxwellBoltzmannVelocity 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.maxwellboltzmann — Function
maxwellboltzmann(; T) -> MaxwellBoltzmannConstruct a MaxwellBoltzmann velocity sampler. T may be a Number, Parameter, or ParametricExpression.
MixedPolarization
AtomTwin.MixedPolarization — Type
MixedPolarizationA 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_PER — Function
estimate_PER(mp::MixedPolarization) -> Float64Estimate 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) -> Float64Estimate 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) -> Float64Extract the MixedPolarization from a ParametricBeam and return its PER.
AtomTwin.estimate_PER_dB — Function
estimate_PER_dB(args...) -> Float64Return the polarization extinction ratio in dB: 10 log₁₀(PER).