sos¶
A piecewise-linear cost curve stated as a special-ordered set — the same restriction piecewise writes with binaries, handed to the solver as a set it branches on itself.
The problem¶
A convex-combination curve needs one thing said about its weights: at most two may be nonzero, and they must be neighbours. Otherwise the weights mix distant breakpoints and the model prices the chord under the curve instead of the curve:
There are two ways to say the last line and the language now has both.
piecewise: (§4) builds it: a binary per segment, an adjacency row per
breakpoint, and one more row picking a segment. sos: (§4.1) declares it and
leaves the formulation to the sink — which is the point, because a solver that
knows what SOS2 means branches on the set directly rather than searching the
binaries someone wrote for it.
The model¶
The same model, as math
Sets¶
| Symbol | Meaning |
|---|---|
| \(\mathcal{T}\) | index \(t\) --- snapshot |
| \(\mathcal{G}\) | index \(g\) --- generator |
| \(\mathcal{B}\) | index \(b\) --- bp |
Parameters¶
| Symbol | Meaning |
|---|---|
| \(p^{\mathrm{max}}\) | p_max over \(\mathcal{G}\) |
| \(\mathit{load}\) | load over \(\mathcal{T}\) |
| \(\mathit{bp}^{\mathrm{x}}\) | bp_x over \(\mathcal{G} \times \mathcal{B}\) |
| \(\mathit{bp}^{\mathrm{y}}\) | bp_y over \(\mathcal{G} \times \mathcal{B}\) |
Variables¶
| Symbol | Meaning |
|---|---|
| \(p\) | p over \(\mathcal{T} \times \mathcal{G}\) |
| \(\mathit{op\_cost}\) | op_cost over \(\mathcal{T} \times \mathcal{G}\) |
| \(\mathit{lam}\) | lam over \(\mathcal{T} \times \mathcal{G} \times \mathcal{B}\) |
Objective¶
total_cost
Subject to¶
convexity
dispatch
cost
balance
Variable domains¶
p
op_cost
lam
lam sos
dimensions:
snapshot:
dtype: int
generator:
dtype: str
bp:
dtype: int
parameters:
p_max:
dims: [generator]
load:
dims: [snapshot]
bp_x:
dims: [generator, bp] # per-generator breakpoint positions
bp_y:
dims: [generator, bp] # per-generator cost at each breakpoint
variables:
p:
foreach: [snapshot, generator]
bounds:
lower: 0
upper: p_max
op_cost:
foreach: [snapshot, generator]
bounds:
lower: 0
lam: # convex-combination weight on each breakpoint
foreach: [snapshot, generator, bp]
bounds:
lower: 0
upper: 1
sos:
on_one_segment: # at most two neighbouring weights — the curve, not its hull
variable: lam
over: bp
type: 2
constraints:
convexity:
foreach: [snapshot, generator]
expression: sum(lam, over=bp) == 1
dispatch:
foreach: [snapshot, generator]
expression: p == sum(lam * bp_x, over=bp)
cost:
foreach: [snapshot, generator]
expression: op_cost == sum(lam * bp_y, over=bp)
balance:
foreach: [snapshot]
expression: sum(p, over=generator) == load
objectives:
total_cost:
sense: minimize
expression: op_cost
What it exercises¶
sos: is the one declaration that adds neither a column nor a row. It names
columns the variable already made and says which of them may be nonzero
together, so it leaves the engine as a fifth stream beside cols, obj,
rows and the matrix.
That stream is also the one a sink may not be able to take, which is what makes
this model worth reading beside piecewise:
| what it does with this model | |
|---|---|
gurobi |
addSOS — branches on the set, no binaries in the model at all |
lp_file |
an sos section, read by any solver whose parser has one |
highs |
no SOS concept — the set arrives reformulated, as a binary per segment and a linking row per member |
So the same file runs everywhere, and what differs is the search, not the
answer. On HiGHS the reformulation is very nearly what piecewise: would have
emitted, which is the honest summary of what a capability gap costs here: a
worse relaxation, never a refusal. Two conditions come with it — every member
needs a finite upper bound (lam has one), and the result is mixed-integer, so
an otherwise-continuous model gives up its duals.
Compare piecewise, which reaches the same feasible set through a declaration that expands before the plan exists: by the time the model is built there is nothing left called piecewise, where a set is still a set right up to the sink that takes it.
examples/sos.yaml · back to all models