Skip to main content

Definition

Definition​

Definition(name="definition", sense="min")

A definition declares the sets, parameters and variables a model is written from, and its constraints, in the expression syntax a model uses. It contains no data. A set declared here identifies a dimension and has no members, and a parameter identifies a shape and has no values.

An expression contains references, not arrays. The free dimensions of an equation and its sense are read from the relation, and neither is declared beside it. sense is "min" or "max", set once here.

MemberReturns
set(name)a declared Set, whose members arrive with the data
alias(name, base)a declared Alias over one of this definition's sets
param(name, sets)a declared Param, whose values arrive with the data
var(name, sets, subset=None, lower=0.0, upper=inf, integer=False)a declared Variable
constraint(name, relation, where=None, over=None)nothing; registers the constraint
piecewise(name, x, x_points, y, y_points, sign, method, active=None, relaxed=False, where=None)a Piecewise; build generates its declarations and checks its breakpoints
build(data)a Model over the declarations, bound to data
explain()an Explanation of what is declared
set_objective(expression)nothing; sets the objective
sense"min" or "max", as declared
sets, aliases, parameters, variables, constraints, piecewise_declarationsthe registries, keyed by name
from nimopt import Definition, Sum

d = Definition("dispatch", sense="min")
snapshot = d.set("snapshot")
generator = d.set("generator")
p_max = d.param("p_max", (generator,))
load = d.param("load", (snapshot,))
cost = d.param("cost", (generator,))
p = d.var("p", (snapshot, generator), lower=0.0, upper=p_max)
d.constraint("balance", Sum(generator, p[snapshot, generator]) == load[snapshot])
d.set_objective(Sum(snapshot, generator, cost[generator] * p[snapshot, generator]))

print(list(d.sets), list(d.parameters))
print(d.constraints["balance"][0].expression.frame)
print(list(d.variables), d)
Output
['snapshot', 'generator'] ['p_max', 'load', 'cost']
('snapshot',)
['p'] Definition('dispatch', 1 variables, 1 constraints)

One namespace for sets and parameters​

Sets and parameters share one key space. The data a definition is built from is keyed by declared name, and one key identifies one symbol. Declaring a parameter under the name of a set raises ValueError.

from nimopt import Definition

d = Definition("d")
S = d.set("S")
d.param("S", (S,))
Raises ValueError
ValueError: parameter 'S' is already declared as a set; declare another name

Equations are in no data mapping. A constraint may therefore take the name of the parameter that bounds it.

from nimopt import Definition, Sum

d = Definition("d")
S = d.set("S")
supply = d.param("supply", (S,))
one = d.param("one", (S,))
x = d.var("x", (S,))
d.constraint("supply", Sum(S, one[S] * x[S]) <= supply[S])

print(list(d.parameters), list(d.constraints))
Output
['supply', 'one'] ['supply']

An alias in a definition​

alias(name, base) declares a second name for one of the sets of the definition. A model relates a set to itself through an alias. The alias has no data of its own and reads the labels bound to its base set. build takes members for the set and none for the alias, and an alias in data raises ValueError.

import numpy as np
from nimopt import Definition, Sum

d = Definition("network", sense="min")
N = d.set("N")
NP = d.alias("NP", N)
limit = d.param("limit", (N, NP))
flow = d.var("flow", (N, NP), lower=0.0)
d.constraint("cap", flow[N, NP] <= limit[N, NP])
d.set_objective(Sum(N, NP, limit[N, NP] * flow[N, NP]))

m = d.build({"N": np.array(["a", "b"]), "limit": np.ones((2, 2))})
print(m.n_columns, m.n_rows)
Output
4 4

Domains in a definition​

A Domain resolves labels through the coordinate of each set, and a declared set has none. where= and over= on constraint, and subset= on var, therefore take a tuple of the sets of the definition, meaning their full product. They also take one of its parameters, whose coefficients are the coordinates. Both forms resolve to the same domain. A model and a definition declare a sparse variable or an explicit row domain the same way.

Building​

build(data) copies the declaration graph, binds the copy, numbers the columns and returns a Model. data maps the name of a declared set to its members and the name of a declared parameter to its values. The definition is unchanged, and it builds one model per dataset it is given.

The values of a parameter are given dense over its product, as an array of one value per cell. They are also given long over its entries, as one mapping of label columns and one value column. The long form gives a parameter with coefficients at some coordinates and none at the rest. A variable declared with subset= that parameter takes its members from those coordinates.

import numpy as np
from nimopt import Definition, Sum

d = Definition("transport", sense="min")
P, W = d.set("P"), d.set("W")
cost = d.param("cost", (P, W))
supply = d.param("supply", (P,))
demand = d.param("demand", (W,))
flow = d.var("flow", (P, W), subset=cost, lower=0.0)
d.constraint("supply", Sum(W, cost[P, W] * flow[P, W]) <= supply[P])
d.constraint("demand", Sum(P, cost[P, W] * flow[P, W]) >= demand[W])
d.set_objective(Sum(P, W, cost[P, W] * flow[P, W]))

m = d.build(
{
"P": np.array(["p1", "p2"]),
"W": np.array(["w1", "w2"]),
"cost": (
{"P": np.array(["p1", "p1", "p2"]), "W": np.array(["w1", "w2", "w1"])},
np.array([1.0, 2.0, 3.0]),
),
"supply": np.array([3.0, 3.0]),
"demand": np.array([1.0, 1.0]),
}
)

# three arcs, so three columns rather than the four the product would span
print(m.n_columns, m.n_rows)
print(m.solve().status)
Output
3 4
optimal

Data that omits a declaration, or contains a key the definition never declared, raises ValueError before anything is bound.

from nimopt import Definition

d = Definition("d")
d.set("S")
d.build({})
Raises ValueError
ValueError: data does not cover ['S']; add an entry for each