Expressions
Term
One variable, an optional coefficient, the dimensions summed over, and a scale factor. A term describes a block of coefficients and contains references, not arrays. Writing it allocates nothing: an expression over a million columns costs the same as one over ten.
| Member | Returns |
|---|---|
free_dims | the dimensions it is still indexed over |
carried_dims | every dimension it has |
with_coefficient(coefficient) | the term, scaled by a parameter |
summing(dims) | the term, reduced over those dimensions |
scaled(by) | the term, multiplied by a number |
restricted_to(domain) | the term, over those members only |
A caller builds terms through the operators rather than these members:
cost[P, W] * x[P, W] gives a coefficient, Sum gives the reduction, and
- gives the scale.
Expression
A list of terms and the frame they share. The frame is the union of the terms' free dimensions, ordered by the term that introduces each. A term narrower than the frame is broadcast over it when the expression is materialised.
| Member | Returns |
|---|---|
terms | the terms it contains |
frame | the dimensions it is indexed over |
coords | the coordinates of that frame |
materialise() | its coefficients as a nimblend array |
materialise_at(at) | its coefficients at one coordinate of the frame, as a nimblend array over the column dimension |
import numpy as np
from nimopt import Model, Param, Set
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))
y = m.var("y", (P, W))
combined = cost[P, W] * x[P, W] - y[P, W]
print(combined.frame)
print(len(combined.terms))
print(combined.terms[0].free_dims)
Output
('P', 'W')
2
('P', 'W')
Sum
Sum(I, J, ..., expression, where=None)
The expression reduced over the named sets. Each set named leaves the
frame. where= takes a domain and restricts the entries of each term before
the reduction. The sum then runs over the coordinates given, not over every
coordinate of the product.
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',)
()
A sum is over the members of a set and takes the set, not a lag of it. Write the lag at the variable reference.
import numpy as np
from nimopt import Model, Set, Sum
T = Set("T", np.array(["t0", "t1", "t2"]))
m = Model("schedule")
x = m.var("x", (T,))
Sum(T - 1, x[T])
Raises ValueError
ValueError: a sum is over the members of ['T'] and takes the set, not a lag of it; write the lag at the variable's reference
Relation
An expression, a sense and a right-hand side, produced by comparing an
expression with <=, >= or ==. Model.constraint turns one into a
constraint.
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))
bounded = Sum(W, x[P, W]) <= 30.0
print(type(bounded).__name__, bounded.sense)
Output
Relation <=
A relation has no truth value. Python evaluates 0 <= expr <= 10 as two
comparisons joined by and and keeps only the second. The chained form
raises, and the first bound is not dropped.
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))
0.0 <= Sum(W, x[P, W]) <= 10.0
Raises TypeError
TypeError: a relation has no truth value; write each bound in its own constraint
Forms that are not linear
nimopt expresses linear terms. Each form below raises where it is
written, and the message gives the form to write instead.
A variable raised to a power is not linear; a coefficient takes the power and a variable multiplies it.
import numpy as np
from nimopt import Model, Set
T = Set("T", np.arange(3))
m = Model("m")
x = m.var("x", (T,))
x[T] ** 2
Raises TypeError
TypeError: cannot raise an expression to a power: expressions are linear; raise a coefficient to the power and multiply it by a variable
A variable in a denominator is not linear either; the reciprocal is written as a coefficient the variable multiplies.
import numpy as np
from nimopt import Model, Set
T = Set("T", np.arange(3))
m = Model("m")
x = m.var("x", (T,))
1.0 / x[T]
Raises TypeError
TypeError: cannot divide by an expression: expressions are linear; declare the reciprocal as a coefficient the variable multiplies
The absolute value of an expression is not linear. A magnitude is written
with two rows bounding the expression, and a reduction with Sum over its
sets.
import numpy as np
from nimopt import Model, Set
T = Set("T", np.arange(3))
m = Model("m")
x = m.var("x", (T,))
abs(x[T])
Raises TypeError
TypeError: an expression has no absolute value: expressions are linear; bound the expression with two rows, or reduce it with `Sum` over its sets
An LP has no row for a strict inequality.
import numpy as np
from nimopt import Model, Set
T = Set("T", np.arange(3))
m = Model("m")
x = m.var("x", (T,))
x[T] < 5.0
Raises TypeError
TypeError: an LP has no row for a strict inequality; write `<=` or `>=`, and reduce with `Sum` in place of `min` or `max`
The built-in sum of expressions with no set to reduce over calls
Expression.sum. That call raises, and it returns no unreduced expression.
import numpy as np
from nimopt import Model, Set
T = Set("T", np.arange(3))
m = Model("m")
x = m.var("x", (T,))
x[T].sum()
Raises TypeError
TypeError: an expression is reduced over the sets it is summed across; specify them with `Sum(I, J, expression)`
A relation expresses one bound. Comparing it a second time raises, and the first bound is not dropped.
import numpy as np
from nimopt import Model, Set
T = Set("T", np.arange(3))
m = Model("m")
x = m.var("x", (T,))
(x[T] <= 5.0) >= 1.0
Raises TypeError
TypeError: a relation already has one bound; compare the expression again in its own constraint
A dimension is reduced once. A second reduction over the same dimension raises.
import numpy as np
from nimopt import Model, Set, Sum
T = Set("T", np.arange(3))
m = Model("m")
x = m.var("x", (T,))
Sum(T, Sum(T, x[T]))
Raises ValueError
ValueError: term 'x' already sums over ['T']; sum over each dimension once