This repository is a Maven harness that generates Lemma Specs at representative catalog scales and times cold load, evaluation, scoped updates, and snapshot restore through the Java engine. The logistics ladder fixtures here are byte-identical to those used by the engine’s published snapshot benchmarks. On release builds, an enterprise workspace (126000 rate cells, about 10 MiB of Spec text) cold-loads in about 32 s and restores from a snapshot in about 0.5 s; full tables and methodology are in the engine benchmarks reference. Recreate via the Java SDK with the commands in Run. Catalog sizing notes are in research/; a short hand-written Spec is in examples/.
The same logistics ladder can also be timed against Drools (--engine drools|both): same use case, native encoding in each product (Lemma catalog-as-Spec vs Drools catalog-as-facts). Results: RESULTS.md.
The work is licensed under Apache-2.0 (LICENSE).
- JDK 21 or newer
- Maven Wrapper (
./mvnw) com.lemmabase:lemma-engine0.9.10 (local~/.m2install from lemma tree)org.drools:drools-engine10.2.0 when using--engine drools|both
exec:java forks a dedicated JVM (fork=true). Default heap -Xmx2g. For the larger presets, pass -Dexec.jvmArgs="-Xmx8g".
Micro shapes (typically finishes in seconds; may share one JVM):
./mvnw -q exec:java -Dexec.args="--shape deep,wide,unless --rules 100,1000 --loads 3 --warmup 10 --evals 50"Logistics ingest / restore / eval (about half a minute per cold compile at the largest preset; multiply by --loads). Multi-size runs dispatch one fresh JVM per rate_cells:
./mvnw -q exec:java -Dexec.jvmArgs="-Xmx8g" \
-Dexec.args="--shape logistics --rules 1050,6300,18900,126000 --loads 3 --warmup 10 --evals 50"Scoped tip replan on the logistics workspace (Lemma-only). --rules is rate_cells:
./mvnw -q exec:java -Dexec.jvmArgs="-Xmx8g" \
-Dexec.args="--shape replan --rules 1050,6300,18900,126000 --loads 3"snapshot aliases logistics stages (compile builds a ready snapshot; restore loads it).
Lemma and Drools side by side (--engine both and/or multiple sizes → sequential child JVMs, one engine × one size each):
./mvnw -q exec:java -Dexec.jvmArgs="-Xmx8g" \
-Dexec.args="--engine both --shape logistics --rules 1050,6300,18900,126000 --loads 3 --warmup 10 --evals 50"Write generated Spec text without running the engine:
./mvnw -q exec:java -Dexec.args="--shape logistics --rules 1050 --generate-only --write target/generated-lemma"In the result tables, timings are milliseconds.
| Shape | --rules N |
Measures |
|---|---|---|
deep |
chain length | load / eval |
wide |
data + pair rules + fold | load / eval |
unless |
arms on one rule | load / eval |
logistics |
rate-cell preset (below) | ingest / restore / eval |
replan |
rate-cell preset (same as logistics) | tip Spec update vs full plan (lemma-only) |
snapshot |
same presets as logistics | same as logistics |
--rules (rate_cells) |
Profile | Contents |
|---|---|---|
| 1050 | ground | 1 carrier, Ground service (7x150 cells), ZIP3 zones, quote, and rate-shop |
| 6300 | carrier | 1 carrier, 6 services |
| 18900 | d2c | 3 carriers, 6 services |
| 126000 | enterprise | 20 contracts, 6 services |
Workspace Specs include zones_*, rates_*, accessorials, fuel_*, quote_*, and the terminal rule rate_shop.cheapest. See research/logistics-system-lookup-scale.md and research/logistics-in-lemma-syntax.md. Prices in generated Specs are synthetic. Both engines assert eval against a shared CatalogFixture oracle (Lemma exact cents; Drools ±0.01).
| Column | Lemma | Drools |
|---|---|---|
compile_ms |
Emit Spec → Engine.load → snapshot() |
Emit catalog facts → serialize fact artefact (pipeline DRL compiled once per JVM, untimed) |
restore_ms |
Engine.fromSnapshot |
Deserialize facts → newKieSession → insert catalog into WM |
eval_* |
Engine.run |
Insert shipment, fireAllRules, read seed Cheapest |
ready_bytes |
Snapshot size | Serialized catalog facts |
Drools keeps a fixed pipeline DRL (ground/air rate + cheapest; ~4 rules) at every ladder size. Catalog scale is RateCell / ZoneCell / lane facts in working memory, not rules.
Published logistics / replan / snapshot cells must not share a process with another engine, another rate_cells size, or Maven. The harness forks exec:java and, for --engine both or multiple --rules, dispatches sequential child JVMs (one engine × one size). Micro shapes may share a JVM.
Lemma-only. Same logistics workspace as --shape logistics, plus a light tip Spec. Measures min tip replan vs full plan:
- Full plan (catalog +
rate_shop+rate_tipV1) →compile_ms - Restore →
restore_ms update(rate_tip V2)— thin Spec thatusesone already-planned quote (cheapest: q.total + 100); catalog +rate_shopkept →upd_tip_ms- Identical tip update →
upd_tip_same_ms - Eval
rate_tip.cheapestafter tip update
Ideal tip would uses rate_shop, but engine panics on tip → rate_shop → quote data-target chains; tip→quote (same depth as rate_shop→quote) works.
| Column | Operation |
|---|---|
compile_ms |
Full logistics ingest + light tip |
restore_ms |
Engine.fromSnapshot |
upd_tip_ms |
update(rate_tip) with surcharge tip |
upd_tip_same_ms |
Identical tip update |
eval_after_tip_ms |
rate_tip.cheapest after tip update |
ok |
Matches expected (first quote total + 100) |
Micro shapes still report engine_load_ms = Engine.load of Spec text (no snapshot round-trip).
import com.lemmabase.lemma.Engine;
import com.lemmabase.lemma.Response;
import com.lemmabase.lemma.RuleResult;
import com.lemmabase.lemma.RunRequest;
import java.math.BigDecimal;
import java.util.Map;
try (Engine engine = Engine.create()) {
engine.load("""
spec order
data quantity: number
data unit_price: number
rule total: quantity * unit_price
""");
Response response =
engine.run(
RunRequest.of("order")
.data(
Map.of(
"quantity", 3,
"unit_price", new BigDecimal("19.99"))));
RuleResult.Number total = (RuleResult.Number) response.results().get("total");
BigDecimal amount = total.number();
}Use BigDecimal or integers for decimals; float and double are rejected. Override limits with Engine.create(ResourceLimits.builder()...). Engine serializes calls on an internal lock.
Java SDK timings for this harness are in RESULTS.md, including Drools side-by-side numbers.
Figures below are from the Lemma engine’s release snapshot bench (cargo benchmarks engine), same machine as the engine benchmarks doc, on fixtures byte-identical to SpecGenerator.logistics. Source of truth: engine benchmarks.
| Profile | Rate cells | Source | Load | Snapshot | Restore |
|---|---|---|---|---|---|
| ground | 1050 | 0.1 MiB | 147 ms | 0.9 MiB | 5.7 ms |
| carrier | 6300 | 0.5 MiB | 1.2 s | 5.0 MiB | 29 ms |
| d2c | 18900 | 1.5 MiB | 3.7 s | 14.9 MiB | 83 ms |
| enterprise | 126000 | 10.1 MiB | 32.3 s | 101.4 MiB | 537 ms |
Run the same ladder through this harness with --shape logistics or --shape snapshot and the --rules presets in Run.