See what a piecewise curve or an SOS expands to#
This guide shows how to write a piecewise curve (a
piecewise: entry) or a special ordered
set (an SOS) out as plain
entries. You can then hand the expanded file to software that builds the model
(an engine) but cannot read an
sos: entry.
You need mathspec installed and the specification of an optimisation model (a spec) that loads.
Write a piecewise curve out#
Save the spec in the "Before" tab below as before.yaml, and load it. Its
curve has method: sos2:
Write out only the piecewise curve with spec.expand('piecewise'), which keeps
every sos: entry. Then write out both with spec.expand():
Variable domains
x
y
Subject to
curve
Assumptions
curve_complete
dimensions:
bp: { dtype: int, ordered: true }
parameters:
x_bp: { dims: [bp] }
y_bp: { dims: [bp] }
variables:
x: { dims: [], bounds: { lower: 0 } }
y: { dims: [], bounds: { lower: 0 } }
curve_lam:
dims: [bp]
bounds: { lower: 0, upper: 1 }
description: convex-combination weight on a breakpoint
constraints:
curve_convexity:
dims: []
expression: sum(curve_lam, over=bp) == 1
curve_x:
dims: []
expression: (x) == sum(curve_lam * x_bp, over=bp)
curve_y:
dims: []
expression: (y) == sum(curve_lam * y_bp, over=bp)
sos:
curve:
variable: curve_lam
along: bp
type: 2
assumptions:
curve_complete:
holds: x_bp AND y_bp
description: >-
piecewise 'curve': every breakpoint the curve runs through needs a row in
'x_bp', 'y_bp'. A missing row does not shorten the curve. Attach the rows,
or declare a where: that sets how far the curve runs.
Variable domains
curve_lam
x
y
Subject to
curve_convexity
curve_x
curve_y
curve
Assumptions
curve_complete
dimensions:
bp: { dtype: int, ordered: true }
parameters:
x_bp: { dims: [bp] }
y_bp: { dims: [bp] }
variables:
x: { dims: [], bounds: { lower: 0 } }
y: { dims: [], bounds: { lower: 0 } }
curve_lam:
dims: [bp]
bounds: { lower: 0, upper: 1 }
description: convex-combination weight on a breakpoint
curve_seg:
dims: [bp]
domain: binary
description: a binary per segment, 1 where the two members it spans may be nonzero
constraints:
curve_convexity:
dims: []
expression: sum(curve_lam, over=bp) == 1
curve_x:
dims: []
expression: (x) == sum(curve_lam * x_bp, over=bp)
curve_y:
dims: []
expression: (y) == sum(curve_lam * y_bp, over=bp)
curve_pick:
dims: []
expression: sum(curve_seg, over=bp) <= 1
curve_adjacency:
dims: [bp]
expression: curve_lam <= (curve_seg + shift(curve_seg, along=bp, offset=1, edge=0))
assumptions:
curve_complete:
holds: x_bp AND y_bp
description: >-
piecewise 'curve': every breakpoint the curve runs through needs a row in
'x_bp', 'y_bp'. A missing row does not shorten the curve. Attach the rows,
or declare a where: that sets how far the curve runs.
Variable domains
curve_lam
curve_seg
x
y
Subject to
curve_adjacency
curve_convexity
curve_pick
curve_x
curve_y
Assumptions
curve_complete
expand('piecewise') writes the curve out as the weights curve_lam, the row
that makes them sum to one and one row per entry of links:. The call keeps the SOS that the curve states as an sos: entry,
and expand() writes that SOS out too. curve_complete, a statement about the data
(an assumption), stays in the spec
after either call writes the curve out.
Keep this Python session open, because later steps use spec and ms.
Print the expansion as math#
Print before.yaml with each entry that expands
(formulation) written out as plain
entries:
The command and the Python call print the symbol tables of the spec, then the
math. The "Math" tab under expand() in the section above contains the same
math without the symbol tables.
Write an SOS out#
Save the spec in the "Before" tab below as sos.yaml, and write it out with
ms.to_spec('sos.yaml').expand(). In this spec, the sos: entry pick
requires at most one member of p to be non-zero:
dimensions:
g: { dtype: str }
variables:
p:
dims: [g]
bounds: { lower: 0, upper: 10 }
pick_seg:
dims: [g]
domain: binary
description: a binary per member, 1 where that member may be nonzero
constraints:
pick_pick:
dims: []
expression: sum(pick_seg, over=g) <= 1
pick_nonzero:
dims: [g]
expression: p <= 10.0 * (pick_seg)
Variable domains
p
pick_seg
Subject to
pick_nonzero
pick_pick
expand() replaces pick with the binary variable pick_seg and two
constraints.
Check the expanded spec#
Load the expanded spec again, to check that mathspec accepts it as a spec of its own:
The call returns a Spec without an error. Save the YAML text that to_yaml()
returns as a file, and hand the file to an engine that reads only plain
entries.
See also
Spec.expand(), for every argument the call accepts and what it returns- the rows of an SOS written out,
for the rows of each constraint that
expand()writes for an SOS - writing a formulation out, for the entries that each formulation adds