Observables
Observables are quantities computed from simulation trajectories. During optimization, observables provide the measured values that are compared to experimental targets — the difference drives gradient-based parameter updates.
Observable API
All observables follow a simple callable interface:
observable(trajectory) → jnp.ndarray
Given a SimulatorTrajectory, an observable
returns a JAX array of shape (n_states, ...) containing the per-frame
measured values.
There are two observable patterns in mythos:
Single observables return a single array:
from mythos.observables.propeller import PropellerTwist
obs = PropellerTwist(
rigid_body_transform_fn=transform_fn,
h_bonded_base_pairs=jnp.array([[1, 14], [2, 13], [3, 12]]),
)
values = obs(trajectory) # shape: (n_states,)
Mapped observables return a dictionary of arrays, keyed by name. These are useful when a single simulation produces multiple related measurements (e.g., distances for every bond type in a Martini topology):
from mythos.observables.bond_distances import BondDistancesMapped
obs = BondDistancesMapped(topology=martini_topology)
values = obs(trajectory) # {"DMPC_NC3_PO4": array(...), "DMPC_PO4_GL1": array(...), ...}
BaseObservable
The BaseObservable class is the base for DNA/RNA structural observables. It
requires a rigid_body_transform_fn callable that converts rigid body
coordinates into nucleotide site positions, and provides helpers for computing
helical axes and duplex quartets.
See mythos.observables.base for the full API.
Martini observables (BondDistances, TripletAngles, membrane observables)
do not inherit from BaseObservable — they are standalone frozen dataclasses
that operate directly on Cartesian coordinates.
Using Observables in Loss Functions
Observables are typically paired with loss functions to produce scalar losses for optimization.
ObservableLossFn
The ObservableLossFn wrapper combines an observable with a loss function
in a single callable. Given a trajectory, it computes the observable value
(as a weighted sum over frames), then evaluates the loss against a target:
from mythos.losses.observable_wrappers import ObservableLossFn, RootMeanSquaredError
from mythos.observables.propeller import PropellerTwist
loss_fn = ObservableLossFn(
observable=PropellerTwist(
rigid_body_transform_fn=transform_fn,
h_bonded_base_pairs=h_bonded_pairs,
),
loss_fn=RootMeanSquaredError(),
return_observable=True, # also return the measured value
)
loss, measured = loss_fn(trajectory, target, weights)
The weights array controls how frames are aggregated — typically zeros for
equilibration frames and uniform weights for production frames:
eq_steps = 2000
prod_steps = 18000
weights = jnp.concat([
jnp.zeros(eq_steps),
jnp.ones(prod_steps) / prod_steps,
])
Available Loss Functions
SquaredError\((y_{\text{target}} - y_{\text{actual}})^2\)
RootMeanSquaredError\(\sqrt{\frac{1}{N}\sum_i (y_{\text{target},i} - y_{\text{actual},i})^2}\)
l2_lossStandalone function: \(\sum_i (y_{\text{actual},i} - y_{\text{target},i})^2\)
See mythos.losses for the full API.
Using Observables with Objectives
In the Optimization framework, observables can also be used inside
Objective and DiffTReObjective instances. In this context, the
observable is called on the trajectory exposed by a simulator, and the
objective’s grad_or_loss_fn computes gradients from the result.
For DiffTRe-based optimization, observables that support weighted
evaluation (via the weights argument) allow Boltzmann-reweighted loss
computation without re-running the simulation.
WassersteinDistance
The WassersteinDistance observable wraps another observable and computes
the 1D Wasserstein (Earth Mover’s) distance between its output distribution
and a fixed reference distribution. This is particularly useful for bottom-up
fitting where you want to match a full distribution shape rather than a single
scalar:
from mythos.observables.wasserstein import WassersteinDistance
obs = WassersteinDistance(
observable=my_bond_observable,
v_distribution=reference_distribution, # target distribution array
)
The mapped variant WassersteinDistanceMapped operates on multiple named
distributions simultaneously.
Available Observables
DNA / RNA Structural
Class |
Description |
Module |
|---|---|---|
|
Helical rise (Å) from quartet midpoint projections |
|
|
Average pitch angle (rad); pitch = π / ⟨angle⟩ |
|
|
Helical diameter from backbone distances |
|
|
Angle between hydrogen-bonded base normals |
|
|
Total twist in the X-Y plane |
|
|
Extension in the Z direction |
|
|
Persistence length from vector autocorrelation + linear fit |
|
|
SVD-aligned RMSE vs. a reference structure |
|
|
Melting temperature via umbrella sampling + histogram reweighting |
Membrane
Class |
Description |
Module |
|---|---|---|
|
Bilayer thickness via leaflet assignment |
|
|
Area per lipid via leaflet assignment |
|
|
Membrane Tm from sigmoid fit to APL vs. temperature |
Bonded / Angle
Class |
Description |
Module |
|---|---|---|
|
Per-bond distances (single or multi-bond mapped variant) |
|
|
Angles at central atom in triplets (single or mapped variant) |
Distance Metrics
Class |
Description |
Module |
|---|---|---|
|
1D Wasserstein (Earth Mover’s) distance to a reference distribution |
For the full API reference, see mythos.observables.