Metadata-Version: 2.4
Name: latentplant
Version: 0.5.0
Summary: Learned world models for industrial process time series: action-conditioned latent dynamics, imagination rollouts with calibrated uncertainty, planning and regime-change detection
Author-email: Felipe Santibanez-Leal <fsantibanez@gmail.com>
License: MIT
Project-URL: Repository, https://github.com/fsantibanezleal/CAOS_LatentPlant
Keywords: world-models,latent-dynamics,model-based-rl,imagination,process-control,time-series,uncertainty-quantification,mineral-processing,rssm,planning
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.24
Requires-Dist: torch>=2.2
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Provides-Extra: onnx
Requires-Dist: onnx>=1.16; extra == "onnx"
Requires-Dist: onnxruntime>=1.18; extra == "onnx"
Provides-Extra: baselines
Requires-Dist: sysidentpy>=0.3; extra == "baselines"
Requires-Dist: pysindy>=1.7; extra == "baselines"
Dynamic: license-file

# latentplant

[![ci](https://github.com/fsantibanezleal/CAOS_LatentPlant/actions/workflows/ci.yml/badge.svg)](https://github.com/fsantibanezleal/CAOS_LatentPlant/actions/workflows/ci.yml)
[![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

**Learned world models for industrial process time series.** An action-conditioned model of
process dynamics that can be rolled forward in imagination, carries calibrated uncertainty, and
supports what-if queries, planning and regime-change detection.

## What a world model is here, and why it is not a forecaster

A forecaster maps history to a future. A world model maps history **and a proposed action
sequence** to a distribution over futures:

```python
rollout(obs_ctx, act_ctx, exo_ctx, act_fut, exo_fut, n_samples=64)
```

`act_fut` is an argument. That single difference is what makes counterfactual questions
expressible ("what if we raise the amine dose for the next four hours") and what makes planning
possible at all. A model that ignores its action input is a forecaster wearing a costume, so the
test suite asserts direction-of-effect on a system with known gain.

## The disciplines baked in

**Rollout-first evaluation.** One-step teacher-forced error is not a world-model metric: on any
autocorrelated process series it flatters a model that has learned nothing. Everything in
`latentplant.metrics` is indexed by horizon, and `break_even_horizon` reports where the model
stops beating persistence. That integer belongs next to every headline number.

**Two uncertainties, reported separately.** `uncertainty_split` returns aleatoric (process and
sensor noise, irreducible) and epistemic (the model does not know this region) as different
arrays. A noisy tag and an unfamiliar operating point look identical in a single band and mean
completely different things; the epistemic band is what a regime-change alarm should watch.

**Cadence is declared, not guessed.** An hourly lab assay carried on 20-second rows repeats about
180 times. A model trained without masking those repeats learns to copy the previous row and
scores beautifully. `PlantSchema` takes `update_seconds` per tag and `stale_mask` marks the
repeats so the loss can ignore them.

**Leakage-safe by construction.** Episodes never cross an acquisition gap, windows never cross an
episode, splits are chronological, and standardization is fitted on train only.

## Install

```bash
pip install latentplant                 # core: numpy + torch
pip install "latentplant[onnx]"         # ONNX export for a browser inference lane
pip install "latentplant[baselines]"    # sysidentpy / pysindy baseline adapters
```

## Quick start

```python
import numpy as np
from latentplant import (PlantSchema, TagSpec, ProbabilisticEnsemble,
                         split_on_gaps, make_windows, chronological_split)
from latentplant.metrics import rollout_report

schema = PlantSchema(
    tags=(
        TagSpec("silica_conc", "target", "pct", 0.0, 10.0, update_seconds=3600.0),
        TagSpec("pulp_level", "observation", "pct", 0.0, 100.0),
        TagSpec("amina_flow", "action", "m3/h", 0.0, 800.0),
        TagSpec("iron_feed", "exogenous", "pct"),
    ),
    row_seconds=20.0,
)

episodes = split_on_gaps(schema, values, timestamps)      # a plant stop ends an episode
windows = make_windows(schema, episodes, context=24, horizon=12)
train, val, test = chronological_split(windows)

wm = ProbabilisticEnsemble(n_obs=2, n_act=1, n_exo=1, n_members=5)
wm.fit(train, epochs=60, seed=0)

roll = wm.rollout(test.obs_ctx, test.act_ctx, test.exo_ctx,
                  test.act_fut, test.exo_fut, n_samples=64, seed=0)
lo, hi = roll.interval(0.9)
print(rollout_report(roll.samples, test.obs_fut, test.obs_ctx, test.mask_fut))
```

Counterfactual A/B from the same anchor:

```python
more, less = wm.counterfactual(ctx_obs, ctx_act, ctx_exo, action_a, action_b, exo_fut)
lift = more.mean - less.mean
```

On real plant data this supports direction-of-effect claims only: logged actions come from a
closed loop, so observational data does not identify interventions without assumptions. On a
simulator the ground truth exists and the imagination-to-reality gap is measured instead.

Plan against the model, and measure what the plan was worth:

```python
from latentplant import Planner, cvar_cost, target_tracking_cost, imagination_gap

planner = Planner(model=wm, bounds=action_bounds, horizon=24, method="cem")
plan = planner.plan(obs_ctx[:1], act_ctx[:1], exo_ctx[:1], exo_fut[:1],
                    cvar_cost(target_tracking_cost(target), alpha=0.2),
                    action_rate_penalty=2.0)

realized = env.execute(plan.actions)          # only possible where truth exists
print(imagination_gap(plan, realized, persistence_from=obs_ctx[0]))
```

Fix an overconfident model's intervals without retraining it:

```python
from latentplant import fit_conformal, calibration_report

conf = fit_conformal(cal_roll, cal.obs_fut, level=0.9, mask=cal.mask_fut)
print(calibration_report(conf, test_roll, test.obs_fut, mask=test.mask_fut))
```

## Documentation

Full docs in [`docs/`](docs/README.md): concepts (what a world model is, the plant contract,
windows and leakage), models (probabilistic ensemble, RSSM, ensembled RSSM), evaluation (rollout
metrics, conformal calibration) and planning (imagination, the imagination-to-reality gap).

## Status

0.03.000, alpha. Shipping now: the ingestion contract with the multi-rate observation operator,
episodes/windows/leakage-safe splits, the probabilistic ensemble (PETS-class, TS-inf
propagation), the vector RSSM, the ensembled RSSM with the aleatoric/epistemic split, the
rollout metric suite, split-conformal interval calibration, and CEM/MPPI planning with the
imagination-to-reality gap. On the roadmap: ONNX export for a browser inference lane, a
Gymnasium imagination env, and baseline adapters.

## Not to be confused with

[`phenoforge`](https://pypi.org/project/phenoforge/), the sibling engine, which fits and
ensembles **closed-form phenomenological equations**. `latentplant` **learns latent dynamics from
data**. Same industry, different object.

## References

The design follows: Chua et al. 2018, *Deep RL in a Handful of Trials with Probabilistic Dynamics
Models*, [arXiv:1805.12114](https://arxiv.org/abs/1805.12114) (probabilistic ensembles, TS-inf);
Lakshminarayanan et al. 2017, *Simple and Scalable Predictive Uncertainty Estimation using Deep
Ensembles*, [arXiv:1612.01474](https://arxiv.org/abs/1612.01474); Hafner et al. 2019, *Learning
Latent Dynamics for Planning from Pixels*, [arXiv:1811.04551](https://arxiv.org/abs/1811.04551)
(RSSM); Hafner et al. 2023, *Mastering Diverse Domains through World Models*,
[arXiv:2301.04104](https://arxiv.org/abs/2301.04104); Janner et al. 2019, *When to Trust Your
Model*, [arXiv:1906.08253](https://arxiv.org/abs/1906.08253) (rollout horizon and compounding
error); Che et al. 2016, *Recurrent Neural Networks for Multivariate Time Series with Missing
Values*, [arXiv:1606.01865](https://arxiv.org/abs/1606.01865) (cadence and masking); Romano et
al. 2019, *Conformalized Quantile Regression*,
[arXiv:1905.03222](https://arxiv.org/abs/1905.03222) (interval calibration); Williams et al.
2017, *Information Theoretic Model Predictive Control*,
[arXiv:1707.02342](https://arxiv.org/abs/1707.02342) (MPPI); Levine et al. 2020, *Offline
Reinforcement Learning: Tutorial, Review, and Perspectives*,
[arXiv:2005.01643](https://arxiv.org/abs/2005.01643) (why the planner refuses to issue
setpoints).

Developed by Felipe Santibanez-Leal. MIT licensed.
