-
-
Notifications
You must be signed in to change notification settings - Fork 405
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SCIPBackend: Faster copy, remove_constraint methods #35103
Merged
vbraun
merged 10 commits into
sagemath:develop
from
mkoeppe:t/34890/scipbackend__faster_copy_method
Feb 25, 2024
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
3725d35
SCIPBackend.__copy__: Use Model(sourceModel=...) for copying
mkoeppe 1ce89ce
GenericBackend.remove_constraints: Fix handling of int
mkoeppe 74ef63f
SCIPBackend.nrows: Use model.getNConss if constraints are not cached
mkoeppe e7c4b9a
SCIPBackend.remove_constraints: New, override generic implementation
mkoeppe 6e211ac
SCIPBackend.__copy__: Use origcopy=True
mkoeppe 094beb3
src/sage/numerical/backends/scip_backend.pyx: docstring edit
mkoeppe 2442318
Merge branch 'develop' into t/34890/scipbackend__faster_copy_method
mantepse e716fb4
Merge pull request #24 from mantepse/numerical/scipbackend__faster_coβ¦
mkoeppe cfeda27
add doctests, fix typos
mantepse cc55c9e
Merge pull request #25 from mantepse/numerical/scipbackend__faster_coβ¦
mkoeppe File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,6 +61,20 @@ cdef class SCIPBackend(GenericBackend): | |
def get_constraints(self): | ||
""" | ||
Get all constraints of the problem. | ||
|
||
EXAMPLES:: | ||
|
||
sage: from sage.numerical.backends.generic_backend import get_solver | ||
sage: lp = get_solver(solver="SCIP") | ||
sage: lp.add_variables(3) | ||
2 | ||
sage: lp.add_linear_constraint(zip([0, 1, 2], [8, 6, 1]), None, 48) | ||
sage: lp.add_linear_constraint(zip([0, 1, 2], [2, 1.5, 0.5]), None, 8) | ||
|
||
sage: lp.get_constraints() | ||
[c1, c2] | ||
sage: lp.row(1) # indirect doctest | ||
([0, 1, 2], [2.0, 1.5, 0.5]) | ||
""" | ||
if self.constraints is None: | ||
self.constraints = self.model.getConss() | ||
|
@@ -71,10 +85,11 @@ cdef class SCIPBackend(GenericBackend): | |
Get the model as a pyscipopt Model. | ||
|
||
EXAMPLES:: | ||
sage: from sage.numerical.backends.generic_backend import get_solver | ||
sage: p = get_solver(solver = "SCIP") | ||
sage: p._get_model() | ||
<pyscipopt.scip.Model object at ... | ||
|
||
sage: from sage.numerical.backends.generic_backend import get_solver | ||
sage: p = get_solver(solver = "SCIP") | ||
sage: p._get_model() | ||
<pyscipopt.scip.Model object at ... | ||
""" | ||
return self.model | ||
|
||
|
@@ -369,6 +384,42 @@ cdef class SCIPBackend(GenericBackend): | |
self.model.delCons(self.get_constraints()[i]) | ||
self.constraints = None | ||
|
||
cpdef remove_constraints(self, constraints) noexcept: | ||
r""" | ||
Remove several constraints. | ||
|
||
INPUT: | ||
|
||
- ``constraints`` -- an iterable containing the indices of the rows to remove | ||
|
||
EXAMPLES:: | ||
|
||
sage: from sage.numerical.backends.generic_backend import get_solver | ||
sage: p = get_solver(solver='SCIP') | ||
sage: p.add_variables(2) | ||
1 | ||
sage: p.add_linear_constraint([(0, 2), (1, 3)], None, 6) | ||
sage: p.add_linear_constraint([(0, 3), (1, 2)], None, 6) | ||
sage: p.row(0) | ||
([0, 1], [2.0, 3.0]) | ||
sage: p.remove_constraints([0, 1]) | ||
sage: p.nrows() | ||
0 | ||
""" | ||
if isinstance(constraints, int): | ||
self.remove_constraint(constraints) | ||
return | ||
|
||
if self.model.getStatus() != 'unknown': | ||
self.model.freeTransform() | ||
self.constraints = None | ||
|
||
all_constraints = self.get_constraints() | ||
to_remove = [all_constraints[i] for i in constraints] | ||
for constraint in to_remove: | ||
self.model.delCons(constraint) | ||
self.constraints = None | ||
|
||
cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None) noexcept: | ||
""" | ||
Add a linear constraint. | ||
|
@@ -528,7 +579,7 @@ cdef class SCIPBackend(GenericBackend): | |
|
||
INPUT: | ||
|
||
- ``indices`` (list of integers) -- this list constains the | ||
- ``indices`` (list of integers) -- this list contains the | ||
indices of the constraints in which the variable's | ||
coefficient is nonzero | ||
|
||
|
@@ -692,6 +743,20 @@ cdef class SCIPBackend(GenericBackend): | |
|
||
EXAMPLES:: | ||
|
||
sage: # needs sage.graphs | ||
sage: g = graphs.CubeGraph(9) | ||
sage: p = MixedIntegerLinearProgram(solver="SCIP") | ||
sage: p.solver_parameter("limits/gap", 100) | ||
sage: b = p.new_variable(binary=True) | ||
sage: p.set_objective(p.sum(b[v] for v in g)) | ||
sage: for v in g: | ||
....: p.add_constraint(b[v]+p.sum(b[u] for u in g.neighbors(v)) <= 1) | ||
sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution | ||
sage: p.solve() # rel tol 100 | ||
1.0 | ||
sage: backend = p.get_backend() | ||
sage: backend.best_known_objective_bound() # random | ||
31.0 | ||
""" | ||
return self.model.getPrimalbound() | ||
|
||
|
@@ -713,12 +778,28 @@ cdef class SCIPBackend(GenericBackend): | |
|
||
EXAMPLES:: | ||
|
||
sage: # needs sage.graphs | ||
sage: g = graphs.CubeGraph(9) | ||
sage: p = MixedIntegerLinearProgram(solver="SCIP") | ||
sage: p.solver_parameter("limits/gap", 100) | ||
sage: b = p.new_variable(binary=True) | ||
sage: p.set_objective(p.sum(b[v] for v in g)) | ||
sage: for v in g: | ||
....: p.add_constraint(b[v]+p.sum(b[u] for u in g.neighbors(v)) <= 1) | ||
sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution | ||
sage: p.solve() # rel tol 100 | ||
1.0 | ||
sage: backend = p.get_backend() | ||
sage: backend.get_relative_objective_gap() # random | ||
46.99999999999999 | ||
|
||
TESTS: | ||
|
||
Just make sure that the variable *has* been defined, and is not just | ||
undefined:: | ||
|
||
sage: backend.get_relative_objective_gap() > 1 # needs sage.graphs | ||
True | ||
""" | ||
return self.model.getGap() | ||
|
||
|
@@ -811,7 +892,26 @@ cdef class SCIPBackend(GenericBackend): | |
sage: p.add_linear_constraints(2, 2, None) | ||
sage: p.nrows() | ||
2 | ||
|
||
TESTS:: | ||
|
||
After calling :meth:`remove_constraints` we know that | ||
`self.constraints is None`. `SCIP` keeps track of the number | ||
of constraints, so we can do the optimization:: | ||
|
||
sage: from sage.numerical.backends.generic_backend import get_solver | ||
sage: p = get_solver(solver='SCIP') | ||
sage: p.add_variables(2) | ||
1 | ||
sage: p.add_linear_constraint([(0, 2), (1, 3)], None, 6) | ||
sage: p.row(0) | ||
([0, 1], [2.0, 3.0]) | ||
sage: p.remove_constraints([0]) | ||
sage: p.nrows() | ||
0 | ||
""" | ||
if self.constraints is None: | ||
return self.model.getNConss() | ||
Comment on lines
+913
to
+914
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add a doctest showing this code path? |
||
return len(self.get_constraints()) | ||
|
||
cpdef col_name(self, int index) noexcept: | ||
|
@@ -870,7 +970,6 @@ cdef class SCIPBackend(GenericBackend): | |
sage: p.set_variable_type(0,0) | ||
sage: p.is_variable_binary(0) | ||
True | ||
|
||
""" | ||
return self.variables[index].vtype() == 'BINARY' | ||
|
||
|
@@ -1155,27 +1254,10 @@ cdef class SCIPBackend(GenericBackend): | |
6.0 | ||
""" | ||
cdef SCIPBackend cp = type(self)(maximization=self.is_maximization()) | ||
cp.model = Model(sourceModel=self.model, origcopy=True) | ||
cp.problem_name(self.problem_name()) | ||
for i, v in enumerate(self.variables): | ||
vtype = v.vtype() | ||
cp.add_variable(self.variable_lower_bound(i), | ||
self.variable_upper_bound(i), | ||
binary=vtype == 'BINARY', | ||
continuous=vtype == 'CONTINUOUS', | ||
integer=vtype == 'INTEGER', | ||
obj=self.objective_coefficient(i), | ||
name=self.col_name(i)) | ||
assert self.ncols() == cp.ncols() | ||
|
||
for i in range(self.nrows()): | ||
coefficients = zip(*self.row(i)) | ||
lower_bound, upper_bound = self.row_bounds(i) | ||
name = self.row_name(i) | ||
cp.add_linear_constraint(coefficients, | ||
lower_bound, | ||
upper_bound, | ||
name=name) | ||
assert self.nrows() == cp.nrows() | ||
cp.obj_constant_term = self.obj_constant_term | ||
cp.variables = cp.model.getVars() | ||
return cp | ||
|
||
cpdef solver_parameter(self, name, value=None) noexcept: | ||
|
@@ -1189,6 +1271,13 @@ cdef class SCIPBackend(GenericBackend): | |
- ``value`` -- the parameter's value if it is to be defined, | ||
or ``None`` (default) to obtain its current value. | ||
|
||
EXAMPLES: | ||
|
||
sage: from sage.numerical.backends.generic_backend import get_solver | ||
sage: lp = get_solver(solver="SCIP") | ||
sage: p.solver_parameter("limits/time", 1) | ||
sage: p.solver_parameter("limits/time") | ||
1.0 | ||
""" | ||
if value is not None: | ||
if name.lower() == 'timelimit': | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you extend this doctest to show the constraints have been removed?
It would also be good to have another doctest showing particular constraints (or just one) have been removed and the remaining are properly set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, I'll add these tests