How to create variables
In PuLP 4 variables are created by the problem and belong to it. Create the problem first, then its variables:
from pulp import LpProblem, LpMinimize, LpBinary, LpInteger
prob = LpProblem("example", LpMinimize)
x = prob.add_variable("x", lowBound=0, upBound=10)
A variable cannot be used in another problem. Adding an expression that uses a
variable from another problem raises PulpError.
One variable: add_variable
add_variable() takes a name, the bounds and the
category:
x = prob.add_variable("x", lowBound=0) # x >= 0
y = prob.add_variable("y", upBound=5, cat=LpInteger) # integer, y <= 5
z = prob.add_variable("z", cat=LpBinary) # 0 or 1
lowBoundandupBounddefault toNone, which means no bound. A bound must be finite; passNoneinstead offloat("inf").catis"Continuous"(default),"Integer"or"Binary". A binary variable is stored as an integer variable with bounds 0 and 1, soz.catreturns"Integer". Usez.isBinary()to check for binaries.The variable is part of the problem as soon as it is created, even if no constraint uses it yet.
Many variables: add_variable_dicts
Most models need one variable per item, or per combination of items.
add_variable_dicts() creates them all in one call and
returns them in a dictionary keyed by the index values. Use it in place of the
3.x LpVariable.dicts. The arguments are:
name: the prefix of the variable names.indices: one iterable of index values, or a tuple of iterables for several indices.lowBound,upBound,cat: the same as foradd_variable. They apply to every variable.
With one index you get a flat dictionary. Each name is the prefix and the index
value joined by _:
warehouses = ["A", "B"]
bars = [1, 2, 3]
buy = prob.add_variable_dicts("buy", warehouses, lowBound=0)
# {'A': buy_A, 'B': buy_B}
buy["A"] # the variable called buy_A
With a tuple of indices you get nested dictionaries, one level per index:
flow = prob.add_variable_dicts("flow", (warehouses, bars), lowBound=0, cat=LpInteger)
flow["A"][2] # the variable called flow_A_2
flow["A"] # {1: flow_A_1, 2: flow_A_2, 3: flow_A_3}
The nested dictionaries work well with lpSum:
from pulp import lpSum
supply = {"A": 1000, "B": 4000}
demand = {1: 500, 2: 900, 3: 1800}
for w in warehouses:
prob += lpSum(flow[w][b] for b in bars) <= supply[w], f"supply_{w}"
for b in bars:
prob += lpSum(flow[w][b] for w in warehouses) >= demand[b], f"demand_{b}"
Choosing the names
If name contains %s, PuLP fills in one %s per index instead of
adding _%s for each:
route = prob.add_variable_dicts("route_%s_to_%s", (warehouses, bars))
route["B"][3].name # 'route_B_to_3'
Index values are converted with str(), and characters that LP and MPS files
cannot hold, such as spaces, are replaced by _. For example, the index
"new york" gives the name ship_new_york. Give each family of variables
its own prefix. Two variables with the same name are accepted, but
writeLP, writeMPS and the solvers that go through those files raise
PulpError: Repeated variable names.
Binary variables
cat=LpBinary creates integer variables with bounds 0 and 1 in every level of
the dictionary:
is_open = prob.add_variable_dicts("open", warehouses, cat=LpBinary)
is_open["A"].isBinary() # True
Other shapes: add_variable_dict and add_variable_matrix
Both take the same arguments as add_variable_dicts:
add_variable_dict()returns one flat dictionary keyed by tuples, one key per combination of the indices:assign = prob.add_variable_dict("assign", (warehouses, bars), cat=LpBinary) assign["A", 1] # the variable called assign_A_1
add_variable_matrix()returns nested lists, indexed by position instead of by value:grid = prob.add_variable_matrix("grid", (warehouses, bars)) grid[0][1] # the variable called grid_A_2
Use whichever shape makes your constraints easier to write.
add_variable_dicts is the usual choice.
Changing a variable after it is created
The bounds and the category of a variable can be changed later. This is useful, for example, to solve the linear relaxation of a problem and then make it integer again:
x.lowBound = 2
x.upBound = None # no upper bound
x.cat = LpInteger
x.cat = "Continuous" # relax it
x.cat = LpBinary # integer with bounds 0 and 1
To add a variable to constraints that already exist, see How to model column-wise.