Skip to content

fluxopt's composed spec writes 28 of its 70 rules by hand, and six language features would state them #745

Description

@FBumann

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 7, plus the charging binary #265
size = invest_size · invest_active, size_at_build = invest_size · invest_build 6, plus one auxiliary variable #266
a curve whose number of links is data 5, plus lam and the sos: set #82
the envelope folded into rate_min/rate_max, level_min/level_max in the data 4 parameters #261
minimum and maximum up/down time over steps of unequal length 10, plus uptime, downtime and six big-M/ceiling parameters new: a duration window for sum_back
11 dimensions declared 44 times, 47 given: entries across 10 fragments not rules; the cost of every file loading alone new: fragments that share declarations
How each maps, the two new asks, what is not needed

Where fluxopt stands

Measured on the composed spec of fluxopt/fluxopt#344 (mathspec 0b0e4b0, specsolve e78c1a3):

                 monolith (#341)  split (#342)  regimes (#343)  flags (#344)
constraints            86              86             70             70
parameters             93              93             89             77
relations               6               6              6              9
given entries           —              57             47             47

#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

New: a duration window for sum_back

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.yaml
imports: [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

Version

mathspec 0b0e4b0 (#742 head), through specsolve e78c1a3.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions