Getting started
LawSynth discovers governing equations from numeric time-series and writes them as an *executable world*: a small, portable .lsworld bundle you can inspect, simulate, analyze, and export. It is deterministic and offline — the same inputs always produce byte-identical outputs (see determinism.md), and nothing here touches the network.
This page builds and runs LawSynth end to end on a shipped dataset. Every command and every block of output below was produced by the current build; you can reproduce all of it with examples/run_all.sh.
Build the CLI
LawSynth is a Rust workspace. Build the command-line binary once:
cargo build -p lawsynth-cli
That produces target/debug/lawsynth. Add --release for an optimized build at target/release/lawsynth. If you already have a checked-out dependency cache you can build fully offline:
cargo build -p lawsynth-cli --offline
In the examples below lawsynth means "the compiled binary" — either put target/debug on your PATH, or invoke the binary by its full path. The verification script resolves the binary the same way the benchmarks do: $LAWSYNTH_BIN, then target/debug/lawsynth, then target/release/lawsynth, then cargo run --offline -p lawsynth-cli --.
Confirm the build:
lawsynth help
The core idea
Given a CSV of observations sampled over time, discover estimates each state's time derivative and fits a sparse law over a candidate library of terms (polynomials, and optionally trig/rational features). The result is a system of ordinary differential equations dx/dt = f(x) that you can roll forward with simulate. The whole pipeline is a fixed, seeded computation — no hidden randomness, no wall-clock reads.
A first dataset
This guide ships a deterministic dataset, examples/lotka-volterra.csv: a 200-sample predator–prey trajectory generated by LawSynth's own Lotka–Volterra template. You can regenerate it exactly with:
lawsynth new lotka-volterra --output /tmp/lv.lsworld --data lotka-volterra.csv --samples 200
created world: /tmp/lv.lsworld (2 state(s), 4 parameter(s), template 'lotka-volterra')
generated observations: lotka-volterra.csv (200 rows, step 0.05, columns time,x,y)
The CSV has a strictly increasing finite time column and two finite state columns, x (prey) and y (predator):
time,x,y
0.000000000000e0,1.000000000000e1,5.000000000000e0
5.000000000000e-2,9.545869105529e0,5.146442614078e0
CSV, TSV, and a numeric Parquet subset are all accepted (.csv, .tsv, .parquet).
Discover a world
lawsynth discover lotka-volterra.csv \
--time time --state x,y --preset ecology \
--output lotka-volterra.lsworld
discovered world: mse=3.251151e-6, complexity=18
solver: stlsq
--preset ecology seeds discovery with settings suited to predator–prey systems (degree-2 polynomial features with bilinear x*y cross terms, threshold 0.02). Explicit flags such as --degree, --threshold, and --solver always override a preset. Run lawsynth presets to see all presets and lawsynth discover --help for the full flag list. The candidate-library and sparsity contracts live in the template-priors and discovery-run specs.
Understand what it found
explain gives a plain-language and structured reading of the world:
lawsynth explain lotka-volterra.lsworld
World summary
2 state variable(s), 2 variable(s) total, 0 parameter(s)
dimensionality: 2-dimensional | total complexity: 18 AST node(s)
Laws
dx/dt = 1.09937 * x + -0.399798 * x * y
- x increases in proportion to x (rate 1.09937).
- x decreases in proportion to x * y (rate 0.399798).
reads: x, y
dy/dt = -0.399876 * y + 0.09999 * x * y
- y decreases in proportion to y (rate 0.399876).
- y increases in proportion to x * y (rate 0.09999).
reads: x, y
Variables
x state [dimensionless]
y state [dimensionless]
Parameters
(none)
Notes
Regime and dependency-hypothesis metadata are produced by
`discover --regimes` / `--causal` and are not stored in the world bundle.
The discovered law is the classic Lotka–Volterra system, dx/dt = 1.1·x − 0.4·x·y and dy/dt = −0.4·y + 0.1·x·y, recovered to within rounding of the generator's coefficients. inspect is the one-line structural summary:
lawsynth inspect lotka-volterra.lsworld
continuous world: 2 states, 2 variables, 0 parameters
Note:
discoverinlines fitted coefficients as constants, so a discovered world reports 0 parameters — the numbers live inside the law expressions, not as named parameters. Template worlds fromlawsynth newcarry named parameters instead.
Roll the world forward
simulate integrates the world from an initial state over a time span with a fixed step (classical RK4):
lawsynth simulate lotka-volterra.lsworld \
--initial x=10 --initial y=5 --start 0 --end 2 --step 0.5
time,x,y
0.00000000000000000e0,1.00000000000000000e1,5.00000000000000000e0
5.00000000000000000e-1,5.65127591640327775e0,6.03519841161347159e0
1.00000000000000000e0,2.88374771443967415e0,6.06930302659370557e0
1.50000000000000000e0,1.56084342004881460e0,5.52822818892534595e0
2.00000000000000000e0,9.61564303591452840e-1,4.81134687372857694e0
Output is a CSV trajectory on stdout with full 17-digit precision, so it can be diffed byte-for-byte across runs and machines. The solver, time-grid, and initial-state semantics are pinned by the simulation-contract spec.
Next
- workflow.md — the full pipeline: discover → simplify →
stability → control → export → uncertainty, each a real command with real output.
- determinism.md — why re-running discovery yields a
byte-identical world, demonstrated with cmp.
- README.md — index and pointers to the boundary
specs/.