Skip to main content

Inspecting a built model

Row​

Returned by Model.row(name, **coords). One row as the assembled matrix stores it: the columns, the coefficients and the bounds that are passed to the solver. Model.row computes the entries of that row alone. It builds no matrix and no other row. coords gives one label per free dimension. A constraint with no free dimension has one row, and Model.row(name) returns it.

FieldContains
constraintthe equation this row belongs to
coordinatethe row's own coordinate, per free dimension
indexthe solver's own row number
termsone RowTerm per coefficient
sense, lower, upperread from the row's bounds
RowTerm fieldContains
columnthe solver's own column number
variablethe variable that column belongs to
coordinatethat column's coordinate, per dimension
coefficientthe value in the matrix

A variable occupies a contiguous range of the column space from its start. A column resolves to its variable through that range, and to a coordinate through the numbering rule of that variable.

sense is read from the bounds: equal bounds are ==, an infinite lower bound is <=, an infinite upper bound is >=.

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]]))
supply = Param.from_dense("supply", (P,), np.array([30.0, 25.0]))

m = Model("transport")
x = m.var("x", (P, W))
m.constraint("supply", Sum(W, cost[P, W] * x[P, W]) <= supply[P])

print(m.row("supply", P="porto"))
Output
supply[P='porto'] row 1
3·x[porto,berlin] + 1·x[porto,paris] + 6·x[porto,rome] <= 25

A coordinate at which the constraint has no row raises ValueError; the message points to the function that reports why it is missing. row and absent raise KeyError for a name that is not a declared constraint, and the message lists the declared constraints. row raises ValueError for a label that is not a member of its dimension, and the message identifies the dimension.

import numpy as np
from nimopt import Model, Param, Set

P = Set("P", np.array(["p1", "p2", "p3"]))
m = Model("m")
x = m.var("x", (P,), upper=5.0)
one = Param.from_dense("one", (P,), np.ones(3))
rhs = Param.from_long("rhs", (P,), {"P": np.array(["p1", "p2"])}, np.ones(2))
m.constraint("cap", one[P] * x[P] <= rhs[P])

m.row("cap", P="p3")
Raises ValueError
ValueError: constraint 'cap' has no row at {'P': 'p3'}; read `absent('cap')` for the rule that dropped it

Absence​

Returned by Model.absent(name). What a constraint set out to produce, what it produced, and which coordinates were dropped.

FieldContains
constraintthe equation this is about
stated_by"terms" where the rows are derived, "over" where given explicitly
expected, standingrows expected, rows kept
dropped_rowsone DroppedRow(coordinate, rule, detail) per row lost
dropped_termsone DroppedTerm(coordinate, variable, rule, detail) per term lost

expected - len(dropped_rows) == standing.

dropped_rows ruleMeaning
term-does-not-reacha term has no value at that coordinate; the row would express a constraint that was not written
wherethe condition excludes it
absent-rhsthe right-hand side has no value there
dropped_terms ruleMeaning
absent-coefficienta coefficient absent inside a sum; the row is kept with one term fewer

The two rules differ in what they remove. A coefficient absent inside a sum removes a term and keeps the row. A term absent along a free dimension removes the row.

Under over= the rows are given explicitly. Nothing is dropped, and a right-hand side that omits one raises instead. An empty dropped_rows beside stated_by="over" follows from that rule.

import numpy as np
from nimopt import Model, Param, Set, Sum

P = Set("P", np.array(["p1", "p2"]))
W = Set("W", np.array(["w1", "w2", "w3"]))
m = Model("t")
flow = m.var("flow", (P, W))
cost = Param.from_long(
"cost",
(P, W),
{"P": np.array(["p1", "p1", "p2"]), "W": np.array(["w1", "w2", "w1"])},
np.array([1.0, 2.0, 3.0]),
)
supply = Param.from_dense("supply", (P,), np.array([3.0, 3.0]))
m.constraint("supply", Sum(W, cost[P, W] * flow[P, W]) <= supply[P])

print(m.absent("supply"))
Output
supply 2 of 2 rows stated by terms
term absent P='p1', W='w3' flow absent-coefficient (cost)
term absent P='p2', W='w2' flow absent-coefficient (cost)
term absent P='p2', W='w3' flow absent-coefficient (cost)