You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Prompt: "Summarize what we would want from mathspec in one issue"
Note
The following content was generated by AI.
fluxopt's math is now ten fragments composed with merge (fluxopt/fluxopt#342 to #344, on #742). After the regime and flag rework, 28 of 70 rules are hand-written formulations. Four open issues cover most of them; two asks are new.
What fluxopt writes by hand
Rules today
Issue
a rate on or off with a variable size, switched by a binary other rules also read; the storage charge/discharge exclusion
#343 wrote each rule that had one row family per regime once, with the regimes as cases: of what it reads. For example, 8 ramp rules became 2. #344 replaced 13 boolean flags with the table or the position(time) they restated. What is left is not regime splits or flags. It is formulations the language has no word for.
Existing issues, as fluxopt would use them
an indicator constraint has no spelling, though its big-M now derives from declared bounds #265 (indicator). fluxopt needs lo·S·b ≤ x ≤ hi·S·b where S is sometimes a variable and b is at(running, by=status_of, …). running is also read by the costs, transitions, durations, ramp release and gated curves, so the formulation must read an existing binary, not own one. a semi-continuous variable domain: zero, or between the declared bounds #263's semi-continuous domain owns its binary and so does not fit. The storage exclusion's charging binary is read nowhere else, so that use could own it. M from the declared bounds.upper of S (size_max) removes the three places fluxopt restates it by hand.
a binary times a bounded variable has no spelling, so the McCormick triple is written out at every use #266 (binary × bounded). Both fluxopt uses gate on a bare binary (invest_active, invest_build), so the open question 1 there does not block them. Spelled as the product size == invest_size * invest_active, the math prints as the model, and the expansion writes the three exact rows from the continuous factor's declared bound, refusing an open one.
sum_back(x, along=time, window=p) counts steps, and a parameter window may not vary along the axis. fluxopt's steps have a duration dt that varies, so "the last 4 hours" is a different number of steps at different times. Today fluxopt tracks durations with a continuous counter and big-M rows (uptime_gate, uptime_accum_max/min, uptime_min_run, the same for downtime, and two carry-over rows).
Ask: a window measured in a weight along the axis, e.g. sum_back(startup, along=time, window=uptime_min, weight=dt): the positions before t whose cumulative dt up to t is below the window. No datetime is needed; the weight is a numeric parameter the spec already reads. This is the sum_back sibling of #299, which asks the same of shift, and the triage against the relational ceiling there applies here too. Open: how carry-over from before the horizon maps onto the window's edge, and whether "a row of pairs within the window" shipped as a relation is the data-preparation answer instead.
New: fragments that share declarations
Each fragment loads and prints alone, so each restates the dimensions it uses and every name it reads under given:. #743 cut a restated dimension to its dtype. The given: entries are the larger cost: status.yaml reads 11, and most repeat one of six shared parameters (dt, time_weight, size_bound, …).
Proposal, for triage:
# ramps.yamlimports: [flows.yaml, status.yaml]
Meaning. Every dimension and relation the imported file declares is declared here, as written there. Every parameter, variable or expression it declares that this file names is read as a given: entry with the imported frame.
Typesetting. Unchanged. Imported names print under Given exactly as hand-written entries do, and the legend is the same.
Safety. A path that does not resolve, relative to the importing file, is a load error that names it. An import copies declarations, so a reading cannot disagree with its introducer, which removes that refusal class for imported names. An import is not transitive, and a cycle is refused. A term: stays hand-written, because it is this file's own claim. merge expands imports before it composes, so every composition rule holds as written. to_dict() keeps imports:.
The tension. A fragment no longer loads without the files it imports. Whether that is still "the file is the artifact" is the question to decide first.
Not needed
edge=<parameter> for shift over a variable.cases: holding shift covers it: chore(main): release 0.0.0-alpha.62 #343's level_before does it for storage, and the status transition takes the same form.
Note
The following content was generated by AI.
fluxopt's math is now ten fragments composed with
merge(fluxopt/fluxopt#342 to #344, on #742). After the regime and flag rework, 28 of 70 rules are hand-written formulations. Four open issues cover most of them; two asks are new.chargingbinarysize = invest_size · invest_active,size_at_build = invest_size · invest_buildlamand thesos:setrate_min/rate_max,level_min/level_maxin the datauptime,downtimeand six big-M/ceiling parameterssum_backgiven:entries across 10 fragmentsHow each maps, the two new asks, what is not needed
Where fluxopt stands
Measured on the composed spec of fluxopt/fluxopt#344 (mathspec
0b0e4b0, specsolvee78c1a3):#343 wrote each rule that had one row family per regime once, with the regimes as
cases:of what it reads. For example, 8 ramp rules became 2. #344 replaced 13 boolean flags with the table or theposition(time)they restated. What is left is not regime splits or flags. It is formulations the language has no word for.Existing issues, as fluxopt would use them
lo·S·b ≤ x ≤ hi·S·bwhereSis sometimes a variable andbisat(running, by=status_of, …).runningis also read by the costs, transitions, durations, ramp release and gated curves, so the formulation must read an existing binary, not own one. a semi-continuous variable domain: zero, or between the declared bounds #263's semi-continuous domain owns its binary and so does not fit. The storage exclusion'schargingbinary is read nowhere else, so that use could own it. M from the declaredbounds.upperofS(size_max) removes the three places fluxopt restates it by hand.invest_active,invest_build), so the open question 1 there does not block them. Spelled as the productsize == invest_size * invest_active, the math prints as the model, and the expansion writes the three exact rows from the continuous factor's declared bound, refusing an open one.piecewise:a link should be a row, and its sign a column #82 (piecewise link as a row). fluxopt now carries the links as the relationcurve_ofand the sense as astrcolumnlink_sense(chore: the alpha.61 entry lists the one change it shipped, rather than replaying the whole history #344). That is the shapepiecewise:a link should be a row, and its sign a column #82 asks the block to take.upper: relative_rate_max * size_boundremoves the data-side folds, whose comments exist only to explain why they are folded.New: a duration window for
sum_backsum_back(x, along=time, window=p)counts steps, and a parameter window may not vary along the axis. fluxopt's steps have a durationdtthat varies, so "the last 4 hours" is a different number of steps at different times. Today fluxopt tracks durations with a continuous counter and big-M rows (uptime_gate,uptime_accum_max/min,uptime_min_run, the same for downtime, and two carry-over rows).Ask: a window measured in a weight along the axis, e.g.
sum_back(startup, along=time, window=uptime_min, weight=dt): the positions beforetwhose cumulativedtup totis below the window. No datetime is needed; the weight is a numeric parameter the spec already reads. This is thesum_backsibling of #299, which asks the same ofshift, and the triage against the relational ceiling there applies here too. Open: how carry-over from before the horizon maps onto the window's edge, and whether "a row of pairs within the window" shipped as a relation is the data-preparation answer instead.New: fragments that share declarations
Each fragment loads and prints alone, so each restates the dimensions it uses and every name it reads under
given:. #743 cut a restated dimension to its dtype. Thegiven:entries are the larger cost:status.yamlreads 11, and most repeat one of six shared parameters (dt,time_weight,size_bound, …).Proposal, for triage:
given:entry with the imported frame.term:stays hand-written, because it is this file's own claim.mergeexpands imports before it composes, so every composition rule holds as written.to_dict()keepsimports:.Not needed
edge=<parameter>forshiftover a variable.cases:holdingshiftcovers it: chore(main): release 0.0.0-alpha.62 #343'slevel_beforedoes it for storage, and the status transition takes the same form.mandatory,is_cyclicand the like. The refusal ("a label on one dimension is a parameter over it") is right, andboolparameters state those sets.Version
mathspec
0b0e4b0(#742 head), through specsolvee78c1a3.