Repository navigation
Releases: fluxopt/specsolve
Release list
0.7.0
0.6.2
0.6.1
specsolve now requires polars below 2.0. On polars 2.0.0, a row's dual can come
back empty, and a model can take about nine times the memory. Nothing else
changes.
0.6.0
Breaks:
- An archive, spill or saved result written by 0.5.0 is refused: the layout is 3.
catalog.parquethas nodim_position.owned.parquetis atanswer/windows/, and only withkeep_windows=True.
The pull requests since 0.5.0:
- fix(data): a datetime label is held in microseconds whatever unit it arrives in (#1839)
- feat(archive): an archive writes every column as a type that parquet readers agree on (#1835)
- fix(archive): an archive's catalog describes each file as it is written, and kept windows have a catalog of their own (#1838)
- fix(sweep): a sweep over a datetime axis in nanoseconds or milliseconds solves rather than crashing in its first slice (#1848)
0.5.0
The top level of specsolve now holds only what you call. What a call returns
is in specsolve.types, and what it raises is in specsolve.errors. Any other
name under specsolve. is internal. A spilled sweep now reads like one held in
memory. This release breaks imports, one class name, one attribute and one
method. Nothing on disk changed: archives, spills and saved results written by
0.4.0 read back.
Breaks:
Model,Result,Sweep,ResultArchiveandSweepArchiveare imported
fromspecsolve.types, and so are the rows they hold:Record,
Provenance,Metrics,DiagnosticsandConstraintRow. Write
sps.types.Resultforsps.Result.specsolve.relational.parquetis
removed.- Every error and
SpecsolveWarningis imported fromspecsolve.errors.
Writesps.errors.DataErrorforsps.DataError. SolveArchiveis nowResultArchive. What an archive holds is
archive.resultfor one solve andarchive.sweepfor a sweep;.answeris
removed.Model.evaluatoris private. An archive and a sweep still evaluate an
expression the file never named.
New:
- On a spilled or scanned sweep,
primal,dual,evaluate,to_pandas,
to_dataarray,to_datasetandsaveread the name they are asked for
instead of refusing.sweep.scan(name)returns the same frame, not yet read. - The reference has an internal glossary: the names the code uses for its own
parts, for anyone readingsrc/.
The pull requests since 0.4.0:
- refactor(archive): Record, Metrics and Provenance are imported from specsolve.relational.answer_layout, which an archive's own layout nests (#1823)
- refactor(archive): an archive of one solve is a ResultArchive read through .result, and a sweep archive is read through .sweep (#1828)
- feat(sweep): a spilled or scanned sweep's readers read the name they are asked for instead of refusing (#1830)
- refactor(api): what a call returns is imported from specsolve.types, and what it raises from specsolve.errors (#1833)
0.4.0
An archive now says what produced it, and a directory of archives reads as one
table per file. This release breaks archives, spills and saved results from
0.3.0, a model file that uses a reserved name, one import and one keyword.
Breaks:
- An archive, a sweep spill or a saved result written by 0.3.0 is refused by
name. Itsformat.jsonnames the specsolve that wrote it: read it with that
release. - A spec that declares a name starting with
specsolve_, in any letter case,
is refused. Rename the name. The prefix is reserved for the columns
specsolve adds. - The readers of a sweep return the answer. An
EachWindowsweep comes back
over the model's own coordinates, and the lookahead rows are dropped.
original_index=is removed. To get the frames of each window, pass
per_window=True. - A sweep archive holds the answer. To archive the frames of each window too,
passkeep_windows=Truetosolve_over. SliceMetricsis removed. A row ofsweep.metricsis aMetricsrow, and
loadedis nowloads, which is0or1.- The
runcolumn ofrecordandmetricsis nowspecsolve_run.
New:
- Every table in an archive carries
specsolve_run, the archive's own name.
record.parquetandmetrics.parquethave the same columns for a solve and
for every kind of sweep. A sweep names each slice inslice_axisand
slice. catalog.parquetlists each file an archive holds, with its kind,
description and dimensions.- An archive holds each source as the tidy table that the solve read.
sps.tidy(spec, sources)returns those tables. - The record names the solver, its version, its options, and the specsolve
and mathspec versions. The options are a JSON string. To record more
options than the solver's list, passrecord_options=. - specsolve requires
mathspec>=0.2.1,<0.3, so a release installed later
gets the mathspec it was released with.
Fixes:
- A sweep key named like a metrics column, such as
loads, is refused before a
slice is solved. - A sweep over an index given as an iterator reads it once, and every slice
and the archive see the same labels.
The pull requests since 0.3.0:
- fix(deps): an installed specsolve release keeps the mathspec minor version it was released against (#1816)
- feat(archive): every archived table carries the run it came from as specsolve_run, and names starting with specsolve_ are reserved (#1809)
- feat(archive): an archive's record and metrics have the same columns for a solve and for every kind of sweep (#1810)
- feat(archive): an archive lists every name it holds, its kind, description and dimensions in catalog.parquet (#1812)
- feat(sweep): a sweep reads and archives its answer over the model's own coordinates by default, and keeps the per-window frames only on request (#1811)
- feat(data): an archive holds each source as the tidy table the solve read, and tidy() returns those tables (#1813)
- fix(sweep): a sweep key named like a metrics column is refused before a slice is solved (#1820)
- feat(archive): an archived answer names the solver, its options and the specsolve and mathspec versions that produced it (#1817)
- feat(api): a caller can name more solver options whose value the record keeps (#1818)
0.3.0
specsolve now requires mathspec 0.2.1. This release breaks one import.
check(spec, sink=...)is removed. To ask a sink without a solve, call
build(spec, sources).check(sink). A sink refuses only what the built model
asks of it.
With mathspec 0.2.1:
- A divisor, a power's base or an exponent may add over parameters, such as
x / (1 - cost)or(1 + rate) ** years. - A spec composed from several files with
mathspec.mergebuilds. A fragment
is refused by name.
Fixes:
- A constant sum that no member reaches at a coordinate adds zero there.
- A
cases:region whose value is0, read through a relation, no longer
makes the build refuse its rows.
The pull requests since 0.2.1:
- feat(sinks): a built model checks whether a sink takes it without a solve, and check no longer takes a sink (#1806)
- feat: specsolve requires mathspec 0.2.1, where a divisor or a power over parameters may add and a spec may be composed from several files (#1800)
- fix(engine): a constant sum that no member reaches at a coordinate adds zero there rather than leaving a gap (#1801)
- fix(engine): a cased quantity whose otherwise is zero no longer reads as short where it is read through a relation (#1804)
- docs: the tutorials teach in the order a new user needs them, and warm-starting a re-solve has its own how-to guide (#1788)
- docs: a how-to guide chooses a solver or writes the model to a file, and lists the solvers this build has (#1790)
- docs: a how-to guide reads an answer into pandas or xarray (#1789)
0.2.1
This release fixes three wrong answers. None of them raised an error. A model
that meets one builds a different row, or reads a different value, in 0.2.1.
No file, archive or import breaks.
- A sum over a variable with a
where:, beside a parameter over fewer
dimensions, such assum(y + w, over=f)withwoverfalone. The
parameter now drops out where the variable is absent, in the constraint row
and inevaluate. Before, it stayed in, so the row had a wrong right-hand
side. - A divisor, a power's base or an exponent that is a
sum(...). It is now
taken as its total, in a build and in a read. Before, it was taken summand by
summand. - A reported quotient whose divisor is absent or zero. It now has no value at
that coordinate. Before, it readinfor raised aDataError.
Result.dual now states the sign of a dual: the rate at which the optimal
objective rises with the row's right side, for every comparator, under either
sense and on every solver. No value changes. mathspec's reference now defines
dual(c) the same way
(energy-models/mathspec#751).
The pull requests since 0.2.0:
- fix(engine): a sum leaves out a parameter where a variable added to it is absent, in the model and in a read (#1784)
- fix(engine): a divisor, a power's base or an exponent that is a sum is taken as its total rather than summand by summand (#1779)
- fix(api): a reported quotient is absent where its divisor is absent or zero (#1776)
- docs(api): a dual is the rate at which the optimal objective rises with its row's right side, on every sink (#1774)
0.2.0
A saved answer now records which layout it was written in and which specsolve
wrote it, so a later release can name an answer it no longer reads. The Python
API reference is now rendered from the docstrings.
This release breaks one thing:
- An answer or an archive that 0.1.0 or earlier wrote is refused with a
LayoutErrorthat says to solve the model again and save it. Its
format.jsonreads{"answer": 0}; 0.2.0 writes
{"layout": 1, "specsolve": "0.2.0"}. An archive still holds the spec and
the data it was solved with, underspec.yamlandsources/.
The pull requests since 0.1.0:
- feat(api)!: a saved answer is stamped with layout 1 and the specsolve version that wrote it, and one written by 0.1.0 or earlier is refused by name (#1765)
- docs: each fact about the Python API is stated once, in the docstring of the name it belongs to (#1769)
- docs: the Python API page renders every public name from its docstring (#1766)
- docs(sweeps): the sweep reference says how many sessions a sweep opens on a remote Gurobi (#1771)
0.1.0
🎉 specsolve is officially on PyPI!
pip install specsolvespecsolve 0.1.0 requires mathspec 0.2.0 and uses its words. A spec is the
problem a file states, with no data. A model is a spec with data attached,
which build returns as a Model
(glossary).
The messages and the docs use the two words in these senses.
Since 0.1.0rc1, this release breaks three things:
- An archive holds its spec as
spec.yaml, notmodel.yaml. An archive that
0.1.0rc1 wrote is refused with aLayoutError. To read one, rename its
model.yamlmember tospec.yaml. - specsolve requires mathspec 0.2.0. There, the first parameter of
to_spec,
adviceand the typesetting functions isspec, notmodel, and the module
mathspec.modelismathspec.spec. A call that passesmodel=by keyword
fails. A positional call works as before. - Messages say "spec" where they said "model" for the file. A test that
matches the old text fails.
The pull requests since 0.1.0rc1:
- docs: the site follows the reader's light or dark setting in its own violet colour, with code, diagrams and footnotes readable in both (#1767)
- feat!: specsolve requires mathspec 0.2.0 and uses its words spec and model, so an archive holds its spec as spec.yaml (#1768)
- docs: every link to the language's documentation points at mathspec.readthedocs.io (#1764)
- fix: the PyPI page links the docs, issues and changelog, and a stale saved answer says the layout moves before 1.0 (#1763)