PuLP Documentation

repository·master·Indexed 25 days ago

https://github.com/coin-or/pulp

PuLP is a Python-based linear and mixed-integer programming (MILP) modeler. It allows users to define optimization problems and solve them using various open-source and proprietary solvers, including GLPK, COIN CLP/CBC, CPLEX, and GUROBI. PuLP can generate MPS or LP files and provides utilities like lpSum for efficient formulation, pulp.allcombinations for combinatorial problems, and pulp.makeDict for creating cost lookup tables.

Tokens
19.7K
Snippets
39
Records
132
Agent score
81%

What's inside PuLP

  1. Overview of PuLP optimization capabilities

    master

    PuLP is a Python-based modeler for linear and mixed integer programming (MILP). It allows you to create optimization problems and solve them using various open-source or proprietary solvers. PuLP can generate MPS or LP files to interface with solvers.

    Supported solvers include:

    • Open-source: GLPK, COIN-OR CLP/CBC, HiGHS, SCIP/FSCIP.
    • Proprietary: CPLEX, GUROBI, MOSEK, XPRESS, CHOCO, MIPCL.
    • Other: OR-Tools CP-SAT (via the CPSAT API).
  2. Use PuLP_LPARRAY for NumPy-style linear programming

    master

    PuLP_LPARRAY is an extension for PuLP that integrates NumPy-style array operations with PuLP's linear programming objects (LpVariable, LpAffineExpression, and LpConstraint). It allows you to manage sets of linear variables using broadcasting, reshaping, and indexing, eliminating the need for manual loops or complex indexing when building models.

    Key features include:

    • Array-based variable sets: Use NumPy-like syntax for variable manipulation.
    • Efficient Linearization: Special support functions for linearizing operations like min, max, abs, clip-to-binary, and boolean operators.
    • Axis Support: Wide support for the axis keyword in operations like .sum().
  3. Understand Python classes and instances

    master

    Classes allow you to group data (attributes) and behaviors (methods) together.

    • Class Attributes: Variables defined directly in the class body that are shared by all instances.
    • __init__ method: The constructor used to initialize a new instance with specific attributes.
    • self: An implied first parameter in class methods that refers to the specific instance being operated on.
    • __str__ method: Defines the string representation of the object when print() is called on an instance.
  4. How to use Python lists, tuples, and dictionaries

    master

    PuLP modeling relies heavily on Python's collection types. Understanding the syntax for creation and access is essential:

    • Lists: Ordered sequences created with square brackets []. They are mutable.
    • Tuples: Ordered sequences created with round brackets () and commas. They are immutable (cannot be modified after creation).
    • Dictionaries: Key-value pairs created with curly brackets {}. They allow non-consecutive keys (like strings or floats) to map to values.

    Note on Accessing Elements: Regardless of how the collection was created, you always use square brackets [] to access elements or values (e.g., my_list[0] or my_dict['key']).

  5. Organize new tests in PuLP

    master

    When adding new functionality or fixing issues, place your tests in the pulp/tests/ directory using the following conventions:

    • Model or I/O tests: Add to test_pulp.py (using PuLPModelTest).
    • Shared solve tests: Add to solver_common.py.
    • Solver-specific overrides: Add to test_<solver>.py (e.g., test_cbc.py).
  6. Work with LpAffineExpression and lpSum

    master

    An LpAffineExpression represents a linear combination of variables. It is mathematically defined as $\sum_{i \in I} a_i x_i$, where $x_i$ is an LpVariable and $a_i$ is a numerical coefficient.

    You can construct these expressions using the lpSum function, which is a convenient way to sum multiple terms (variables multiplied by coefficients) to form objective functions or constraint sides.

  7. Understand Linear Programming (LP) requirements

    master

    A Linear Program is a mathematical program that must satisfy three conditions:

    1. Decision Variables: Must be real variables.
    2. Objective: Must be a linear expression.
    3. Constraints: Must be linear expressions.

    A linear expression follows the form: a_1 x_1 + a_2 x_2 + ... + a_n x_n {<=, =, >=} b where a_i and b are known constants and x_i are variables.

    Linear programs are typically solved using the Revised Simplex Method (Primal Simplex), the Dual Simplex Method, or an Interior Point Method.

  8. Understand the optimization and modeling process

    master

    Solving an optimization problem with PuLP follows a non-linear process that can be broken down into five general steps. PuLP acts as a 'shortcut' by allowing you to formulate the mathematical program directly in Python, which can then be passed to various solvers (e.g., CPLEX, COIN, gurobi) without manually entering the program into solver-specific software.

    The 5 Steps of Optimization:

    1. Getting the problem description: Moving from an abstract description to a formal, rigorous model description.
    2. Formulating the mathematical program: Translating the problem into math. This involves:
      • Identifying Decision Variables (quantifiable decisions).
      • Formulating the Objective Function (minimizing or maximizing a goal, like cost or profit).
      • Formulating Constraints (logical or explicit restrictions expressed via decision variables).
      • Identifying Data (the 'hard numbers' for variable bounds and coefficients).
    3. Solving the mathematical program: Using algorithms (like Revised Simplex or Interior Point Methods) or heuristics to find an optimal or near-optimal solution.
    4. Performing post-optimal analysis: Examining the robustness of the solution by testing how changes in data (e.g., increasing costs) affect the outcome.
    5. Presenting the solution and analysis: Translating mathematical results back into actionable business decisions for stakeholders.
  9. Access underlying solver models via solverModel

    master

    PuLP allows access to the official solver's native API objects through the prob.solverModel attribute. This attribute is populated when buildSolverModel() is executed (usually during prob.solve()). This is useful for accessing advanced features like dual prices, extreme rays, or specific solver parameters not exposed by PuLP.

    Example: Accessing OR-Tools CpModel:

    import pulp
    prob = pulp.LpProblem("name", pulp.LpMinimize)
    x = prob.add_variable("x", lowBound=0)
    prob += x
    status = prob.solve(pulp.CPSAT(msg=False))
    model = prob.solverModel  # This is an ortools.sat.python.cp_model.CpModel

    Example: Accessing CPLEX API before solving: You can use lower-level methods to manipulate the solver model before the actual solve call:

    import pulp
    prob = pulp.LpProblem('name', pulp.LpMinimize)
    x = prob.add_variable('x', lowBound=0)
    prob += x
    
    solver = pulp.CPLEX_PY()
    solver.buildSolverModel(prob)
    # Edit the object before solving (e.g., loading MIP starts)
    solver.solverModel.MIP_starts.read(SOME_MST_FILE)
    # Solve and then fill PuLP variables
    solver.callSolver(prob)
    status = solver.findSolutionValues(prob)
    import pulp
    prob = pulp.LpProblem("name", pulp.LpMinimize)
    x = prob.add_variable("x", lowBound=0)
    prob += x
    status = prob.solve(pulp.CPSAT(msg=False))
    prob.solverModel  # ortools.sat.python.cp_model.CpModel
  10. Find all solutions to a problem

    master

    If a problem (like an under-constrained Sudoku) has multiple valid solutions, you can find them iteratively. After a successful solve, add a new constraint that prevents the solver from picking the exact same combination of variables again. Repeat this process in a loop until the solver can no longer find a feasible solution.

    Pattern:

    1. Solve the problem.
    2. If successful, record the solution.
    3. Add a constraint that excludes the current solution (e.g., by ensuring the sum of the current variable values is less than the total number of variables).
    4. Repeat.
  11. Understand the orloge output dictionary structure

    master

    When calling orloge.get_info_log_solver, the returned dictionary contains the following structured data:

    • best_bound: The best lower bound found.
    • best_solution: The best objective value found.
    • cut_info: A dictionary containing best_bound, best_solution, time, and a cuts dictionary (e.g., counts for Clique, Gomory, Implied bound, MIR).
    • first_relaxed: The first relaxed objective value.
    • first_solution: The first solution objective value.
    • gap: The optimality gap.
    • matrix: Information about the original matrix (constraints, nonzeros, variables).
    • matrix_post: Information about the matrix after presolve.
    • nodes: Number of nodes explored.
    • presolve: Details on presolve performance (cols, rows, time).
    • progress: A pandas.DataFrame containing the step-by-step solver progress (e.g., Node, NodesLeft, Objective, Depth, Gap, Time).
    • rootTime: Time spent at the root node.
    • sol_code: Solver solution code.
    • solver: The name of the solver used.
    • status: Human-readable status string.
    • status_code: Numerical status code.
    • time: Total execution time.
    • version: Solver version.
    # Example of the dictionary structure returned by orloge
    {
        'best_bound': -41.0,
        'best_solution': -41.0,
        'cut_info': {
                     'best_bound': -167.97894,
                     'best_solution': -41.0,
                     'cuts': {'Clique': 1, 'Gomory': 16, 'Implied bound': 23, 'MIR': 22},
                     'time': 21.0
                    },
        'first_relaxed': -178.94318,
        'first_solution': -41.0,
        'gap': 0.0,
        'matrix': {'constraints': 53467, 'nonzeros': 199175, 'variables': 26871},
        'matrix_post': {'constraints': 35616, 'nonzeros': 149085, 'variables': 22010},
        'nodes': 526.0,
        'presolve': {'cols': 4861, 'rows': 17851, 'time': 3.4},
        'progress': <pandas.DataFrame>,
        'rootTime': 0.7,
        'sol_code': 1,
        'solver': 'GUROBI',
        'status': 'Optimal solution found',
        'status_code': 1,
        'time': 46.67,
        'version': '7.0.0'
    }
  12. Understand Integer Programming (IP) and Mixed Integer Programming (MIP)

    master

    Integer Programming is a variation of Linear Programming where some decision variables are restricted to integer values (known as integer variables).

    • Mixed Integer Programs (MIPs): Programs that contain a combination of both continuous (real) variables and integer variables.
    • Solving Method: Integer programs are typically solved using the branch-and-bound process.
    • Complexity Warning: For MIPs of any reasonable size, the solution time grows exponentially as the number of integer variables increases.