$$ % Define your custom commands here \newcommand{\bmat}[1]{\begin{bmatrix}#1\end{bmatrix}} \newcommand{\E}{\mathbb{E}} \newcommand{\P}{\mathbb{P}} \newcommand{\S}{\mathbb{S}} \newcommand{\R}{\mathbb{R}} \newcommand{\S}{\mathbb{S}} \newcommand{\norm}[2]{\|{#1}\|_{{}_{#2}}} \newcommand{\pd}[2]{\frac{\partial #1}{\partial #2}} \newcommand{\pdd}[2]{\frac{\partial^2 #1}{\partial #2^2}} \newcommand{\vectornorm}[1]{\left|\left|#1\right|\right|} \newcommand{\abs}[1]{\left|{#1}\right|} \newcommand{\mbf}[1]{\mathbf{#1}} \newcommand{\mc}[1]{\mathcal{#1}} \newcommand{\bm}[1]{\boldsymbol{#1}} \newcommand{\nicefrac}[2]{{}^{#1}\!/_{\!#2}} \newcommand{\argmin}{\operatorname*{arg\,min}} \newcommand{\argmax}{\operatorname*{arg\,max}} \newcommand{\dd}{\operatorname{d}\!} $$

models.types

models.types

Core datatypes: model state, parameters, and daily inputs.

All three are equinox.Module subclasses, which makes them JAX pytrees. That buys us three things for free: jax.grad differentiates through them, jax.lax.scan carries them, and jax.vmap maps over them – so the same structures serve simulation, filtering, and optimization without conversion.

Conventions

Mass is kilograms, energy is kilocalories, time is days. Pounds appear only at the I/O boundary. Every state and parameter is a scalar array so that the same code runs on a single trajectory or, under vmap, on a cloud of sigma points.

Classes

Name Description
DailyInput Exogenous inputs for a single day, held constant across that day.
Params Personal metabolic parameters, estimated jointly with the state.
State Hidden physiological state.

DailyInput

models.types.DailyInput()

Exogenous inputs for a single day, held constant across that day.

Holding inputs constant over each day makes the one-day step map an exact zero-order-hold discretisation of the continuous dynamics, which is both the physically sensible reading of a daily food log and the numerically convenient one.

The two *_known flags are floats rather than booleans so the structure stays a homogeneous pytree that vmap and scan handle without special-casing.

Attributes

Name Description
carb_dev_g Carbohydrate intake relative to the reference (habitual) intake, g/day.
intake_known 1.0 when a usable food log exists for this day, else 0.0.
intake_sd_kcal Uncertainty of a logged day’s energy, kcal: zero unless the day is
macro_known 1.0 when the protein/carb/fat split is available, else 0.0.
sodium_dev_mg Sodium intake relative to the reference (habitual) intake, mg/day.

Params

models.types.Params()

Personal metabolic parameters, estimated jointly with the state.

Every field is in natural units and positivity- or range-constrained where physically required. The filter works in an unconstrained reparameterization (see wdm.estimation.reparam) and converts back here, so these values are always physically admissible regardless of what the optimizer does.

The thermic-effect coefficients are deliberately not estimated. Their influence on the scale is confounded with maintenance energy, and attempting to identify them from weight data alone yields nonsense with wide error bars dressed up as a result.

Attributes

Name Description
at_gain Dimensionless, in [0, 1). Fraction of a sustained deficit eventually
at_tau days. Time constant of adaptation.
gly_gain kg per (g/day). Steady-state glycogen-complex mass per unit
gly_tau days. Glycogen repletion time constant.
na_gain kg per (mg/day). Steady-state water retention per unit sodium
na_tau days. Sodium-water time constant.
partition_kappa Shift in the fat fraction of tissue change per (g/kg/day) of protein
rmr_intercept kcal/day. Mass-independent expenditure: organ metabolism, baseline
rmr_slope kcal/day per kg. How expenditure scales with body mass. Strongly

Methods

Name Description
prior Prior-centered parameters, given an expenditure prior from wdm.config.
prior
models.types.Params.prior(rmr_intercept, rmr_slope)

Prior-centered parameters, given an expenditure prior from wdm.config.

State

models.types.State()

Hidden physiological state.

Attributes

Name Type Description
tissue_kg Scalar Slow-moving body mass: fat plus fat-free solids and their structural water. This is what a diet actually changes, and it is not what the scale reads.
fat_kg Scalar Fat mass, a component of tissue_kg. Only weakly observable from scale data – it influences the dynamics solely through the effective energy density of tissue change – so its posterior stays wide. Reported with error bars for exactly that reason.
gly_water_kg Scalar Deviation of the glycogen-plus-bound-water compartment from its level at the habitual carbohydrate intake. Fast, carbohydrate-driven, and responsible for a large share of day-to-day scale movement.
na_water_kg Scalar Deviation of sodium-associated extracellular water from its level at the habitual sodium intake. Zero throughout when no sodium column is supplied.
adapt_kcal Scalar Adaptive thermogenesis: the reduction in daily expenditure beyond what mass loss alone explains. Negative during an energy deficit. This is the state that produces plateaus.

Functions

Name Description
inputs_from_episode Convert a loaded ~wdm.io.Episode into stacked daily inputs.
previous_day_inputs Re-index daily inputs so that entry k holds day k - 1’s.

inputs_from_episode

models.types.inputs_from_episode(ep)

Convert a loaded ~wdm.io.Episode into stacked daily inputs.

Every field becomes an array of length ep.n_days, so the result is a pytree that jax.lax.scan can iterate over directly. Carbohydrate and sodium are converted to deviations from the episode’s reference intake (habitual eating; see ~wdm.io.Episode) here, which is the one place that convention is applied.

Timing: row k holds the weigh-in on the morning of day k and the food eaten during day k, after it. The state on day k is the morning state, so day k’s food moves the state from morning k to morning k + 1; see previous_day_inputs for the filter’s view.

NaN energy on unlogged days is replaced by zero; the value is never used because ~wdm.models.energetics.effective_intake selects the fallback whenever intake_known is zero. Leaving the NaN in place would poison the gradient through the unused branch of the where, a classic and genuinely irritating JAX foot-gun.

previous_day_inputs

models.types.previous_day_inputs(u)

Re-index daily inputs so that entry k holds day k - 1’s.

A predict-then-update filter step k advances the state from morning k - 1 to morning k, which is driven by the food of day k - 1. Entry 0 repeats day 0 and is never used: the filter’s first step is an update only (predict_first=False).