C++ Module: inhomogeneousGeometricBrownianMotion

Executive Summary

The InhomogeneousGeometricBrownianMotion module is an abstract MuJoCo dynamics-task base class that implements a scalar inhomogeneous geometric Brownian motion (IGBM) stochastic process. It provides state registration, drift/diffusion setup, and parameter accessors. Derived classes define how the scalar stochastic state is mapped into output messages.

Unlike the additive-noise MeanRevertingNoise (Ornstein-Uhlenbeck) process, IGBM has multiplicative noise: the diffusion is proportional to the state, so the exact continuous process stays strictly positive when started positive with a positive mean. This makes it well suited to representing a multiplicatively-uncertain positive physical quantity (e.g. a density or a scale factor) that also mean-reverts. (See the note below on positivity under an explicit integrator.)

Module Description

The internal scalar state \(X_t\) follows the Itô stochastic differential equation

\[\text{d}X_t = \frac{1}{\tau}\,(\mu - X_t)\,\text{d}t + \sigma\,X_t\,\text{d}W_t\]

where:

  • \(\mu\) is the mean-reversion level (long-run mean)

  • \(\tau\) is the mean-reversion time constant [s]

  • \(\sigma\) is the volatility [s-1/2] of the multiplicative noise

  • \(\text{d}W\) is a Wiener process increment

Mirroring MeanRevertingNoise, the process is configured in stationary form: the user sets the mean level \(\mu\), the time constant \(\tau\), and the stationary standard deviation \(\sigma_{st}\). The SDE volatility is derived internally as

\[\sigma^2 = \frac{2}{\tau}\,\frac{\sigma_{st}^2}{\mu^2 + \sigma_{st}^2}\]

so that the stationary moments are exactly

\[\mathbb{E}[X_\infty] = \mu, \qquad \operatorname{Var}[X_\infty] = \sigma_{st}^2.\]

Because \(\sigma_{st}^2/(\mu^2+\sigma_{st}^2) < 1\), this parameterization automatically satisfies the IGBM stationarity condition \(2/\tau > \sigma^2\), so a stationary distribution always exists.

At each update, the base class computes the drift

\[\dot{X} = \frac{\mu - X}{\tau}\]

and configures one scalar diffusion source with the state-dependent amplitude

\[\sigma X\]

The base class then calls the virtual hook:

writeOutput(CurrentSimNanos, x)

which must be implemented by subclasses.

Assuming \(\mu > 0\) and \(X_0 > 0\), the exact continuous process stays strictly positive. The module integrates the SDE as written (pure math), so an explicit integrator carries no such guarantee: for a large time step or Wiener increment the numerically-integrated \(X\) can occasionally go non-positive. Consumers that need a non-negative quantity (e.g. a density) should clamp the value they derive from \(X\) (see C++ Module: igbmAtmDensity), rather than assume positivity here.

Message Interfaces

This base class does not define concrete input or output messages.

Subclasses are responsible for defining and publishing message interfaces in their writeOutput implementation.

Detailed Behavior

At each update step, the module performs the following operations:

  1. Ensures the scalar state was registered.

  2. Reads current state value \(X\).

  3. Sets the deterministic derivative \(\dot{X} = (\mu - X)/\tau\).

  4. Sets the scalar diffusion magnitude \(\sigma X\) for one noise source, with \(\sigma\) derived from the stationary-form parameters.

  5. Calls subclass writeOutput(CurrentSimNanos, x).

During state registration, the module:

  • registers a 1x1 state named inhomogeneousGBMState,

  • assigns one noise source,

  • initializes the state to the mean-reversion level \(\mu\) (a positive, stationary-consistent default, since \(x = 0\) is degenerate for the multiplicative diffusion; override with setStateValue if desired).


class InhomogeneousGeometricBrownianMotion : public StatefulSysModel
#include <inhomogeneousGeometricBrownianMotion.h>

Base class that provides a scalar inhomogeneous geometric Brownian motion (IGBM) state x and its stochastic evolution. Derived classes implement output handling.

The state x follows the Itô SDE

