diff --git a/src/pulp/pulp.py b/src/pulp/pulp.py old mode 100755 new mode 100644 index 0acc157..6c6a842 --- a/src/pulp/pulp.py +++ b/src/pulp/pulp.py @@ -1682,6 +1682,15 @@ class LpProblem(object): def setInitial(self,values): self.initialValues = values + def numVariables(self): + return len(self._variable_ids) + + def numConstraints(self): + return len(self.constraints) + + def getSense(self): + return self.sense + class FixedElasticSubProblem(LpProblem): """ Contains the subproblem generated by converting a fixed constraint diff --git a/src/pulp/solvers.py b/src/pulp/solvers.py index 7d2cbbd..625f15b 100644 --- a/src/pulp/solvers.py +++ b/src/pulp/solvers.py @@ -1232,7 +1232,7 @@ class XPRESS(LpSolver_CMD): "Optimal":LpStatusOptimal, } if statusString not in xpressStatus: - raise PulpSolverError("Unknow status returned by XPRESS: "+statusString) + raise PulpSolverError("Unknown status returned by XPRESS: "+statusString) status = xpressStatus[statusString] values = {} while 1: @@ -2365,3 +2365,140 @@ except(ImportError): def ctypesArrayFill(myList, type = None): return None +class GurobiFormulation(object): + """ + The Gurobi LP/MIP solver (via its python interface) + without holding our own copy of the constraints + + Contributed by Ben Hollis + + This is an experimental interface that implements some of the + LpProblem interface, this should probably be done with an ABC + Also needs tests + """ + try: + sys.path.append(gurobi_path) + global gurobipy + import gurobipy + except: + def __init__(self, sense): + raise PulpSolverError("GUROBI: Not Available") + else: + + def __init__(self, name, sense): + self.gurobi_model = gurobipy.Model(name) + self.sense = sense + if sense == LpMaximize: + self.gurobi_model.setAttr("ModelSense", -1) + self.varables = {} + self.objective = None + self.status = None + + def addVariable(self, v): + if v.name not in self.varables: + self.varables[v.name] = v + lower_bound = v.getLb() + if lower_bound is None: + lower_bound = -gurobipy.GRB.INFINITY + upper_bound = v.getUb() + if upper_bound is None: + upper_bound = gurobipy.GRB.INFINITY + varType = gurobipy.GRB.CONTINUOUS + if v.isInteger(): + varType = gurobipy.GRB.INTEGER + v.solver_var = self.gurobi_model.addVar(lower_bound, upper_bound, vtype = varType, obj = 0, name = v.name) + return v + + def update(self): + self.gurobi_model.update() + + def numVariables(self): + return self.gurobi_model.getAttr('NumVars') + + def numConstraints(self): + return self.gurobi_model.getAttr('NumConstrs') + + def getSense(self): + return self.sense + + def addVariables(self, variables): + [self.addVariable(v) for v in variables] + + def add(self, constraint, name = None): + self.addConstraint(constraint, name) + + def solve(self, callback = None): + print("***Solving using thin Gurobi Formulation") + self.gurobi_model.reset() + for var, coeff in self.objective.items(): + var.solver_var.setAttr("Obj", coeff) + self.gurobi_model.optimize(callback = callback) + return self.findSolutionValues() + + def findSolutionValues(self): + for var in self.varables.values(): + try: + var.varValue = var.solver_var.X + except gurobipy.GurobiError: + pass + GRB = gurobipy.GRB + gurobiLPStatus = { + GRB.OPTIMAL: LpStatusOptimal, + GRB.INFEASIBLE: LpStatusInfeasible, + GRB.INF_OR_UNBD: LpStatusInfeasible, + GRB.UNBOUNDED: LpStatusUnbounded, + GRB.ITERATION_LIMIT: LpStatusNotSolved, + GRB.NODE_LIMIT: LpStatusNotSolved, + GRB.TIME_LIMIT: LpStatusNotSolved, + GRB.SOLUTION_LIMIT: LpStatusNotSolved, + GRB.INTERRUPTED: LpStatusNotSolved, + GRB.NUMERIC: LpStatusNotSolved, + } + self.status = gurobiLPStatus.get(self.gurobi_model.Status, LpStatusUndefined) + return self.status + + def addConstraint(self, constraint, name = None): + if not isinstance(constraint, LpConstraint): + raise TypeError("Can only add LpConstraint objects") + if name: + constraint.name = name + try: + if constraint.name: + name = constraint.name + else: + name = self.unusedConstraintName() + except AttributeError: + raise TypeError("Can only add LpConstraint objects") + #if self._addVariables(constraint.keys()): + #self.gurobi_model.update() + + + expr = gurobipy.LinExpr(constraint.values(), [v.solver_var for v in constraint.keys()]) # Solver_var is added inside addVariable + if constraint.sense == LpConstraintLE: + relation = gurobipy.GRB.LESS_EQUAL + elif constraint.sense == LpConstraintGE: + relation = gurobipy.GRB.GREATER_EQUAL + elif constraint.sense == LpConstraintEQ: + relation = gurobipy.GRB.EQUAL + else: + raise PulpSolverError('Detected an invalid constraint type') + self.gurobi_model.addConstr(expr, relation, -constraint.constant, name) + + def __iadd__(self, other): + if isinstance(other, tuple): + other, name = other + else: + name = None + if other is True: + return self + elif isinstance(other, LpConstraint): + self.addConstraint(other, name) + elif isinstance(other, LpAffineExpression): + self.objective = other + self.objective.name = name + elif isinstance(other, LpVariable) or type(other) in [int, float]: + self.objective = LpAffineExpression(other) + self.objective.name = name + else: + raise TypeError("Can only add LpConstraint, LpAffineExpression or True objects") + return self \ No newline at end of file