Skip to content

Glossary

The one definition of each name this project uses. The rest hang off one distinction:

A spec is what you write: the math, with no data. A model is a spec with your data attached, a Model in Python. A result is one answer read back.

spec ──▶ build ──▶ Model ──▶ solve ──▶ Result
 │       (+data)                     (one answer)
 └─────▶ check ──▶ Program
                   (the plan, for reading)

The chain

Spec
Short for specification. The math before any data: a YAML file, a mapping, or a Spec from mathspec.to_spec. It carries no numbers, and every verb takes it first. A Spec carries its own program, so one handed back to a verb is not read again. What it may contain is the language.
Program
The spec lowered to the plan a build reads its rows off: what check returns, still with no data, for reading the plan. No verb takes one back: lowering has no inverse, so keep the Spec (the spec argument). The two states are the language's (Spec and Program).
Formulation
A block that states rows nothing lowers, piecewise: today. Every verb refuses a spec still carrying one; to_spec(spec).expand('piecewise') or .expand() writes it out first (the spec argument).
Model
A spec with data attached, the language's own meaning of the word (glossary). These docs use it in no other sense. specsolve.Model, what build returns, is one. One Model feeds any sink through solve() or write(path); row(...) and diagnostics() read it without solving. update(...) puts new numbers on it in place.
Result
One answer read back from a solve: objective, primal(name), dual(name), evaluate(expression) and the rest of reading a result. It owns its tables, so it outlives its model.
Answer
What came back, whichever verb asked: a Result for one solve, a Sweep for a sweep. save writes one as a directory — record.parquet for how it terminated, then primal/, dual/, activity/ and expression/ — and an archive holds that directory as answer/.
Archive
A spec, the data it was solved with and what came back, written together as one zip or one directory by archive= (archiving). It reads back as a SolveArchive, or a SweepArchive where the sources were cut. Its run is the archive's own name, stamped into the answer when it is written. Never "artifact".
Digest
A hash that says whether two things are the same input. spec_digest names the document an answer came from, and an archive whose answer names another is refused; archive.source_digests names each data member, so two archives of one spec say which input moved.

The verbs

check · build · solve · write
check(spec) validates and lowers; check(spec, sink) also asks whether that sink takes it. build(spec, sources) returns a Model. solve and write build and then solve or stream in one call. There is no Python API for constructing a spec.
evaluate
evaluate(spec, sources, expression) values one expression of a spec that declares no variables: arithmetic on the attached data, no solver. result.evaluate(expression) is the same read at a solution.
update
model.update(sources) puts new numbers on a built model in place, naming only what changed. A change that moves a mask rebuilds and solves cold.
load · scan
The two ways a saved answer is read back. load_result, load_sweep and load_archive read whole, so the directory is free afterwards. scan_result, scan_sweep and scan_archive read each frame at the call that asks for it, so the files have to outlive the value (loading or scanning). Never "open".
Buildable
The type alias for a spec argument: str | Path | Mapping | Spec. The lowered Program that check returns is not one.
Source
The type alias for one value of sources. The shapes it covers are the data contract.
Label
One member of a dimension, wind say, and its type alias: int | float | str | datetime. A sweep's slice key is a label too, and EachCoordinate(dim) slices on one label of dim at a time.

The data

Index
A dimension's labels in order, supplied under the dimension's own key in sources. shift reads that order positionally (the data contract).
Coordinate
One point of a declaration's dimensions: one snapshot for one generator. A parameter has a value at each coordinate it covers, or no row there. The language calls the dimensions themselves the declaration's frame (named expressions).
Table
A polars DataFrame with one column per dimension, a value column and one row per coordinate: what a parameter arrives as, and what primal hands back. A relation's table is the exception, one column per column it declares. The code calls one a frame and means the same thing.
Relation
A named map between dimensions, supplied under its own key as a table of the rows it has. A keyed relation declares key columns and value columns and holds one row per key; a bare one holds each row at most once (the data contract).
Assumption
An assumptions: entry: a predicate on the data that only the numbers can answer, checked when data attaches and refused as a DataError where it fails. The conditions a piecewise: method puts on its breakpoints arrive the same way.
Mask
The where: on a declaration. What an excluded coordinate means is absence.

How it runs

Lane
A way a spec is executed. specsolve's is the relational lane: it validates at load time, lowers to the plan and streams on polars. The test suite's linopy lane builds the same spec as a linopy.Model, as the oracle the relational lane is checked against (relationship to linopy).
Engine
The relational lane's builder: it fills the model's tables from the attached data and hands them to a sink.
Sink
Where the handoff lands: a solver (highs, gurobi, xpress) or a file writer (.lp, .mps). linopy is a lane, not a sink. What a sink can ingest is its capability: a special-ordered set is one, and a sink without it refuses a model carrying a set rather than rewriting it (what each sink takes).
Sources
The data you attach: parameter, dimension and relation names to tables, and dimension names to their labels.
attach
Fitting sources onto a spec to make a Model; what build does and update does again. Never "bind", so that bound means one thing.

The built form

Handoff
The built model as a sink sees it, and all a sink sees: cols (bounds, type), obj, rows, matrix (CSR), quad (the objective's quadratic part), qmatrix (the constraints') and sos. Handing it over is the phase the metrics clock as handoff_seconds.
keep
How much of a session model.solve carries to the next solve: solver (default), progress (its work too) or nothing (the verbs).

Sweeps

solve_over (a sweep)
Solve one spec once per slice of an axis and fold the answers into a Sweep, releasing each slice's model as it goes. A sweep, never a "study" (sweeps).
Axis · slice · key
An axis says how the sources split: EachCoordinate(dim), one slice per label; EachWindow(dim, ...), one per window of consecutive labels; or a hand-built list of (key, sources) pairs. A slice is one set of sources, solved as one model, and its key is the label its rows are prefixed with in every table the sweep hands back.
Sweep
A sweep's answer: Result's readers one dimension wider, the key column first. original_index=True reads a table back over the sliced dimension's own labels.
carry
carry={parameter: variable} hands one slice's solution to the next as data, in slice order.
held · spilled
Where a sweep's frames are. A held sweep carries them in memory, and sweep.primal(name) and the exports — the frame readers, the ones that hand back a table — answer off them. A spilled sweep left them in a directory, which is what spill_to= writes and what scan_sweep reads: there sweep.scan(name) is the reader and the frame readers refuse (spilling).

Row types

Record · Metrics · SliceMetrics
The three saved rows, each a NamedTuple that names its own columns. Where a column is nullable, the type also derives the schema it is written with, so an all-null column keeps its own type instead of the one a single row infers. Record is how a solve terminated, one per solve, and what result.record hands back. Metrics is what it took — the sizes, what the sink added to them, the counters and the clocks, every clock naming its unit — and is what archive.metrics hands back (the attributes). SliceMetrics is one slice of a sweep's share of that, in its own columns, and is the row behind sweep.metrics.

A row is a value and gets a type; a table stays a Table. So a result hands back its one Record, while Record and SliceMetrics are the rows behind sweep.record and sweep.metrics rather than what those hand back, and a reader that wants one row of a table asks the frame for it.

bound means one thing

bound
A lower or upper limit on a variable or a constraint row: the bounds: of a declaration, the BOUNDS section of an .mps file, an absent bound the solver reads as infinity. Nothing else; data is attached, never bound.