Source code for flopt.solvers.pulp_search

import pulp

from flopt.solvers.base import BaseSearch
from flopt.expression import Const
from flopt.solution import Solution
from flopt.constants import (
    VariableType,
    ExpressionType,
    ConstraintType,
    SolverTerminateState,
)
from flopt.env import setup_logger

logger = setup_logger(__name__)


class LpVariable(pulp.LpVariable):
    def __init__(self, name, lowBound, upBound, cat):
        super().__init__(name, lowBound=lowBound, upBound=upBound, cat=cat)

    def value(self):
        """for creating the objective and constraints of pulp"""
        return self

    def getValue(self):
        return self.varValue


[docs]class PulpSearch(BaseSearch): """API for PuLP, linear programming modeling tool Parameters ---------- solver : pulp.Solver solver pulp use, see https://coin-or.github.io/pulp/technical/solvers.html. default is pulp.PULP_CBC_CMD Examples -------- .. code-block:: python import flopt # Variables a = flopt.Variable("a", lowBound=0, upBound=1, cat="Integer") b = flopt.Variable("b", lowBound=1, upBound=2, cat="Continuous") c = flopt.Variable("c", lowBound=-1, upBound=3, cat="Continuous") # Problem prob = flopt.Problem() prob += a + b + c + 2 prob += a + b >= 2 prob += b - c >= 3 prob.solve(solver="Pulp", msg=True) When you use other solvers in pulp, for example GLPK solver, you set solver parameter in PulpSearch object. .. code-block:: python solver = flopt.Solver("Pulp") import pulp glpk_solver = pulp.GLPK_CMD() solver.setParams(solver=glpk_solver) prob.solve(solver, msg=True) """ name = "Pulp" can_solve_problems = { "Variable": VariableType.Number, "Objective": ExpressionType.Linear, "Constraint": ExpressionType.Linear, } def __init__(self): super().__init__() self.solver = None def search(self, solution, *args): self.start_build() lp_prob, lp_solution = self.createLpProblem(solution, self.prob) self.end_build() if self.solver is not None: solver = self.solver else: solver = pulp.PULP_CBC_CMD( timeLimit=max(0, self.timelimit - self.build_time), msg=self.msg ) lp_status = lp_prob.solve(solver) # get result for lp_var in lp_solution: name = lp_var.getName() value = lp_var.getValue() if lp_var.cat in {pulp.LpInteger, pulp.LpBinary}: value = round(value) solution.setValue(name, value) # update best solution if needed self.registerSolution(solution) # lp_status = -1: infeasible # -2: unbounded # -3: undefined logger.info(f"PuLP LpStatus {pulp.constants.LpStatus[lp_status]}") if lp_status == -1: return SolverTerminateState.Infeasible elif lp_status == -2: return SolverTerminateState.Unbounded elif lp_status == -3: return SolverTerminateState.Abnormal return SolverTerminateState.Normal def createLpProblem(self, solution, prob): """Convert Problem into pulp.LpProblem Parameters ---------- solution : Solution prob : Problem Returns ------- pulp.LpProblem, Solution """ # conver VarElement -> LpVariable lp_variables = [] for var in solution: if var.type() == VariableType.Continuous: cat = "Continuous" elif var.type() == VariableType.Integer: cat = "Integer" elif var.type() == VariableType.Binary: cat = "Binary" else: raise ValueError(var.type()) lp_var = LpVariable( var.name, lowBound=var.getLb(), upBound=var.getUb(), cat=cat ) lp_variables.append(lp_var) lp_solution = Solution(lp_variables) # conver Problem -> pulp.LpProblem name = "" if self.name is None else self.name sense = ( pulp.LpMinimize if prob.sense in {"minimize", "Minimize"} else pulp.LpMaximize ) lp_prob = pulp.LpProblem(name=name, sense=sense) if not isinstance(prob.obj, Const): lp_prob.setObjective(prob.obj.value(lp_solution)) for const in prob.getConstraints(): const_exp = const.expression.value(lp_solution) if not isinstance(const_exp, (int, float)): if const.type() == ConstraintType.Eq: lp_prob.addConstraint(const_exp == 0, const.name) else: # const.type() == ConstraintType.Le lp_prob.addConstraint(const_exp <= 0, const.name) return lp_prob, lp_solution