1D Optogenetic Hill Circuit¶
Minimal single-species optogenetic gene circuit. One fluorescent protein X driven by a continuous light input U ∈ [0, 1] through a Hill production rate and linear degradation. Intended as a transparent sandbox for system-identification experiments.
Reaction network¶
Two Gillespie reactions:
Production:
∅ → Xwith ratek_prod · U^n / (K^n + U^n)Degradation:
X → ∅with ratek_deg · X
Steady state under constant U: ⟨X⟩_ss = k_prod · hill(U, K, n) / k_deg.
Action and observation¶
Action:
Box(low=0.0, high=1.0, shape=())— continuous light intensity.Observation: shape
(1,)—X / X_obs_normalizer.
Physics¶
Stochastic dynamics using the Gillespie algorithm for exact simulation.
Pure stateless physics for the 1D optogenetic circuit (opto_hill_1d).
Minimal single-species stochastic gene circuit driven by a continuous light input. Intended as a transparent sandbox for system-identification experiments — everything about the dynamics is controlled by four kinetic parameters and one continuous input.
- System Description:
Light Input (U ∈ [0, 1]) → X (fluorescent protein copy number)
Two Chemical Reactions: 1. Light-driven production: ∅ → X (rate: k_prod · hill(U, K, n)) 2. Protein degradation: X → ∅ (rate: k_deg · X)
Steady state at constant U: ⟨X⟩_ss = k_prod · hill(U, K, n) / k_deg
- class myriad.envs.bio.opto_hill_1d.physics.PhysicsState(time, X, next_reaction_time)[source]¶
Bases:
NamedTuplePure physical state of the 1D optogenetic circuit.
- Variables:
time (jax.jaxlib._jax.Array) – Current simulation time (minutes)
X (jax.jaxlib._jax.Array) – Fluorescent protein copy number (molecules, integer-valued)
next_reaction_time (jax.jaxlib._jax.Array) – Scheduled time of next reaction (minutes). Set to inf when no reaction is pending (sample fresh). Preserved across RL step boundaries for physical accuracy.
- time: Array¶
Alias for field number 0
- X: Array¶
Alias for field number 1
- next_reaction_time: Array¶
Alias for field number 2
- to_array()[source]¶
Convert to flat array for NN-based agents.
Note: next_reaction_time is excluded as it’s internal bookkeeping.
- Returns:
Array of shape (2,) with [time, X]
- Return type:
Array
- class myriad.envs.bio.opto_hill_1d.physics.PhysicsConfig(timestep_minutes=5.0, max_gillespie_steps=10000)[source]¶
Bases:
objectStatic structural constants for the 1D optogenetic circuit.
Passed as static_argnames to jit. Kinetic parameters (k_prod, K, n, k_deg) belong in PhysicsParams because they vary between cells and are the targets of system identification.
- __init__(timestep_minutes=5.0, max_gillespie_steps=10000)¶
- replace(**updates)¶
Returns a new object replacing the specified fields with new values.
- class myriad.envs.bio.opto_hill_1d.physics.PhysicsParams(k_prod=5.0, K=0.5, n=2.0, k_deg=0.05)[source]¶
Bases:
objectDynamic kinetic parameters — vmappable over parameter space.
Defaults yield a steady state near X ≈ k_prod / k_deg = 100 molecules at full light (U=1) and half-max response at U=0.5.
- __init__(k_prod=5.0, K=0.5, n=2.0, k_deg=0.05)¶
- replace(**updates)¶
Returns a new object replacing the specified fields with new values.
- class myriad.envs.bio.opto_hill_1d.physics.PhysicsParamsPrior(k_prod_loc=1.6094379124341003, k_prod_scale=0.0, K_loc=-0.6931471805599453, K_scale=0.0, n_loc=0.6931471805599453, n_scale=0.0, k_deg_loc=-2.995732273553991, k_deg_scale=0.0)[source]¶
Bases:
objectLog-normal prior over kinetic parameters.
Each parameter p is sampled as p ~ exp(Normal(loc, scale)). With scale=0 the distribution collapses to a point mass at exp(loc), so the default (all scales zero) is fully deterministic and backward compatible.
- __init__(k_prod_loc=1.6094379124341003, k_prod_scale=0.0, K_loc=-0.6931471805599453, K_scale=0.0, n_loc=0.6931471805599453, n_scale=0.0, k_deg_loc=-2.995732273553991, k_deg_scale=0.0)¶
- replace(**updates)¶
Returns a new object replacing the specified fields with new values.
- myriad.envs.bio.opto_hill_1d.physics.compute_propensities(state, action, params)[source]¶
Compute reaction propensities for the two reactions.
- Parameters:
state (PhysicsState) – Current physical state (time, X)
action (Array) – Continuous light intensity U ∈ [0, 1]
params (PhysicsParams) – Kinetic parameters — vmappable
- Returns:
Array of shape (2,) with propensities [r_production, r_degradation]
- Return type:
Array
- myriad.envs.bio.opto_hill_1d.physics.apply_reaction(state, reaction_idx)[source]¶
Apply a single reaction to update the state.
Uses jax.lax.switch for JAX-compatible control flow.
- Parameters:
state (PhysicsState) – Current physical state
reaction_idx (Array) – Index of reaction to apply (0 = production, 1 = degradation)
- Returns:
Updated physical state after reaction
- Return type:
- myriad.envs.bio.opto_hill_1d.physics.step_physics(key, state, action, params, config, previous_action, interval_start)[source]¶
Pure physics step using the Gillespie SSA.
Runs Gillespie simulation from current time until the end of the current interval (
interval_start + timestep_minutes).- Parameters:
key (Array) – RNG key for stochastic simulation
state (PhysicsState) – Current physical state
action (Array) – Continuous light intensity U ∈ [0, 1]
params (PhysicsParams) – Dynamic parameters
config (PhysicsConfig) – Static physics constants
previous_action (Array) – Action from previous timestep. If different from action, the pending reaction time is invalidated (propensities changed).
interval_start (Array) – Start time of current interval (
t * timestep_minutes).
- Returns:
Next physical state after simulating until interval end
- Return type:
System Identification Task¶
Zero-reward task. Parameters θ* = (k_prod, K, n, k_deg) live in SysIdTaskParams and persist across resets; only the cell state X resets between episodes. The inference objective is computed outside the environment.
System identification task for the 1D optogenetic circuit (opto_hill_1d).
The circuit runs with unknown kinetic parameters θ* = (k_prod, K, n, k_deg) stored in SysIdTaskParams. The agent observes the fluorescent protein copy number X(t) and selects continuous light intensities U(t) ∈ [0, 1]. Between episodes the cell state resets (X=0), but θ* is fixed — it is a property of the circuit, not the cell.
Reward is zero; the inference algorithm is the agent, and its objective (information gain or posterior entropy) is computed outside the environment.
- class myriad.envs.bio.opto_hill_1d.tasks.sysid.SysIdTaskState(physics, t, U)[source]¶
Bases:
NamedTupleState for the opto_hill_1d system identification task.
- Variables:
physics (myriad.envs.bio.opto_hill_1d.physics.PhysicsState) – Underlying stochastic physics state (time, X, next_reaction_time)
t (jax.jaxlib._jax.Array) – Timestep counter within the current episode (0 to max_steps)
U (jax.jaxlib._jax.Array) – Previous continuous light input — used by step_physics for pending-reaction invalidation when the action changes.
- physics: PhysicsState¶
Alias for field number 0
- t: Array¶
Alias for field number 1
- U: Array¶
Alias for field number 2
- class myriad.envs.bio.opto_hill_1d.tasks.sysid.SysIdTaskConfig(physics=<factory>, max_steps=288, X_obs_normalizer=100.0)[source]¶
Bases:
objectStatic configuration for the opto_hill_1d SysID task.
- physics: PhysicsConfig¶
- __init__(physics=<factory>, max_steps=288, X_obs_normalizer=100.0)¶
- replace(**updates)¶
Returns a new object replacing the specified fields with new values.
- class myriad.envs.bio.opto_hill_1d.tasks.sysid.SysIdTaskParams(physics=<factory>)[source]¶
Bases:
objectDynamic parameters for the SysID task — the unknown circuit parameters θ*.
These are vmappable: pass different SysIdTaskParams per-env to simulate a population of cells each with its own kinetic parameters.
- Variables:
physics (myriad.envs.bio.opto_hill_1d.physics.PhysicsParams) – Kinetic parameters (k_prod, K, n, k_deg) — targets of inference
- physics: PhysicsParams¶
- __init__(physics=<factory>)¶
- replace(**updates)¶
Returns a new object replacing the specified fields with new values.
- class myriad.envs.bio.opto_hill_1d.tasks.sysid.SysIdTaskParamsPrior(physics=<factory>)[source]¶
Bases:
objectPrior distribution over SysID task parameters.
- physics: PhysicsParamsPrior¶
- __init__(physics=<factory>)¶
- replace(**updates)¶
Returns a new object replacing the specified fields with new values.
- class myriad.envs.bio.opto_hill_1d.tasks.sysid.SysIdObs(X_normalized)[source]¶
Bases:
NamedTupleObservation for the opto_hill_1d SysID task.
Only the fluorescent protein copy number is observable.
- Variables:
X_normalized (jax.jaxlib._jax.Array) – X count divided by X_obs_normalizer
- X_normalized: Array¶
Alias for field number 0
- myriad.envs.bio.opto_hill_1d.tasks.sysid.step(key, state, action, params, config)¶
Step the circuit forward one interval using θ* from params.
- Parameters:
key (Array) – RNG key for Gillespie simulation
state (SysIdTaskState) – Current task state
action (Array) – Continuous light intensity U ∈ [0, 1] (scalar)
params (SysIdTaskParams) – Task parameters carrying θ*
config (SysIdTaskConfig) – Static task configuration
- Returns:
obs, next_state, reward (always 0.0), done, info
- Return type:
tuple[SysIdObs, SysIdTaskState, Array, Array, dict[str, Any]]
- myriad.envs.bio.opto_hill_1d.tasks.sysid.reset(key, params, config)¶
Reset to initial cell state (X=0). θ* is unchanged — it lives in params.
- myriad.envs.bio.opto_hill_1d.tasks.sysid.get_action_space(config)[source]¶
Continuous light intensity U ∈ [0, 1].
- myriad.envs.bio.opto_hill_1d.tasks.sysid.make_env(config=None, params=None, params_prior=None, **kwargs)[source]¶
Create an opto_hill_1d system identification environment.
- Parameters:
config (SysIdTaskConfig | None) – Static task config. If None, built from kwargs.
params (SysIdTaskParams | None) – Task params carrying θ*. If None, uses PhysicsParams defaults.
params_prior (SysIdTaskParamsPrior | None) – Optional prior for domain randomization. If set,
env.sample_params_fnwill sample distinct θ* per parallel env. Can also be triggered via flat kwargs (e.g.k_prod_scale=0.3).**kwargs – Forwarded to SysIdTaskConfig / PhysicsConfig / PhysicsParams / PhysicsParamsPrior via filter_kwargs.
- Returns:
Environment instance for the SysID task.
- Return type:
Environment[SysIdTaskState, SysIdTaskConfig, SysIdTaskParams, SysIdObs]
Example
>>> env = make_env() >>> env = make_env(k_prod_scale=0.3, K_scale=0.2)