pulp: Pulp classes

LpProblem([name, sense])

An LP Problem

LpVariable(_var)

Thin wrapper over the Rust Variable.

LpAffineExpression(_expr)

Thin wrapper around Rust AffineExpr.

LpConstraint(_constr)

LP constraint backed by Rust for a row already stored in the model.

The LpProblem Class

class pulp.LpProblem(name: str = 'NoName', sense: int = 1)

Bases: object

An LP Problem

Creates an LP Problem

This function creates a new LP Problem with the specified associated parameters

Parameters:
  • name – name of the problem used in the output .lp file

  • sense – of the LP problem objective. Either LpMinimize (default) or LpMaximize.

Returns:

An LP Problem

An important attribute of the problem is:

objective

The objective of the problem, an LpAffineExpression

Creating variables (see How to create variables):

add_variable(name: str, lowBound: int | float | Decimal | None = None, upBound: int | float | Decimal | None = None, cat: Literal['Continuous', 'Integer', 'Binary'] = 'Continuous') → LpVariable

Create a variable in this problem and return it.

Parameters:
  • name – the variable’s name

  • lowBound – lower bound (None: no bound); must be finite

  • upBound – upper bound (None: no bound); must be finite

  • cat – "Continuous", "Integer" or "Binary" (stored as an integer variable with bounds 0 and 1)

add_variable_dicts(name: str, indices: tuple[Iterable[Any], ...] | Iterable[Any] | None = None, lowBound: int | float | Decimal | None = None, upBound: int | float | Decimal | None = None, cat: Literal['Continuous', 'Integer', 'Binary'] = 'Continuous', indexStart: list[Any] | None = None) → dict[Any, Any]

Create one variable per index value and return them in a dictionary.

Parameters:
  • name – prefix of the variable names. One _%s per index is appended unless name already contains %s placeholders.

  • indices – an iterable of index values, or a tuple of iterables for several indices. Several indices give nested dictionaries, one level per index.

  • lowBound – lower bound of every variable (None: no bound)

  • upBound – upper bound of every variable (None: no bound)

  • cat – "Continuous", "Integer" or "Binary"

  • indexStart – index values already used in the name (used for the nested levels)

Returns:

{index: variable}, or {i: {j: variable}} for two indices, and so on

>>> buy = prob.add_variable_dicts("buy", ["A", "B"], lowBound=0)
>>> buy["A"].name
'buy_A'
>>> flow = prob.add_variable_dicts("flow", (["A", "B"], [1, 2]), cat="Integer")
>>> flow["A"][2].name
'flow_A_2'
add_variable_dict(name: str, indices: tuple[Iterable[Any], ...] | Iterable[Any] | None, lowBound: int | float | Decimal | None = None, upBound: int | float | Decimal | None = None, cat: Literal['Continuous', 'Integer', 'Binary'] = 'Continuous') → dict[Any, LpVariable]

Create one variable per combination of the indices and return them in one flat dictionary keyed by tuples (by value for a single index). Takes the same arguments as add_variable_dicts().

>>> assign = prob.add_variable_dict("assign", (["A", "B"], [1, 2]))
>>> assign["A", 1].name
'assign_A_1'
add_variable_matrix(name: str, indices: tuple[Iterable[Any], ...] | Iterable[Any] | None = None, lowBound: int | float | Decimal | None = None, upBound: int | float | Decimal | None = None, cat: Literal['Continuous', 'Integer', 'Binary'] = 'Continuous', indexStart: list[Any] | None = None) → list[Any]

Create one variable per index value and return them in a list, or nested lists for several indices, indexed by position. Takes the same arguments as add_variable_dicts().

>>> grid = prob.add_variable_matrix("grid", (["A", "B"], [1, 2]))
>>> grid[0][1].name
'grid_A_2'

Constraints (see How to edit constraints):

addConstraint(constraint: LpAffineExpression, name: str | None = None) → LpConstraint

Adds constraint (an expression with a sense) and returns it as stored in the problem. Edit the returned LpConstraint to change the problem; editing constraint afterwards does not.

constraints() → list[LpConstraint]

The problem’s constraints (LpConstraint), in the order they were added. Editing them edits the problem.

get_constraint_by_name(name: str) → LpConstraint | None

The constraint called name, or None if there is none.

Column-wise modelling (see How to model column-wise):

add_column(name: str, lowBound: int | float | Decimal | None = None, upBound: int | float | Decimal | None = None, cat: Literal['Continuous', 'Integer', 'Binary'] = 'Continuous', obj: float = 0.0, coefficients: Mapping[LpConstraint, float] | None = None) → LpVariable

