Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased
### Added
- Added `Model.captureCons()` and `Model.releaseCons()`
### Fixed
### Changed
- Speed up `Expr.__add__` and `Expr.__iadd__` via the C-level API
Expand Down
26 changes: 25 additions & 1 deletion src/pyscipopt/scip.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -8568,7 +8568,31 @@ cdef class Model:

"""
PY_SCIP_CALL(SCIPdelConsLocal(self._scip, cons.scip_cons))


def captureCons(self, Constraint cons):
"""
Increase the usage counter of a constraint. Must be matched by a releaseCons.

Parameters
----------
cons : Constraint
constraint to capture

"""
PY_SCIP_CALL(SCIPcaptureCons(self._scip, cons.scip_cons))

def releaseCons(self, Constraint cons):
"""
Decrease the usage counter of a constraint; frees it when the counter reaches zero.

Parameters
----------
cons : Constraint
constraint to release
Comment on lines +8588 to +8591

"""
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &cons.scip_cons))

def getValsLinear(self, Constraint cons):
"""
Retrieve the coefficients of a linear constraint
Expand Down
2 changes: 2 additions & 0 deletions src/pyscipopt/scip.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,7 @@ class Model:
def calcNodeselPriority(
self, variable: Incomplete, branchdir: Incomplete, targetvalue: Incomplete
) -> Incomplete: ...
def captureCons(self, cons: Incomplete) -> Incomplete: ...
def catchEvent(
self, eventtype: Incomplete, eventhdlr: Incomplete
) -> Incomplete: ...
Expand Down Expand Up @@ -1487,6 +1488,7 @@ class Model:
def readSolFile(self, filename: Incomplete) -> Incomplete: ...
def redirectOutput(self) -> Incomplete: ...
def relax(self) -> Incomplete: ...
def releaseCons(self, cons: Incomplete) -> Incomplete: ...
def releaseRow(self, row: Incomplete) -> Incomplete: ...
def repropagateNode(self, node: Incomplete) -> Incomplete: ...
def resetParam(self, name: Incomplete) -> Incomplete: ...
Expand Down
19 changes: 19 additions & 0 deletions tests/test_cons.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,3 +368,22 @@ def test_getValsLinear():
@pytest.mark.skip(reason="TODO: test getRowLinear()")
def test_getRowLinear():
assert True


def test_captureCons_releaseCons():
m = Model()
x = m.addVar("x")
c = m.addCons(x <= 1)

# Pair capture+release: problem still holds its own capture, so this is safe.
m.captureCons(c)
m.releaseCons(c)

# Model continues to function — problem's capture survived.
m.optimize()
assert m.getStatus() == "optimal"

with pytest.raises(TypeError):
m.captureCons("not a constraint")
with pytest.raises(TypeError):
m.releaseCons("not a constraint")
Loading