\[ \text{d}X_t = \frac{1}{\tau}\,(\mu - X_t)\,\text{d}t + \sigma\,X_t\,\text{d}W_t \]
so the drift mean-reverts toward \(\mu\) with time constant \(\tau\) while the diffusion \(\sigma X_t\) is multiplicative (proportional to the state, as in geometric Brownian motion). This process is also known as the GARCH diffusion or the mean-reverting geometric Brownian motion.

Like MeanRevertingNoise, the process is configured in stationary form: the user sets the mean level \(\mu\), the time constant \(\tau\), and the stationary standard deviation \(\sigma_{\text{st}}\). The SDE volatility is derived as

\[ \sigma^2 = \frac{2}{\tau}\,\frac{\sigma_{\text{st}}^2}{\mu^2 + \sigma_{\text{st}}^2} \]
so that the stationary moments are exactly
\[ \mathbb{E}[X_\infty] = \mu, \qquad \operatorname{Var}[X_\infty] = \sigma_{\text{st}}^2. \]
Because \(\sigma_{\text{st}}^2/(\mu^2+\sigma_{\text{st}}^2) < 1\), this parameterization automatically satisfies the stationarity condition \(2/\tau > \sigma^2\), so a stationary distribution always exists.

Assuming \(\mu > 0\) and the initial value \(X_0 > 0\), the exact continuous process stays strictly positive for all time. Note, however, that this base class integrates the SDE as written (pure math): an explicit stochastic integrator has no such guarantee, and for a large time step or a large Wiener increment a numerically-integrated \(X\) can occasionally cross zero. Consumers that require a non-negative quantity (e.g. a density) should clamp the value they derive from \(X\) downstream rather than assume positivity here.

The base class: 1) registers a 1-by-1 state with one noise source 2) computes drift and diffusion each step 3) calls the user hook writeOutput(CurrentSimNanos, x)

Public Functions

InhomogeneousGeometricBrownianMotion() = default

Default constructor. Parameters remain at defaults.

virtual void registerStates(DynParamRegisterer registerer) override

Register the scalar IGBM state with one noise source.

Parameters:

registerer – Simulation state registerer.

void UpdateState(uint64_t CurrentSimNanos) override

Update the IGBM state drift and diffusion, then call writeOutput.

Parameters:

CurrentSimNanos – Current simulation time in nanoseconds.

inline double getMean() const

Get the mean-reversion level \(\mu\).

void setMean(double m)

Set the mean-reversion level \(\mu\).

Parameters:

m – Long-run mean the process reverts toward. Positive expected.

inline double getStationaryStd() const

Get stationary standard deviation \(\sigma_{\text{st}}\).

void setStationaryStd(double s)

Set stationary standard deviation \(\sigma_{\text{st}}\).

Parameters:

s – Nonnegative expected.

inline double getTimeConstant() const

Get time constant \(\tau\) [s].

void setTimeConstant(double t)

Set time constant \(\tau\) [s].

Parameters:

t – Positive expected.

inline double getTheta() const

Get \(\theta = 1/\tau\) [s^{-1}].

inline double getSigma() const

Get the derived SDE volatility \(\sigma\) [s^{-1/2}] multiplying \(X_t\,\text{d}W_t\): \(\sigma = \sqrt{(2/\tau)\, \sigma_{\text{st}}^2/(\mu^2+\sigma_{\text{st}}^2)}\).

double getStateValue() const

Get the current scalar state x.

Throws:

std::runtime_error – if called before registerStates.

void setStateValue(double val)

Set the scalar state x.

Parameters:

val – New value. Must be strictly positive, since the multiplicative process is only well-posed for a positive initial state.

Throws:

std::runtime_error – if called before registerStates.

Public Members

BSKLogger bskLogger

Logger.

Protected Functions

virtual void writeOutput(uint64_t CurrentSimNanos, double x) = 0

Pure virtual output hook. Called every step after drift and diffusion are set. Use this to read inputs if needed and write outputs.

Parameters:
  • CurrentSimNanos – Current simulation time in nanoseconds.

  • x – Current scalar state value.

Private Members

StateData *xState = nullptr
double mean = 1.0

Mean-reversion level mu.

double sigmaStationary = 0.0

Stationary std dev of x.

double timeConstant = 1.0

Mean-reversion time constant in seconds.