Expressions
Constraints and the objective are written over sums of variables: the total shipped from a plant, the total received by a warehouse, the total cost. An expression is such a sum. It is symbolic: writing one records the variables, the coefficients and the sets involved, and computes nothing.
Referencing a variable
x[P, W] references the variable over its sets and returns an expression
with one term. The frame of an expression is the tuple of dimensions it
is still indexed over. x[P, W] has frame (P, W): one value per route.
import numpy as np
from nimopt import Model, Set
P = Set("P", np.array(["lisbon", "porto"]))
W = Set("W", np.array(["berlin", "paris", "rome"]))
m = Model("transport")
x = m.var("x", (P, W))
shipped = x[P, W]
print(type(shipped).__name__)
print(shipped.frame)
Output
Expression
('P', 'W')
Sum
Sum(S, expression) sums over the members of S and removes S from the
frame. Summing over W gives the total shipped from each plant, indexed
over P. Summing over both sets gives a scalar.
import numpy as np
from nimopt import Model, Set, Sum
P = Set("P", np.array(["lisbon", "porto"]))
W = Set("W", np.array(["berlin", "paris", "rome"]))
m = Model("transport")
x = m.var("x", (P, W))
print(x[P, W].frame)
print(Sum(W, x[P, W]).frame)
print(Sum(P, W, x[P, W]).frame)
Output
('P', 'W')
('P',)
()
The frame determines the shape of a constraint built on the expression: an
expression with frame (P,) produces one row per plant. An expression with
an empty frame is a scalar, and an objective takes that form.
Coefficients
The cost of a plan is Σ_{p,w} c[p, w] · x[p, w]. Multiplying a reference
by a parameter over the same sets gives the term a coefficient. The frame is
unchanged until the sum is taken.
import numpy as np
from nimopt import Model, Param, Set, Sum
P = Set("P", np.array(["lisbon", "porto"]))
W = Set("W", np.array(["berlin", "paris", "rome"]))
cost = Param.from_dense("cost", (P, W), np.array([[2.0, 4.0, 5.0], [3.0, 1.0, 6.0]]))
m = Model("transport")
x = m.var("x", (P, W))
per_route = cost[P, W] * x[P, W]
total_cost = Sum(P, W, per_route)
print(per_route.frame)
print(total_cost.frame)
print(len(total_cost.terms))
Output
('P', 'W')
()
1
total_cost is a single term. The same expression over a million routes is
still one term: it contains references to cost and x, not their values.
Values are read when the matrix is assembled.
Addition and subtraction
Expressions add and subtract, producing one expression over the frame both share. A balance, inflow minus outflow, is written this way. With a second variable for returned goods, the net shipment on a route is the outbound quantity minus the returned quantity.
import numpy as np
from nimopt import Model, Set
P = Set("P", np.array(["lisbon", "porto"]))
W = Set("W", np.array(["berlin", "paris", "rome"]))
m = Model("transport")
x = m.var("x", (P, W))
returned = m.var("returned", (P, W))
net = x[P, W] - returned[P, W]
print(net.frame)
print(len(net.terms))
Output
('P', 'W')
2
Two terms, one per variable, over the same frame.
Next: Constraints.