Add a variable column-wise: with objective coefficient obj and its coefficients in constraints already in the problem (column generation). Returns the new LpVariable.

Parameters:

coefficients – {constraint: coefficient} for constraints of this problem (LpConstraint, e.g. as returned by addConstraint()); zeros are skipped

Raises:

ValueError – on an infinite or NaN coefficient; the problem is left unchanged

>>> c = prob.addConstraint(x >= 1, "c")
>>> y = prob.add_column("y", 0, 1, obj=2, coefficients={c: 1})
remove_variables(variables: Iterable[LpVariable]) → None

Drop variables from every constraint, the objective and every SOS group. The variables themselves stay valid but unused, so the solvers and the LP/MPS writers leave them out.

Some of the more important methods:

solve(solver: LpSolver | None = None, **kwargs: Any) → LpSolveStats

Solve the given Lp problem.

Parameters:

solver – Optional: the specific solver to be used, defaults to the problem’s solver, then to the default solver.

Returns:

an LpSolveStats describing the solve

Side Effects:
  • The variables and constraints of the problem get the values of the solution the solver handed back

roundSolution(epsInt: float = 1e-05, eps: float = 1e-07) → None

Rounds the lp variables

Inputs:
  • none

Side Effects:
  • The lp variables are rounded

setObjective(obj: LpAffineExpression | LpVariable | int | float) → None

Sets the objective function.

Parameters:

obj – the objective function (LpAffineExpression, LpVariable, or numeric)

Side Effects:
  • The objective function is set

writeLP(filename: str, writeSOS: int | bool = 1, mip: bool = True, max_length: int = 100) → list[LpVariable]

Write the given Lp problem to a .lp file.

This function writes the specifications (objective function, constraints, variables) of the defined Lp problem to a file.

Parameters:

filename (str) – the name of the file to be created.

Returns:

variables

Side Effects:
  • The file is created

writeMPS(filename: str, mpsSense: int = 0, rename: bool = False, mip: bool = True, with_objsense: bool = False) → tuple[list[str], list[str], str, list[str]]

Writes an mps file from the problem information.

Parameters:
  • filename – name of the file to write

  • mpsSense – 0 (use problem sense), 1 minimize, -1 maximize

  • rename – if True, normalized names (X0000000, C0000000) are used in the file

  • mip – include integer/binary markers

  • with_objsense – write OBJSENSE section

Returns:

(variable_names, constraint_names, objective_name, pulp_names_in_column_order)

toJson(filename: str, *args: Any, **kwargs: Any) → None

Creates a json file from the LpProblem information

Parameters:
  • filename (str) – filename to write json

  • args – additional arguments for json function

  • kwargs – additional keyword arguments for json function

Returns:

None

classmethod fromJson(filename: str) → tuple[dict[str, LpVariable], LpProblem]

Creates a new LpProblem from a json file with information

Parameters:

filename (str) – json file name

Returns:

a tuple with a dictionary of variables and an LpProblem

Return type:

(dict, LpProblem)

variables() → list[LpVariable]

Problem variables from the Rust model, in id order (same order as constraints()).

Variables and Expressions

class pulp.LpVariable(_var: Variable)

Thin wrapper over the Rust Variable. Created only via LpProblem.add_variable() or add_variable_dicts/dict/matrix.

fixValue() → None

changes lower bound and upper bound to the initial value if exists. :return: None

classmethod fromDataclass(problem: LpProblem, mps: mpslp.MPSVariable)

Initializes a variable from a dataclass by adding it to the given problem.

Parameters:
  • problem – the problem to add the variable to

  • mps – a mpslp.MPSVariable with the variable information

Returns:

a LpVariable

classmethod fromDict(problem: LpProblem, data: dict[str, Any])

Initializes a variable from a dict by adding it to the given problem.

Parameters:
  • problem – the problem to add the variable to

  • data – a dict with the variable information

Returns:

a LpVariable

property id: int

Index of this variable in the model’s variable list (from ModelCore).

isFixed() → bool
Returns:

True if upBound and lowBound are the same

Return type:

bool

toDataclass() → MPSVariable

Exports a variable into a dataclass with its relevant information

Returns:

a mpslp.MPSVariable with the variable information

Return type:

:mpslp.MPSVariable

toDict() → dict[str, Any]

Exports a variable into a dict with its relevant information.

Returns:

a dict with the variable information

Return type:

:dict

Example (variables are created from a problem):

>>> prob = LpProblem("example", LpMinimize)
>>> x = prob.add_variable('x', lowBound=0, cat='Continuous')
>>> y = prob.add_variable('y', upBound=5, cat='Integer')

