Skip to main content

Conditions on a sum and on a constraint

Two cases require a condition. A constraint may sum over part of the members of a variable, such as the arcs of a network where the variable is indexed over the full product. A constraint may also apply to some members of its frame only, such as a capacity limit on one plant. where= covers both cases. over= declares the rows of a constraint explicitly.

Restricting a sum​

Sum(..., where=domain) restricts each term to the members of domain before summing.

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

P = Set("P", np.array(["p0", "p1"]))
W = Set("W", np.array(["w0", "w1", "w2"]))

arcs = subset(
(P, W),
{"P": np.array(["p0", "p0", "p1"]), "W": np.array(["w0", "w1", "w2"])},
)

m = Model("network")
x = m.var("x", (P, W))
rows = m.constraint("capacity", Sum(W, x[P, W], where=arcs) <= 10.0)

print(x.n_columns)
print(rows.n_rows, rows.nnz)
print(m.assemble().to_dense())
Output
6
2 3
[[1. 1. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 1.]]

The variable is over the full product and has six columns. The condition gives three of them a coefficient. A variable over a subset would have three columns from the start. Use a condition where the variable is over the product and one constraint reads part of it. Declare a subset where the model never uses the other members.

Restricting the rows​

m.constraint(..., where=domain) takes a domain over the constraint's frame and keeps the rows in it. A row outside the condition is not produced.

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

P = Set("P", np.array(["p0", "p1"]))
W = Set("W", np.array(["w0", "w1", "w2"]))

m = Model("network")
x = m.var("x", (P, W))
only_p0 = subset((P,), {"P": np.array(["p0"])})
rows = m.constraint("capacity", Sum(W, x[P, W]) <= 10.0, where=only_p0)

print(rows.n_rows)
print(m.assemble().to_dense())
Output
1
[[1. 1. 1. 0. 0. 0.]]

One row, for p0. p1 has no capacity row.

A condition over dimensions other than the frame of the constraint raises ValueError, and the message gives both index sets.

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

P = Set("P", np.array(["p0", "p1"]))
W = Set("W", np.array(["w0", "w1", "w2"]))

m = Model("network")
x = m.var("x", (P, W))
by_warehouse = subset((W,), {"W": np.array(["w0"])})

m.constraint("capacity", Sum(W, x[P, W]) <= 10.0, where=by_warehouse)
Raises ValueError
ValueError: constraint 'capacity' has free dimensions ('P',); its condition is over ('W',)

Declaring the rows explicitly​

By default the rows of a constraint are derived from its terms: a row exists where every term has a value and the right-hand side has a value. A term with no value along a frame dimension removes the row. A row missing one of its terms would express a constraint that was not written.

over=domain declares the rows instead of deriving them. A term with values at some of the rows contributes where it has them, and every row in the domain is produced.

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

P = Set("P", np.array(["p0", "p1"]))
W = Set("W", np.array(["w0", "w1", "w2"]))

m = Model("network")
x = m.var("x", (P, W))
rows = m.constraint("capacity", Sum(W, x[P, W]) <= 10.0, over=product((P,)))

print(rows.n_rows)
Output
2

over or where, not both​

over= declares the rows and where= restricts them. Passing both raises ValueError.

import numpy as np
from nimopt import Model, Set, Sum, product, subset

P = Set("P", np.array(["p0", "p1"]))
W = Set("W", np.array(["w0", "w1", "w2"]))

m = Model("network")
x = m.var("x", (P, W))

m.constraint(
"capacity",
Sum(W, x[P, W]) <= 10.0,
where=subset((P,), {"P": np.array(["p0"])}),
over=product((P,)),
)
Raises ValueError
ValueError: constraint 'capacity' is given over= and where= together; pass one of them

A condition on a sum and a condition on the constraint compose. The first restricts what is summed, and the second restricts which rows exist.