See what a curve or a set expands to#
A piecewise: block and a sos: block
each stand for plain variables and constraints. Read those rows to review a
formulation, to teach one, or to hand the model to an engine that has no
concept of a set.
The model below states one of each. A piecewise: block ties two variables to
a curve through the breakpoints. A sos: block says that at most one member of
a family is nonzero.
description: one plant whose fuel use follows a curve, and whose output picks one mode
dimensions:
snapshot: { dtype: int }
bp: { dtype: int }
mode: { dtype: int }
parameters:
demand: { dims: [snapshot] }
bp_out: { dims: [bp] }
bp_fuel: { dims: [bp] }
variables:
output:
dims: [snapshot]
bounds: { lower: 0, upper: 100 }
fuel:
dims: [snapshot]
bounds: { lower: 0 }
level:
dims: [snapshot, mode]
bounds: { lower: 0, upper: 1 }
piecewise:
fuel_curve:
over: bp
method: sos2
links:
- [output, bp_out]
- [fuel, bp_fuel]
sos:
mode_pick:
variable: level
over: mode
type: 1
constraints:
meet:
dims: [snapshot]
expression: output >= demand
objective:
sense: minimize
expression: sum(fuel, over=snapshot)
1. Write the formulation out#
expand() returns the same math with its formulations stated as rows. The
command line prints the result instead of returning it.
2. Read the names it added#
The expansion declares what the two blocks stood for, and the blocks themselves are gone:
sorted(spec.variables) # ['fuel', 'level', 'output']
sorted(written_out.variables)
# ['fuel', 'fuel_curve_lam', 'fuel_curve_seg', 'level', 'mode_pick_seg', 'output']
sorted(written_out.constraints)
# ['fuel_curve_adjacency', 'fuel_curve_convexity', 'fuel_curve_link0',
# 'fuel_curve_link1', 'fuel_curve_pick', 'meet', 'mode_pick_nonzero',
# 'mode_pick_pick']
written_out.piecewise # {}
written_out.sos # {}
Every emitted name starts with the block that emitted it, so fuel_curve_lam
is the curve's weights and mode_pick_seg is the set's binaries. A file that
already declares one of these names is refused, which keeps the two apart.
3. Read the rows as math#
Both readings print from the same file. The first states the construct, the second states the rows it stands for.
The curve is one line, and the set is a membership beside the variable it runs along:
The curve becomes weights on the breakpoints, one link row per tied variable, and the binaries that keep the weights adjacent:
The set becomes one binary per member, a row that picks at most one, and a row that holds an unpicked member at zero:
Name the symbols as a paper would with a symbol table, which spells an emitted name as readily as a declared one. Print a model as math has the commands for a document that compiles.
4. Write out one kind at a time#
Pass a kind to keep the other construct. This is how to read a curve without the binaries underneath it:
spec.expand('piecewise') # curves become weights; the sets stay
spec.expand('sos') # sets become binaries; the curves stay
A method: sos2 curve states a set, so writing the curves out adds one:
expand() with no argument writes the curves out first for that reason, and a
set never states a curve.
Two things to know#
An expansion is a file like any other. A curve emits variables,
constraints and assumptions over the parameters the file declared, and no
parameter of its own, so to_yaml() writes every expansion and the same data
binds it.
A method states what it assumes of the data. Every curve states that its
breakpoints are there, because a missing parameter row reads as a zero rather
than as a shorter curve. A method: lp or method: convex curve states more:
it is exact only for breakpoints of the right shape. The expansion writes
those conditions into
assumptions: beside the rows. The
method: sos2 curve above states nothing about the shape, because it takes a
curve of any shape.
What expand() accepts, and what each method: emits, is under
piecewise curves and SOS.