Examples¶
Every model in the repo, what it says, and what it exercises. Three questions, in the order you probably have them: can it say my model? · is it readable? · does it get the right answer? The first and the third are the two tables on this page; the second is each model page itself, where the file sits beside the same model written on another stack.
Every page starts from data in the shape the call wants, and Preparing the data is where that shape comes from.
Every model¶
Teaching examples¶
| dispatch | Least-cost generation against a load profile — the smallest model that is still a model. |
| storage | Dispatch plus a battery, and the only construct in the language whose cost is not obviously linear. |
| transport | A network: generators sit on buses, lines connect buses, and power balances at every bus. |
| piecewise | Per-generator convex cost curves, expanded into a λ-formulation. |
| special-ordered sets | A piecewise-linear cost curve stated as a special-ordered set — piecewise with one line changed, handed to the solver as a set it branches on itself. |
| monthly budget | A cap on what each technology may generate per calendar month — an aggregate over a coarser grouping of time, written with the same operator that places a generator on a bus. |
| multi-period | Capacity decided once per investment period, binding at every snapshot inside it — and the periods need not be the same size. |
| seasons | A store that cycles inside each season rather than across the horizon, with seasons of different lengths — one balance row says it, because the wrap is the season's own and no level is carried from one season into the next. |
| reserves | Energy and reserve co-optimization on a two-bus grid: offers are (generator, market, tranche) triples, reserve zones overlap, and one line dangles. The model exists to prove a claim — every many-to-many shape the language covers, in one instance, each one load-bearing. |
| walkthrough | The dispatch model plus a macro and a named expression — the one used to print every pipeline stage. |
The PyPSA ladder¶
| rung 1 — transport | PyPSA linear optimal power flow, first rung: transport model, linear marginal cost, no KVL. |
| rung 2 — ramp limits | Rung 1 plus a limit on how fast each generator may change output between snapshots. |
| rung 3 — storage | Rung 2 plus a StorageUnit carrying energy between snapshots. |
| rung 4 — cyclic storage | Rung 3 with the horizon closed on itself: the first snapshot's state of charge carries over from the last. |
| rung 5 — KVL | Passive AC lines: flow is decided by physics, not chosen. The last rung of the ladder. |
| transmission losses | The loss on a line is r · s². PyPSA approximates it from below with a fan of tangents. |
| rung 6 — AC-DC, two coordinates | A meshed AC–DC network under a CO₂ budget. PyPSA's own ac-dc-meshed example. |
PyPSA components and modes¶
| unit commitment | Which generators are on, not just how much they produce — a binary per generator per snapshot, with start-up and shut-down charges. |
| minimum up and down times | A unit that has started must stay on; one that has stopped must stay off. |
| linearized unit commitment | A unit may be committed by a third. PyPSA ships this as a mode, not as a debugging convenience. |
| global limits | Limits that hold over a whole set at once: an energy total, a capacity at one bus, and the built network measured twice. |
| multi-link | One Link, one input bus, several output buses, each output derated by its own efficiency — PyPSA's spelling for a CHP plant, an electrolyser with waste heat, any conversion with more than one product. |
| modular capacity | Capacity that comes in whole modules: an integer count decides it, not a continuous bound. |
| energy totals | A generator's dispatch reduced over every snapshot and bounded: a contracted delivery, a reservoir's season. |
| fixed by data | A row of data that is present pins its variable; a row that is absent leaves it free. |
| spillage | A hydro unit takes inflow it did not choose, and spills what neither turbine nor reservoir can absorb. |
| the Store component | The component every sector-coupled PyPSA model uses for hydrogen, heat and gas. |
| mixed cycling | PyPSA's cyclic_state_of_charge is a per-unit flag, so one network runs both regimes at once: a unit that must end each horizon where it began, beside one handed a level it may simply spend. |
| link delay | A shipment is the input shifted along time: withdrawn at one snapshot, delivered at another, derated on the way. |
| multi-period investment | A build year and a lifetime decide which rows an asset appears in, and each period's costs carry its own discount. |
| committable and extendable | A minimum output that is a share of a capacity still being decided: two variables multiplied, and one constant to take them apart. |
| growth limit | A cap on new capacity per investment period, which grows with the period before it. The first shift in the corpus along an axis that is not time-of-day. |
| stochastic scenarios | Three futures over one network: the fleet is built before anyone knows which arrives, and dispatched after. |
| CVaR risk preference | The same three futures as the stochastic rung, planned against the tail as well as the expectation. |
Published optima¶
| Dantzig transport | Dantzig's transportation problem — GAMS model library #1, and the oldest LP in the corpus. |
| Dantzig, economies of scale | GAMS model library trnspwl: the same shipping problem, but a big consignment is cheaper per unit — cost grows as sqrt(x), not linearly. |
| Stigler's diet | The cheapest way to eat for a year and stay alive. 77 foods, 9 nutrients, 1939 prices. |
| Facility location | Where do you put the warehouses? Open a set of them, assign every customer to one, and trade the fixed cost of opening against the cost of serving from further away. |
| GenX piecewise fuel | A day of dispatch for two carbon-capture plants and a wind farm under a net-zero carbon cap, where the gas plant's fuel use bends with its output. |
| Routing telephone calls | How many of 425 requested circuits a five-city network can carry at once — and by which routes. |
| Choosing the mode of transport | Moving 180 tonnes of chemicals out of four depots, where a depot may reach a centre by rail or by road at different cost. |
| OSeMOSYS UTOPIA | What to build and how hard to run it, 1990–2010, to meet three end-use demands at least discounted cost. |
| Travelling salesman | Visit every city once and come home, as cheaply as possible. The most famous problem in combinatorial optimisation, and the one most often assumed to be out of reach here. |
Everything on this page is generated — the catalogue off the site nav and
each page's own opening line, the constructs matrix off each model's resolved
plan, the reference table off examples/ports/references.json, which is the
same file the tests assert against. Regenerate with
uv run python -m tools.constructs; a test fails if any of the three is stale.
Every page also carries the model as math, typeset from the same file the
engine builds (uv run python -m tools.gallery_math, likewise gated). Where a
model has a symbol table in examples/symbols/, the block uses the notation
that model's prose already does.
Can it say my model?¶
Read off the resolved plan of each model rather than its text, so it cannot drift from what the engine builds.
| model | verified | sum |
sum(by=) |
at() |
shift |
shift(edge='wrap') |
where |
bounds |
piecewise |
sos |
MILP |
|---|---|---|---|---|---|---|---|---|---|---|---|
| dispatch | ✔ 10500 | ✓ | · | · | · | · | ✓ | ✓ | · | · | · |
| monthly_budget | ✔ 9500 | ✓ | ✓ | · | · | · | · | ✓ | · | · | · |
| multi_period | ✔ 10020 | ✓ | · | ✓ | · | · | · | ✓ | · | · | · |
| piecewise | ✔ 3850 | ✓ | · | · | · | · | · | ✓ | ✓ | · | · |
| reserves | ✔ 915 | ✓ | ✓ | ✓ | · | · | · | ✓ | · | · | · |
| seasons | · | · | · | · | · | ✓ | · | ✓ | · | · | · |
| sos | · | ✓ | · | · | · | · | · | ✓ | ✓ | ✓ | · |
| storage | ✔ 5650 | ✓ | · | · | · | ✓ | · | ✓ | · | · | · |
| transport | ✔ 4400 | · | ✓ | · | · | · | · | ✓ | · | · | · |
| walkthrough | · | ✓ | · | · | · | · | ✓ | ✓ | · | · | · |
| facility_location | ✔ 932616 | ✓ | · | · | · | · | · | ✓ | · | · | ✓ |
| genx_piecewise_fuel | ✔ 2341.82 | ✓ | · | · | · | ✓ | ✓ | ✓ | · | · | · |
| osemosys_utopia | ✔ 29446.9 | ✓ | · | · | · | · | · | ✓ | · | · | · |
| pypsa_ac_dc | ✔ 1.8441e+07 | ✓ | ✓ | ✓ | · | · | · | ✓ | · | · | · |
| pypsa_committable_extendable | ✔ 21700 | ✓ | · | · | · | · | ✓ | ✓ | · | · | ✓ |
| pypsa_cvar | ✔ 35410 | ✓ | · | · | · | · | · | ✓ | · | · | · |
| pypsa_cyclic_storage | ✔ 17228.8 | · | ✓ | · | ✓ | ✓ | · | ✓ | · | · | · |
| pypsa_energy_sum | ✔ 21400 | ✓ | ✓ | · | · | · | ✓ | ✓ | · | · | · |
| pypsa_fixed | ✔ 49900 | · | ✓ | · | · | · | ✓ | ✓ | · | · | · |
| pypsa_global_limits | ✔ 127212 | ✓ | ✓ | ✓ | · | · | ✓ | ✓ | · | · | · |
| pypsa_growth_limit | ✔ 47110 | ✓ | ✓ | ✓ | ✓ | · | · | ✓ | · | · | · |
| pypsa_kvl | ✔ 17000 | ✓ | ✓ | · | · | · | · | ✓ | · | · | · |
| pypsa_linearized_uc | ✔ 5540 | ✓ | · | · | ✓ | · | · | ✓ | · | · | · |
| pypsa_link_delay | ✔ 4311.11 | · | ✓ | · | ✓ | · | · | ✓ | · | · | · |
| pypsa_losses | ✔ 24114.2 | · | ✓ | · | · | · | ✓ | ✓ | · | · | · |
| pypsa_min_up_down | ✔ 32750 | ✓ | · | · | ✓ | · | ✓ | ✓ | · | · | ✓ |
| pypsa_mixed_cycling | ✔ 4800 | · | ✓ | · | ✓ | ✓ | ✓ | ✓ | · | · | · |
| pypsa_modular | ✔ 56700 | · | ✓ | · | · | · | · | ✓ | · | · | ✓ |
| pypsa_multi_period | ✔ 85300 | ✓ | · | ✓ | · | · | · | ✓ | · | · | · |
| pypsa_multilink | ✔ 1100 | ✓ | ✓ | · | · | · | · | ✓ | · | · | · |
| pypsa_ramp | ✔ 18200 | · | ✓ | · | ✓ | · | · | ✓ | · | · | · |
| pypsa_spill | ✔ 3200 | · | ✓ | · | ✓ | · | ✓ | ✓ | · | · | · |
| pypsa_stochastic | ✔ 33940 | ✓ | · | · | · | · | · | ✓ | · | · | · |
| pypsa_storage | ✔ 15253.2 | · | ✓ | · | ✓ | · | ✓ | ✓ | · | · | · |
| pypsa_store | ✔ 7005.5 | · | ✓ | · | ✓ | · | ✓ | ✓ | · | · | · |
| pypsa_transport | ✔ 22000 | · | ✓ | · | · | · | · | ✓ | · | · | · |
| pypsa_unit_commitment | ✔ 24900 | ✓ | · | · | ✓ | · | ✓ | ✓ | · | · | ✓ |
| stigler_diet | ✔ 0.108662 | ✓ | · | · | · | · | · | ✓ | · | · | · |
| telephone_routing | ✔ 380 | ✓ | ✓ | · | · | · | · | ✓ | · | · | ✓ |
| transport_dantzig | ✔ 153.675 | ✓ | · | · | · | · | · | ✓ | · | · | · |
| transport_modes | ✔ 1715 | ✓ | ✓ | · | · | · | · | ✓ | · | · | · |
| transport_pwl | ✔ 8.78685 | ✓ | · | · | ✓ | · | · | ✓ | ✓ | · | ✓ |
| tsp_mtz | ✔ 2085 | ✓ | ✓ | · | · | · | ✓ | ✓ | · | · | ✓ |
No holes left. Every construct in the table has at least one model behind
it whose optimum came from somebody else. The column was worth keeping
precisely because it named each gap out loud before it was filled: roll /
shift went first (ramp limits), then integrality
(unit commitment), and piecewise last
(economies of scale).
That is a floor, not a ceiling. A tick means one verified model exercises the construct — not that every shape of it is covered, and not that the constructs are exercised in combination. The table's job from here is to stay honest as the language grows, which is why it is generated rather than maintained by hand.
Does it get the right answer?¶
✔ means the optimum did not come from lpspec — a figure published with the model, or a reference implementation hand-written on another stack, each row's provenance saying which. Every model on this page is run by the test suite, so "there is a test" distinguishes nothing. What the badge marks is narrower, and it is the only check that can catch a shared misreading — both lanes of the implementation agreeing on a meaning the modeller did not intend, which passes every lpspec-against-lpspec test green.
Even the differential harness compares two lanes consuming the same resolved AST (hard rule 1), which is what makes them an oracle for each other and also what they cannot see. This is the net for that class, and the evidence behind the ceiling.
| port | optimum | rtol |
duals | reference |
|---|---|---|---|---|
| dispatch | 10500.0 | 1e-09 | ✔ | linopy 0.9.0, via examples/ports/references/linopy/dispatch.py — agreement, not a published figure |
| facility_location | 932615.75 | 1e-09 | · | published by OR-Library (Beasley) for instance cap71 of the uncapacitated warehouse location set, in the file uncapopt: http://people.brunel.ac.uk/~mastjjb/jeb/orlib/uncapinfo.html |
| genx_piecewise_fuel | 2341.8230753008093 | 1e-09 | · | published by GenX: asserted in test/test_piecewisefuel.jl as obj_true = 2341.82308 under genx_setup UCommit=2, CO2Cap=1, ParameterScale=1, and reproduced here by running GenX itself (julia 1.12.6, HiGHS) which reports 2341.8230753008093 |
| monthly_budget | 9500.0 | 1e-09 | ✔ | linopy 0.9.0, via examples/ports/references/linopy/monthly_budget.py — agreement, not a published figure |
| multi_period | 10020.0 | 1e-09 | ✔ | linopy 0.9.0, via examples/ports/references/linopy/multi_period.py — agreement, not a published figure |
| osemosys_utopia | 29446.86269 | 1e-09 | · | published by OSeMOSYS: asserted in OSeMOSYS_GNU_MathProg tests/test_gnu_mathprog.py as obj = 2.944686269e+04 for tests/utopia.txt, and reproduced here by running GLPK directly (glpsol 5.0, src/osemosys.txt) — an oracle outside Python entirely |
| piecewise | 3850.0 | 1e-09 | ✔ | linopy 0.9.0, via examples/ports/references/linopy/piecewise.py — agreement, not a published figure |
| pypsa_ac_dc | 18441021.477729216 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_ac_dc.py — n.objective + n.objective_constant, the system cost |
| pypsa_committable_extendable | 21700.0 | 1e-09 | · | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_committable_extendable.py |
| pypsa_cvar | 35410.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_cvar.py |
| pypsa_cyclic_storage | 17228.77962151063 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_cyclic_storage.py |
| pypsa_energy_sum | 21400.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_energy_sum.py |
| pypsa_fixed | 49900.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_fixed.py |
| pypsa_global_limits | 127211.66666666666 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_global_limits.py |
| pypsa_growth_limit | 47110.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_growth_limit.py |
| pypsa_kvl | 17000.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_kvl.py |
| pypsa_linearized_uc | 5540.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_linearized_uc.py |
| pypsa_link_delay | 4311.111111111111 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_link_delay.py |
| pypsa_losses | 24114.237385131008 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_losses.py |
| pypsa_min_up_down | 32750.0 | 1e-09 | · | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_min_up_down.py |
| pypsa_mixed_cycling | 4800.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_mixed_cycling.py |
| pypsa_modular | 56700.0 | 1e-09 | · | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_modular.py |
| pypsa_multi_period | 85300.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_multi_period.py |
| pypsa_multilink | 1100.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_multilink.py |
| pypsa_ramp | 18200.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_ramp.py |
| pypsa_spill | 3200.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_spill.py |
| pypsa_stochastic | 33940.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_stochastic.py |
| pypsa_storage | 15253.178322993519 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_storage.py |
| pypsa_store | 7005.5025000000005 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_store.py |
| pypsa_transport | 22000.0 | 1e-09 | ✔ | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_transport.py |
| pypsa_unit_commitment | 24900.0 | 1e-09 | · | pypsa 1.2.4 (its own linopy 0.9.0), via examples/ports/references/pypsa/pypsa_unit_commitment.py |
| reserves | 915.0 | 1e-09 | ✔ | linopy 0.9.0, via examples/ports/references/linopy/reserves.py — agreement, not a published figure |
| stigler_diet | 0.10866227820675685 | 1e-09 | ✔ | linopy 0.9.0, via examples/ports/references/linopy/stigler_diet.py — dollars per day; x365 = $39.6617/year1 |
| storage | 5650.0 | 1e-09 | ✔ | linopy 0.9.0, via examples/ports/references/linopy/storage.py — agreement, not a published figure |
| telephone_routing | 380.0 | 1e-09 | · | published by Gueret, Prins, Sevaux & Heipcke, Applications of Optimization with Xpress-MP (Dash Optimization, 2002) SS12.3.3 p. 182 — "380 out of the required 425 calls are routed"; problem and data in SS12.3, pp. 180-182 |
| transport | 4400.0 | 1e-09 | ✔ | linopy 0.9.0, via examples/ports/references/linopy/transport.py — agreement, not a published figure |
| transport_dantzig | 153.675 | 1e-09 | ✔ | published with GAMS model library #1 (trnsport), after Dantzig, Linear Programming and Extensions (1963) ch. 3.32 |
| transport_modes | 1715.0 | 1e-09 | · | published by Gueret, Prins, Sevaux & Heipcke, Applications of Optimization with Xpress-MP (Dash Optimization, 2002) SS10.2.3 p. 143 — "The minimum cost is EUR 1,715k"; problem and data in SS10.2, p. 142 |
| transport_pwl | 8.786852757777865 | 1e-09 | · | linopy 0.9.0's own add_piecewise_formulation, via examples/ports/references/linopy/transport_pwl.py; the model is GAMS model library trnspwl (Dantzig transport with economies of scale), which publishes the formulation and its discretisation but no optimal objective |
| tsp_mtz | 2085.0 | 1e-09 | · | published by TSPLIB for instance gr17 (Groetschel, 17 cities, EXPLICIT lower-diagonal distance matrix); optimum 2085 as listed in the TSPLIB solutions file |
The objective is not the only thing checked. A port with a duals tick
also records the reference's shadow prices and is asserted against them —
for the PyPSA models that is buses_t.marginal_price, the nodal price, which
is the output this audience reads most often after the cost.
That matters because an objective is one number and hides a great deal. A dual
vector is where two implementations most reliably disagree quietly: which side
of a constraint the price belongs to, and what sign an inequality carries.
Dantzig transport is in that set specifically because
both of its constraints are inequalities pointing opposite ways. A MILP has no
dual solution, and lpspec refuses to invent one — which is what the · rows
are.
Adding a port is four files and five rules: CONTRIBUTING.md.
The ladder¶
Reproducing a full PyPSA objective means reproducing marginal and capital cost, ramp limits, storage cycling and KVL at once, and a mismatch then implicates five features instead of one. So each network is a ladder, one feature per rung, each switched off in PyPSA and reproduced here: 1 transport model ✔ · 2 ramp limits ✔ · 3 storage with state of charge ✔ · 4 cyclic boundary condition ✔ · 5 KVL ✔ · 6 a meshed AC-DC network under a CO₂ budget ✔. Rungs 1–5 are one feature at a time on a three-bus network; rung 6 is the first that puts several of them on a network somebody else designed, which is a different question — not can it say this feature but does the whole thing still read. The ladder is the six rungs. A feature that needs no network gets a one-bus model of its own instead — PyPSA components and modes above — where a mismatch implicates the one thing switched on.
A rung that matches is a row in the table above; one that cannot be said is a row in the ledger. Both are evidence, so no rung is wasted.
Rung 2 needed the instance widened before it meant anything. Rung 1's links run saturated, which fixes every generator's output exactly, so a ramp limit on that network can only make it infeasible — never change the answer. A rung that cannot bind is not evidence that it works.
Rung 6 is where a second coordinate first earns its keep. A generator sits on a bus and burns a carrier, and both maps are load-bearing — the balance groups through one, the CO₂ budget reads an emission rate back down through the other. It also carries passive lines and controllable links at once, so both branch kinds group onto the same bus dimension in one equation.
The ladder reached rung 5 without a new primitive. Rung 5 is Kirchhoff's voltage
law, and it needed nothing added to the language: a cycle basis is a sparse
(cycle, line) incidence parameter, and the constraint is one
sum(f * cycle_incidence, over=line) == 0. A line can belong to several
cycles, so the incidence cannot be a declared lookup — that is the shape
finding, and it is the same "topology is data" claim the corpus started with.
Computing the basis is a graph algorithm and stays in data preparation, where
the ceiling puts it.
Rung 4 made the model smaller, which is the ladder paying off in the
direction nobody plans for. Closing the horizon deletes rung 3's boundary
equation outright: edge='wrap' is cyclic already, so the wrap onto the last snapshot
is what it does unguarded, and the acyclic case is the one needing an extra
clause. Two rungs written a day apart, differing by one deleted where, is a
sharper statement about the language than either alone.
Ledger — what a port could not say¶
Feeds the roadmap, with the verdict AGENTS.md asks for: macro, primitive, or escape.
| Port | What could not be said | Worked around by | Verdict |
|---|---|---|---|
| PyPSA rung 1 | a bound of -rating — PyPSA's p_min_pu = -1 |
shipping neg_rating as data |
primitive: bounds as expressions, #31. A second model asking for it |
| Travelling salesman | subtour cuts generated lazily inside branch-and-cut, which is how every serious TSP code works | MTZ, O(n²) and static | refused, and correctly: a solve loop is an algorithm, not a model |
min_up_time was the third row until
minimum up and down times ported it:
sum_back(start_up, over=snapshot, within=min_up_time), each generator's own
width read off the column.
Two rows from 33 ports — a rate worth watching once the corpus has hit the ceiling a few more times.
Shapes still without a witness¶
Not things the language cannot say — things no outside model in the corpus has yet been found to need. Each was searched for and not found, so the row is a standing request rather than a gap in the language:
| Shape | Where it was looked for |
|---|---|
| one axis grouped several ways, all of them load-bearing | OSeMOSYS UTOPIA declares three maps out of its timeslice, but they feed only storage constraints and the instance builds none |
| a chain whose coarse end carries a constraint | PyPSA's ac-dc-meshed has a country per bus and nothing constrains a country; GAMSLIB alum composes three such chains and its shipped scenario switches two of them off |
| a group that carries its own constraint and appears nowhere else | GAMSLIB mexls is exactly this, and its optimum is published only in a book with no reachable text |
| a partial map whose null membership moves an optimum | reserves exercises it, but that model is ours and was built to |
| opposite-sign legs onto one dimension, plus a second hop a constraint reads | an airline fleet-assignment text has it; the data and both optima are not obtainable |
A model that needs one of these is worth more to this corpus than another that exercises a shape already covered.
The TSP row is the one to read, and it is narrower than it first looked. Writing DFJ's subtour rows out in full is sayable — the subsets go in as data exactly the way KVL's cycle basis does, and an 8-city instance with all 246 subsets solves to a correct tour. There are 2ⁿ of them, so it stops being practical around twenty cities, but that is a data-size wall rather than a ceiling.
What is genuinely outside is lazy generation: solve, find the violated subsets, add rows, re-solve. That is an algorithm, and this language describes models. Since lazy generation is what every serious TSP code actually does, "lpspec can express TSP" and "lpspec is a good way to solve a large TSP" are different sentences and only the first is true.
A data-dependent row count is not what rules DFJ out: the cycle basis has one and is ordinary. What the corpus is for is catching exactly that kind of mis-attribution, where a data-size wall reads as a ceiling.
-
Laderman (1947) at the National Bureau of Standards published $39.69/year for this data, the first serious test of the simplex method. This LP's exact optimum is 0.08% under it — his rounding, not a different model — and both select the same five foods: wheat flour, liver, cabbage, spinach, navy beans. ↩
-
examples/ports/references/linopy/transport_dantzig.py — the same LP hand-written in linopy 0.9.0, which reaches 153.675 independently. Secondary: the published figure is what verifies the port. ↩