gives \(x \in [0,\infty)\), \(y \in (-\infty, 5]\), an integer.


class pulp.LpAffineExpression(_expr: AffineExpr)

Bases: object

Thin wrapper around Rust AffineExpr. A linear combination of LpVariables + constant. Optionally carries a constraint sense (for pending constraints created by <=, >=, ==).

Use factory class methods to create instances:
  • LpAffineExpression.empty()

  • LpAffineExpression.from_variable(v)

  • LpAffineExpression.from_constant(c)

  • LpAffineExpression.from_dict({v: coeff, …})

  • LpAffineExpression.from_list([(v, coeff), …])

In brief, \(\textsf{LpAffineExpression([(x[i],a[i]) for i in I])} = \sum_{i \in I} a_i x_i\) where (note the order):

  • x[i] is an LpVariable

  • a[i] is a numerical coefficient.


pulp.lpSum(vector: Iterable[LpAffineExpression | LpVariable | int | float] | Iterable[tuple[LpVariable, float]] | int | float | LpVariable) → LpAffineExpression

Calculate the sum of a list of linear expressions

Parameters:

vector – A list of linear expressions

Constraints

class pulp.LpConstraint(_constr: Constraint)

Bases: object

LP constraint backed by Rust for a row already stored in the model.

Instances appear in LpProblem.constraints() as a list in model order; they are not pending expressions and must not be combined with + / - on affine expressions. Use copy() to obtain a pending LpAffineExpression.

Editing an instance edits the row in the problem: c[x] = 2, del c[x], c.changeRHS(3), c.sense = ... or c += y.

A constraint of a problem, as returned by LpProblem.addConstraint(), LpProblem.constraints() and LpProblem.get_constraint_by_name(). Editing it edits the problem:

>>> c = prob.addConstraint(x + y <= 5, "c")
>>> c[x] = 2           # 2*x + y <= 5
>>> c.changeRHS(8)     # 2*x + y <= 8
>>> c += 3             # 2*x + y <= 5

Editing the expression that was added does not; see How to edit constraints.

__delitem__(key: LpVariable) → None

del c[x]: remove x from this constraint.

__iadd__(other: Any) → LpConstraint

c += expr: add expr to the left-hand side.

__isub__(other: Any) → LpConstraint

c -= expr: subtract expr from the left-hand side.

__setitem__(key: LpVariable, value: float) → None

c[x] = value: same as set_coefficient().

addInPlace(other: Any, sign: Literal[1, -1] = 1) → LpConstraint

Add other (variable, expression, number…) to this row’s left-hand side.

changeRHS(RHS: float) → None

Set the right-hand side of this constraint.

copy() → LpAffineExpression

Return a pending LpAffineExpression copy with sense set.

property id: int

Index of this constraint in the model’s constraint list (from ModelCore).

set_coefficient(var: LpVariable, coefficient: float) → None

Set var’s coefficient in this constraint (0 removes the term), e.g. to add a new column to existing rows.

Combinations and Permutations

pulp.combination(iterable, r)

Return successive r-length combinations of elements in the iterable.

combinations(range(4), 3) –> (0,1,2), (0,1,3), (0,2,3), (1,2,3)

pulp.allcombinations(orgset: list[Any], k: int) → Iterator[tuple[Any, ...]]

returns all combinations of orgset with up to k items

Parameters:
  • orgset – the list to be iterated

  • k – the maxcardinality of the subsets

Returns:

an iterator of the subsets

example:

>>> c = allcombinations([1,2,3,4],2)
>>> for s in c:
...     print(s)
(1,)
(2,)
(3,)
(4,)
(1, 2)
(1, 3)
(1, 4)
(2, 3)
(2, 4)
(3, 4)
pulp.permutation(iterable, r=None)

Return successive r-length permutations of elements in the iterable.

permutations(range(3), 2) –> (0,1), (0,2), (1,0), (1,2), (2,0), (2,1)

pulp.allpermutations(orgset: list[Any], k: int) → Iterator[tuple[Any, ...]]

returns all permutations of orgset with up to k items

Parameters:
  • orgset – the list to be iterated

  • k – the maxcardinality of the subsets

Returns:

an iterator of the subsets

example:

>>> c = allpermutations([1,2,3,4],2)
>>> for s in c:
...     print(s)
(1,)
(2,)
(3,)
(4,)
(1, 2)
(1, 3)
(1, 4)
(2, 1)
(2, 3)
(2, 4)
(3, 1)
(3, 2)
(3, 4)
(4, 1)
(4, 2)
(4, 3)
pulp.value(x: Any) → float | None

Returns the value of the variable/expression x, or x if it is a number