planteo
A typed representation for a problem after it has been read and before it is solved.
A narrative problem statement is ambiguous, under-specified and unit-bearing. Turning it into a solver model is usually done by writing code straight from the text, and that loses the two things that decide whether the result is right: which words produced which symbol, and what the words did not say.
planteo holds the object in between. It does not read natural language and it does not solve
anything. It defines the object, checks it, and emits it.
Why this exists
Across four different fields, the reported check is that the artifact ran, and the measured faithfulness is much lower:
| Field | What gets reported | What was measured |
|---|---|---|
| Optimization modelling | the solver reached the reference objective | objective correctness does not imply a correct model; compensating errors pass (survey) |
| Statement formalization | it compiles | a 3.0 to 29.0 point compile-to-faithfulness gap; the strongest agent compiled 89.5% and was faithful 60.5% (study) |
| Experiment design | the plan looks complete | every model tested is weak at datasets, baselines and metrics (benchmark) |
| Simulation modelling | the model runs | tools do well at qualitative work, badly at causal reasoning and quantitative fixes (benchmark) |
A model that runs is not a model that is right. This package makes the difference inspectable.
The two design commitments
Every quantity carries a dimension. Not optional, and dimensionless is a dimension that must be stated. Adding metres to seconds is rejected before a solver ever sees it, and a unit is compared by its exponent vector rather than by its label, so tonnes and kilograms are compatible while a string comparison would say otherwise.
Every element carries its provenance. A Span records the offsets and the text it covered, so
a document that has been stored or moved can be checked against its narrative rather than trusted. An
element with no span states that it was inferred, and why.
And one thing no surveyed representation carries: open_questions. When the narrative does not
determine something, the question is recorded with the span that raised it and the choice that was
taken, instead of being silently resolved.
Install
pip install planteo # the representation, zero dependencies
pip install "planteo[pyomo]" # plus the Pyomo emitter
Use
from planteo import (
Comparator, Compare, Dimension, Domain, Family, Narrative,
Objective, Problem, Product, Quantity, Ref, Role, Sense, Span, Sum, validate,
)
text = Narrative(
"A plant blends ore from two pits. Pit A costs 12 USD per tonne and pit B "
"costs 9 USD per tonne. Together they must deliver at least 100 tonnes. "
"Minimise the total cost."
)
TONNE = Dimension.of("t", mass=1)
PER_TONNE = Dimension.of("USD/t", currency=1, mass=-1)
problem = Problem(
narrative=text,
family=Family.OPTIMIZATION,
quantities=(
Quantity("x_a", Role.VARIABLE, TONNE, lower=0.0, span=Span.find(text, "Pit A")),
Quantity("x_b", Role.VARIABLE, TONNE, lower=0.0, span=Span.find(text, "pit B")),
Quantity("c_a", Role.PARAMETER, PER_TONNE, value=12.0),
Quantity("c_b", Role.PARAMETER, PER_TONNE, value=9.0),
Quantity("demand", Role.PARAMETER, TONNE, value=100.0),
),
relations=(
Compare(Sum((Ref("x_a"), Ref("x_b"))), Comparator.GE, Ref("demand"), name="meet_demand"),
),
objectives=(
Objective(Sense.MINIMISE, Sum((
Product((Ref("c_a"), Ref("x_a"))),
Product((Ref("c_b"), Ref("x_b"))),
)), name="total_cost"),
),
)
report = validate(problem)
assert report.ok, report
Emit it:
from planteo.emit import pyomo as emit
print(emit.emit_source(problem)) # a runnable Pyomo script, with provenance comments
model = emit.build_model(problem) # or a live ConcreteModel
The emitted source carries the narrative alongside the code, so a reader can check the formalization against the words:
# tonnes taken from pit A
# from the narrative: "Pit A"
model.x_a = pyo.Var(domain=pyo.Reals, bounds=(0.0, None)) # t
Compare two formalizations:
from planteo import compare
compare(mine, yours).verdict # Verdict.EQUIVALENT | Verdict.NOT_PROVEN_EQUIVALENT
There is deliberately no DIFFERENT verdict. Equal canonical form proves equivalence; unequal
canonical form proves nothing, and a vocabulary that pretended otherwise would produce confident
false negatives.
What it validates
- Dimensions. Every relation is checked term by term; both sides of a comparator must agree.
- Closure. No free symbol.
- Determinacy. Every quantity is given, chosen, derived by exactly one relation, or observed.
- Span integrity. Every span still covers the text it recorded.
- Family completeness. The family's required structure is present.
The validator reports every finding rather than stopping at the first, and each finding names the element it is about.
What it is not
- Not a translator. Producing a
Problemfrom text is the job of a harness; this defines the target. - Not a solver or a solver wrapper.
- Not a modelling language. It emits to Pyomo and MiniZinc rather than competing with them.
- Not an equivalence oracle. See the verdict vocabulary above.
Documentation
The wiki is in docs/. The design document that governs this package, written before any
code, is docs/design/SDD.md; every requirement in it names the test that
verifies it.
License
MIT. See LICENSE.
Release files for planteo 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| planteo-0.1.0.tar.gz | 34.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| planteo-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 65.6 kB
Release files / planteo-0.1.0.tar.gz
| Download URL | planteo-0.1.0.tar.gz |
|---|---|
| Size | 34.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a0f47d4c69ada37eeeb584e1db7f7467c122ae11ff6d6fae38a62c15da18ea37
|
|
BLAKE2b-256 checksum How to use checksums |
ad563a7819faa2a2cf829419a71ddae7a534d9943247adea4626c7916db885d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.
Transparency logRelease files / planteo-0.1.0-py3-none-any.whl
| Download URL | planteo-0.1.0-py3-none-any.whl |
|---|---|
| Size | 30.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cd6cd60244edbb27ecd37fb1b48aaf6b6e1706debcf9e7f627563e8bca415a0d
|
|
BLAKE2b-256 checksum How to use checksums |
980018cc57dec38a97c78e7d2bc67fa4dd241b9502e85f4d846dd9ac2d450033
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.
Transparency log