LawSynthGitHub

From a messy sensor CSV to a trusted, shareable model

This walkthrough takes a noisy, unevenly sampled two-channel sensor log all the way to a validated, exported, registered model. It chains: profile → prep → discover → explain → validate → backtest → ensemble → report → export → library. Everything is deterministic and offline.

We'll assume your log looks like this — a time column and two measured channels x and y:

time,x,y
0.00,10.03,4.98
0.04,10.41,4.70
0.11,10.79,4.55     # note: irregular timestamps
...

If you want a concrete file to follow along with, generate a clean synthetic one and treat it as your "sensor" (then imagine noise on top):

lawsynth new lotka-volterra --data sensor.csv --samples 400

1. Know your data first

Never model data you haven't looked at.

lawsynth profile sensor.csv --time time

Read the warnings block carefully — it flags too-few samples, irregular sampling, constant/degenerate columns, outliers, and missing values. Irregular timestamps and noise both corrupt the finite-difference derivatives discovery relies on, so if you see either, clean before modeling.

SDK equivalent:

import lawsynth
study = lawsynth.Study.from_csv("sensor.csv", time="time", state=["x", "y"])
print(study.profile().to_text())

2. Clean it — in a deliberate order

Trim the unusable warm-up, drop dead channels, smooth the noise, and re-grid onto a uniform step (operations apply in the order given):

lawsynth prep sensor.csv --time time --output clean.csv \
  --trim 0.5:18.0 \
  --drop-constant \
  --smooth-window 3 \
  --resample 0.05

The summary reports row/column counts and content fingerprints before and after, so the transformation is auditable.

SDK equivalent — prepare() returns a *new* study on cleaned data; the original is untouched:

clean = study.prepare(trim=(0.5, 18.0), resample_dt=0.05, smooth=3)

3. Discover — and prove cleaning helped

Discover on both raw and cleaned data and compare the fit:

raw_fit   = study.discover(recipe="ecology").explain().fit
clean_fit = clean.discover(recipe="ecology").explain().fit
for s in ("x", "y"):
    print(s, "raw R²", raw_fit[s]["r_squared"], "→ clean R²", clean_fit[s]["r_squared"])

CLI:

lawsynth discover clean.csv --time time --state x,y --output clean.lsworld --preset ecology

4. Read what it found

lawsynth explain clean.lsworld

explain gives a plain-language sentence per term ("x increases in proportion to x·y", …), the variables/parameters, and the dimensionality/complexity. In the SDK, clean.discover(...).explain().to_text() also prints per-state R² and RMSE.

5. Can I trust it? Three independent checks

Holdout skill (CLI):

lawsynth validate clean.lsworld --data clean.csv --time time --holdout 0.2

Look for a STRONG/GOOD verdict and a skill score that beats the persistence baseline.

Out-of-sample forecast decay (SDK rolling-origin backtest):

bt = clean.backtest(origins=5, horizon=40)
print(bt.verdict, "· mean R²", round(bt.mean_r_squared, 3), "· decay", round(bt.decay, 2))

Structural stability (SDK ensemble): which terms are robust vs. sampling artifacts?

ens = clean.discover_ensemble(n=16, fraction=0.8, seed=0, recipe="ecology")
print(ens.to_text())
print("robust terms:", [f"{t.target}<-{t.feature}" for t in ens.robust_terms()])

If validation is strong, backtest decay is gentle, and the ensemble's key terms are robust, you have a trustworthy world. If the ensemble shows a load-bearing term flickering in and out, collect more data or raise the threshold before you rely on it.

6. Share it — report, export, register

A self-contained HTML report with the fit overlay and residual strip:

lawsynth report clean.lsworld --data clean.csv --time time --output clean.report.html

Run-anywhere code exports (no LawSynth needed at the destination):

lawsynth export clean.lsworld --format python --output clean.py
lawsynth export clean.lsworld --format c      --output clean.c
lawsynth export clean.lsworld --format latex             # to stdout

Register the bundle with provenance so you can find it later and know exactly which data and config produced it:

lawsynth library add clean.lsworld --name sensor-model \
  --tags ecology,production --from-data clean.csv \
  --config "ecology preset, smoothed+resampled" --note "validated, R²>0.9"

7. Make the experiment reproducible

Track the discovery run so re-running the same config on the same data resolves to the same content-addressed id:

lawsynth discover clean.csv --time time --state x,y --output clean.lsworld \
  --preset ecology --track --label sensor-baseline
lawsynth runs list

Or fold the whole thing into one pipeline.toml (cookbook recipe 9):

lawsynth pipeline --example > sensor.pipeline.toml
# edit csv/state/outputs, add a [validate] section, then:
lawsynth pipeline sensor.pipeline.toml

What you built

A cleaned dataset, a discovered world you can read, three independent trust checks, a shareable report, portable code exports, and a provenance-tracked entry in your library — all reproducible from the same inputs.

See also