Skip to main content

Get started

Install​

pip install nimopt

nimblend is installed as a dependency. HiGHS is the default solver.

A transport model​

Two plants, Lisbon and Porto, ship to three warehouses, Berlin, Paris and Rome. Plant p has supply s[p], warehouse w has demand d[w], and one unit shipped on route (p, w) costs c[p, w]. The decision variable x[p, w] is the quantity shipped on each route.

minimize Σ_{p,w} c[p,w] · x[p,w]
subject to Σ_w x[p,w] ≤ s[p] for each plant p
Σ_p x[p,w] ≥ d[w] for each warehouse w
x[p,w] ≥ 0

In nimopt the sets index every declaration and the parameters contain the data. m.var declares the decision variable, m.constraint adds each constraint family under a name, and set_objective sets the objective function.

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]))
demand = Param.from_dense("demand", (W,), np.array([20.0, 15.0, 15.0]))

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

m.constraint("supply", Sum(W, x[P, W]) <= supply[P])
m.constraint("demand", Sum(P, x[P, W]) >= demand[W])
m.set_objective(Sum(P, W, cost[P, W] * x[P, W]))

solution = m.solve()
print(solution.status, solution.objective)
Output
optimal 135.0

The solver reports an optimal solution with objective 135.

Reading the solution​

primal("x") returns the shipments as an array indexed over the sets x was declared on. dual("demand") returns the dual value of each demand row: the change in the objective per unit increase in that warehouse's demand.

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]))
demand = Param.from_dense("demand", (W,), np.array([20.0, 15.0, 15.0]))

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

shipped = solution.primal("x")
print(shipped.dims)
print(shipped.to_dense())
print(solution.dual("demand").to_dense())
Output
('P', 'W')
[[20. 0. 10.]
[ 0. 15. 5.]]
[3. 1. 6.]

Rows are plants and columns are warehouses. Lisbon ships 20 to Berlin and 10 to Rome; Porto ships 15 to Paris and 5 to Rome. The duals of the demand rows are 3, 1 and 6: the marginal cost of one additional unit at each warehouse.

Every code block in this documentation is self-contained. The second block therefore repeats the model. Each block runs in a Python session as it is, or opens in the playground with "Run this example".

Next​

  • Vocabulary defines the terms used throughout: set, member, frame, row, absence, and others.
  • The tutorial builds this model one concept per page.
  • The guides cover sparse networks, time lags, conditions on rows, and bounds from data.
  • Explanation covers the design and its costs.