Reputation: 191
Is there any way to remove defined constraint from solver with out clearing solver and creating constraints from first?
suppose my problem is to maximize sum of 3 variables which two constraints
constraint1: variable 2 should be between 8 - 10
constraint2: variable 3 should be between 5 - 10
from ortools.linear_solver import pywraplp
solver = pywraplp.Solver('SolveIntegerProblem',
pywraplp.Solver.CBC_MIXED_INTEGER_PROGRAMMING)
objective = solver.Objective()
Variable[0] = solver.IntVar(0, 5, variable 0 )
Variable[1] = solver.IntVar(0, 10, variable 1 )
Variable[2] = solver.IntVar(0, 20, variable 2 )
objective.SetCoefficient(Variable[0], 1)
objective.SetCoefficient(Variable[1], 1)
objective.SetCoefficient(Variable[2], 1)
objective.SetMaximization()
constraints.append(solver.Constraint(8,10))
constraints[0].SetCoefficient(variable[1],1)
constraints.append(solver.Constraint(5,10))
constraints[1].SetCoefficient(variable[2],1)
Now in the second time of running my code I want to remove constraint number 2, but I can not find any operation to do it and the only way is to clear solver and define constraint from first.
In this semi code the number of constraints were limited but actually, in my real code the number of constraint are many and I can not define them from first.
Upvotes: 5
Views: 3005
Reputation: 88
You can make a constraint innactive but not remove it. TO deactivate it, just set its lower/upper bound to -inf, +inf, respectively. constraints[1].SetBounds(-solver.infinity(),solver.infinity())
The pre-processing will remove them before execution. This does not solve the memory within python (as the array does not change size), but there is no deletion method implemented in pywraplp yet (07/2023). So I think that's the closest you can get to it.
You can see in the snippet below, the +/- inf constraints do not even appear in the print of the LP format:
from ortools.linear_solver import pywraplp
solver = pywraplp.Solver('SolveIntegerProblem',
pywraplp.Solver.CBC_MIXED_INTEGER_PROGRAMMING)
objective = solver.Objective()
variable = {}
variable[0] = solver.IntVar(0, 5, "variable0" )
variable[1] = solver.IntVar(0, 10, "variable1" )
variable[2] = solver.IntVar(0, 20, "variable2" )
objective.SetCoefficient(variable[0], 1)
objective.SetCoefficient(variable[1], 1)
objective.SetCoefficient(variable[2], 1)
objective.SetMaximization()
constraints = []
constraints.append(solver.Constraint(8,10))
constraints[0].SetCoefficient(variable[1],1)
constraints.append(solver.Constraint(5,10))
constraints[1].SetCoefficient(variable[2],1)
solver.Solve()
# normal solution
print('Solution 1:')
print('Objective value =', objective.Value())
for i in range(3):
print(variable[i].name(), '=', variable[i].solution_value())
lp_model = solver.ExportModelAsLpFormat(obfuscated=False)
print(lp_model)
print("\n\n")
# remove seccond constraint by setting its bounds to -infinity and infinity
constraints[1].SetBounds(-solver.infinity(),solver.infinity())
solver.Solve()
print('Solution 2:')
print('Objective value =', objective.Value())
for i in range(3):
print(variable[i].name(), '=', variable[i].solution_value())
lp_model = solver.ExportModelAsLpFormat(obfuscated=False)
print(lp_model)
Output:
Solution 1:
Objective value = 25.0
variable0 = 5.0
variable1 = 10.0
variable2 = 10.0
\ Generated by MPModelProtoExporter
\ Name : SolveIntegerProblem
\ Format : Free
\ Constraints : 2
\ Variables : 3
\ Binary : 0
\ Integer : 3
\ Continuous : 0
Maximize
Obj: +1 variable0 +1 variable1 +1 variable2
Subject to
auto_c_000000000_rhs: +1 variable1 <= 10
auto_c_000000000_lhs: +1 variable1 >= 8
auto_c_000000001_rhs: +1 variable2 <= 10
auto_c_000000001_lhs: +1 variable2 >= 5
Bounds
0 <= variable0 <= 5
0 <= variable1 <= 10
0 <= variable2 <= 20
Generals
variable0
variable1
variable2
End
Solution 2:
Objective value = 35.0
variable0 = 5.0
variable1 = 10.0
variable2 = 20.0
\ Generated by MPModelProtoExporter
\ Name : SolveIntegerProblem
\ Format : Free
\ Constraints : 2
\ Variables : 3
\ Binary : 0
\ Integer : 3
\ Continuous : 0
Maximize
Obj: +1 variable0 +1 variable1 +1 variable2
Subject to
auto_c_000000000_rhs: +1 variable1 <= 10
auto_c_000000000_lhs: +1 variable1 >= 8
Bounds
0 <= variable0 <= 5
0 <= variable1 <= 10
0 <= variable2 <= 20
Generals
variable0
variable1
variable2
End
Upvotes: 0
Reputation: 1476
I know this question is quite old but:
As far as I know, or-tools does not provide any interface that removes constraints or variables. From an engineering perspective, messing with the internal logic to remove them 'by hand' is dangerous.
I absolutely needed that feature for my tech stack and tried multiple python linear programming librairies out there (wrappers around clp/cbc really) and I settle on or-tools despite that flaw for 2 main reasons 1) this was the only librairy with the minimal features support I required out of the box and 2) at the time (~4-5 years ago) it was the only librairy using C bindings.
All others used one form of another of interfacing with the cbc command line which is a ... horrible way to interface with python. It is unscalable due to the overhead of writing and reading files on disk. Nasty nasty nasty. So if I remember correctly, only pylp and or-tools had c bindings and again if I remember correctly, pylp was NOT python 3 compatible (and has been in limbo ever since) so I settled on or-tools.
So to answer your question: to 'remove' variables or constraints with or-tools, I had to build my own python wrapper around or-tools. To deactivate a variable or a constraint, I would set coefficients to zero and free bounds (set to +/- infinity) and set costs to zero to effectively deactivate the constraint. In my wrapper, I would keep a list of deactivated constraints/variables and recycle them instead of creating new ones (which was proven to lead to both increased runtimes and memory leaks because C++ + python is a nightmare in those areas). I heavily suspect that I get floating points noise in the recycling but it's stable enough in practice for my needs.
So in your code example, to rerun without creating an new model from scratch you need to do:
(...)
constr1 = solver.Constraint(8,10)
constraints.append(constr1)
constraints[0].SetCoefficient(variable[1],1)
constr2 = solver.Constraint(5,10)
constraints.append(constr2)
constraints[1].SetCoefficient(variable[2],1)
constr2.SetBounds(-solver.infinity(), solver.infinity())
constr2.SetCoefficient(variable[2], 0)
# constr2 is now deactivated. If you wanted to add a new constraints, you can
# change the bounds on constr2 to recycle it and add new variables
# coefficients
That being said, very recently, python-mip was released and it supports both removing variables and constraints and has c-bindings.
Upvotes: 1
Reputation: 9271
Did you try to use the MPConstraint::Clear()
method ?
Declaration: https://github.com/google/or-tools/blob/9487eb85f4620f93abfed64899371be88d65c6ec/ortools/linear_solver/linear_solver.h#L865
Definition: https://github.com/google/or-tools/blob/9487eb85f4620f93abfed64899371be88d65c6ec/ortools/linear_solver/linear_solver.cc#L101
Concerning Python swig wrapper MPConstraint
is exported as Constraint
object.
src: https://github.com/google/or-tools/blob/9487eb85f4620f93abfed64899371be88d65c6ec/ortools/linear_solver/python/linear_solver.i#L180
But the method Constraint::Clear()
seems not exposed
https://github.com/google/or-tools/blob/9487eb85f4620f93abfed64899371be88d65c6ec/ortools/linear_solver/python/linear_solver.i#L270
You can try to patch the swig file and recompile make python && make install_python
Upvotes: 0