Skip to content

Commit e73ed73

Browse files
Merge branch 'master' into addmatrixcons
2 parents 5a58058 + ec86843 commit e73ed73

File tree

9 files changed

+119
-19
lines changed

9 files changed

+119
-19
lines changed

.github/workflows/coverage.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ jobs:
4040
4141
- name: Install PySCIPOpt
4242
run: |
43-
export CFLAGS="-O0 -ggdb" # disable compiler optimizations for faster builds
44-
python -m pip install .
43+
export CFLAGS="-O0 -ggdb -Wall -Wextra -Werror" # Debug mode. More warnings. Warnings as errors.
44+
python -m pip install . -v 2>&1 | tee build.log
45+
grep -i "warning" build.log || true
4546
4647
- name: Run pyscipopt tests
4748
run: |

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Unreleased
44
### Added
5+
- Added recipe for getting local constraints
56
- More support for AND-Constraints
67
- Added support for knapsack constraints
78
- Added isPositive(), isNegative(), isFeasLE(), isFeasLT(), isFeasGE(), isFeasGT(), isHugeValue(), and tests
@@ -13,6 +14,7 @@
1314
- Generalized getLhs() and getRhs() to additionally support any linear type constraint
1415
### Fixed
1516
- Raised an error when an expression is used when a variable is required
17+
- Fixed some compile warnings
1618
### Changed
1719
- MatrixExpr.sum() now supports axis arguments and can return either a scalar or MatrixExpr depending on the result dimensions
1820
- AddMatrixCons supports accepting ExprCons following matrix dimension reduction via MatrixExpr.sum() or more operation.

src/pyscipopt/event.pxi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ cdef class Eventhdlr:
3939

4040

4141
# local helper functions for the interface
42-
cdef Eventhdlr getPyEventhdlr(SCIP_EVENTHDLR* eventhdlr) noexcept with gil:
42+
cdef Eventhdlr getPyEventhdlr(SCIP_EVENTHDLR* eventhdlr):
4343
cdef SCIP_EVENTHDLRDATA* eventhdlrdata
4444
eventhdlrdata = SCIPeventhdlrGetData(eventhdlr)
4545
return <Eventhdlr>eventhdlrdata

src/pyscipopt/expr.pxi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ cdef class ExprCons:
358358
def __repr__(self):
359359
return 'ExprCons(%s, %s, %s)' % (self.expr, self._lhs, self._rhs)
360360

361-
def __nonzero__(self):
361+
def __bool__(self):
362362
'''Make sure that equality of expressions is not asserted with =='''
363363

364364
msg = """Can't evaluate constraints as booleans.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from pyscipopt import Model, Constraint
2+
3+
def getLocalConss(model: Model, node = None) -> list[list[Constraint]]:
4+
"""
5+
Returns local constraints.
6+
7+
Parameters
8+
----------
9+
model : Model
10+
The model from which to retrieve the local constraints.
11+
node : Node, optional
12+
The node from which to retrieve the local constraints. If not provided, the current node is used.
13+
14+
Returns
15+
-------
16+
list[Constraint]
17+
A list of local constraints. First entry are global constraints, second entry are all the added constraints.
18+
"""
19+
20+
if node is None:
21+
assert model.getStageName() in ["INITPRESOLVE", "PRESOLVING", "EXITPRESOLVE", "SOLVING"], "Model cannot be called in stage %s." % model.getStageName()
22+
cur_node = model.getCurrentNode()
23+
else:
24+
cur_node = node
25+
26+
added_conss = []
27+
while cur_node is not None:
28+
added_conss = cur_node.getAddedConss() + added_conss
29+
cur_node = cur_node.getParent()
30+
31+
return [model.getConss(), added_conss]
32+
33+
def getNLocalConss(model: Model, node = None) -> tuple[int,int]:
34+
"""
35+
Returns the number of local constraints of a node.
36+
37+
Parameters
38+
----------
39+
model : Model
40+
The model from which to retrieve the number of local constraints.
41+
node : Node, optional
42+
The node from which to retrieve the number of local constraints. If not provided, the current node is used.
43+
44+
Returns
45+
-------
46+
list[int]
47+
A list of the number of local constraints. First entry is the number of global constraints, second entry is the number of all the added constraints.
48+
"""
49+
local_conss = getLocalConss(model, node)
50+
return [len(local_conss[0]), len(local_conss[1])]

src/pyscipopt/recipes/nonlinear.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from pyscipopt import Model
22

3-
43
def set_nonlinear_objective(model: Model, expr, sense="minimize"):
54
"""
65
Takes a nonlinear expression and performs an epigraph reformulation.

src/pyscipopt/scip.pxd

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -408,12 +408,6 @@ cdef extern from "scip/scip.h":
408408
ctypedef struct SCIP_PROPDATA:
409409
pass
410410

411-
ctypedef struct SCIP_PROPTIMING:
412-
pass
413-
414-
ctypedef struct SCIP_PRESOLTIMING:
415-
pass
416-
417411
ctypedef struct SCIP_PRESOL:
418412
pass
419413

@@ -456,9 +450,6 @@ cdef extern from "scip/scip.h":
456450
ctypedef struct SCIP_PRESOL:
457451
pass
458452

459-
ctypedef struct SCIP_HEURTIMING:
460-
pass
461-
462453
ctypedef struct SCIP_SEPA:
463454
pass
464455

@@ -510,9 +501,6 @@ cdef extern from "scip/scip.h":
510501
ctypedef struct BMS_BLKMEM:
511502
pass
512503

513-
ctypedef struct SCIP_EXPR:
514-
pass
515-
516504
ctypedef struct SCIP_EXPRHDLR:
517505
pass
518506

@@ -699,6 +687,7 @@ cdef extern from "scip/scip.h":
699687
SCIP_RETCODE SCIPwriteOrigProblem(SCIP* scip, char* filename, char* extension, SCIP_Bool genericnames)
700688
SCIP_RETCODE SCIPwriteTransProblem(SCIP* scip, char* filename, char* extension, SCIP_Bool genericnames)
701689
SCIP_RETCODE SCIPwriteLP(SCIP* scip, const char*)
690+
SCIP_RETCODE SCIPwriteMIP(SCIP * scip, const char * filename, SCIP_Bool genericnames, SCIP_Bool origobj, SCIP_Bool lazyconss)
702691
SCIP_STATUS SCIPgetStatus(SCIP* scip)
703692
SCIP_Real SCIPepsilon(SCIP* scip)
704693
SCIP_Real SCIPfeastol(SCIP* scip)
@@ -707,7 +696,6 @@ cdef extern from "scip/scip.h":
707696
SCIP_Real SCIPgetLocalTransEstimate(SCIP* scip)
708697

709698
# Solve Methods
710-
SCIP_RETCODE SCIPsolve(SCIP* scip)
711699
SCIP_RETCODE SCIPsolve(SCIP* scip) noexcept nogil
712700
SCIP_RETCODE SCIPsolveConcurrent(SCIP* scip)
713701
SCIP_RETCODE SCIPfreeTransform(SCIP* scip)

src/pyscipopt/scip.pxi

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7979,7 +7979,6 @@ cdef class Model:
79797979
79807980
"""
79817981
raise Warning("model.getDualMultiplier(cons) is deprecated: please use model.getDualsolLinear(cons)")
7982-
return self.getDualsolLinear(cons)
79837982

79847983
def getDualfarkasLinear(self, Constraint cons):
79857984
"""
@@ -9670,6 +9669,29 @@ cdef class Model:
96709669
PY_SCIP_CALL( SCIPwriteLP(self._scip, absfile) )
96719670

96729671
locale.setlocale(locale.LC_NUMERIC,user_locale)
9672+
9673+
def writeMIP(self, filename, genericnames=False, origobj=False, lazyconss=False):
9674+
"""
9675+
Writes MIP relaxation of the current branch-and-bound node to a file
9676+
9677+
Parameters
9678+
----------
9679+
filename : str
9680+
name of the output file
9681+
genericnames : bool, optional
9682+
should generic names like x_i and row_j be used in order to avoid troubles with reserved symbols? (Default value = False)
9683+
origobj : bool, optional
9684+
should the original objective function be used (Default value = False)
9685+
lazyconss : bool, optional
9686+
output removable rows as lazy constraints? (Default value = False)
9687+
"""
9688+
user_locale = locale.getlocale(category=locale.LC_NUMERIC)
9689+
locale.setlocale(locale.LC_NUMERIC, "C")
9690+
9691+
absfile = str_conversion(abspath(filename))
9692+
PY_SCIP_CALL(SCIPwriteMIP(self._scip, absfile, genericnames, origobj, lazyconss))
9693+
9694+
locale.setlocale(locale.LC_NUMERIC,user_locale)
96739695

96749696
def createSol(self, Heur heur = None, initlp=False):
96759697
"""

tests/test_recipe_getLocalConss.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from pyscipopt import Model, SCIP_EVENTTYPE
2+
from pyscipopt.recipes.getLocalConss import *
3+
from helpers.utils import random_mip_1
4+
5+
def localconss(model, event):
6+
local_conss = getLocalConss(model)
7+
assert len(local_conss[1]) == getNLocalConss(model)[1]
8+
assert len(local_conss[0]) == len(model.getConss())
9+
assert local_conss[0] == model.getConss()
10+
11+
vars = model.getVars()
12+
if model.getCurrentNode().getNumber() == 1:
13+
pass
14+
15+
elif model.getCurrentNode().getNumber() == 2:
16+
model.data["local_cons1"] = model.addCons(vars[0] + vars[1] <= 1, name="c1", local=True)
17+
assert getNLocalConss(model)[1] == 1
18+
assert getLocalConss(model)[1][0] == model.data["local_cons1"]
19+
20+
elif model.getCurrentNode().getNumber() == 4:
21+
local_conss = getLocalConss(model)
22+
model.data["local_cons2"] = model.addCons(vars[1] + vars[2] <= 1, name="c2", local=True)
23+
model.data["local_cons3"] = model.addCons(vars[2] + vars[3] <= 1, name="c3", local=True)
24+
assert getNLocalConss(model)[1] == 3
25+
assert getLocalConss(model)[1][0] == model.data["local_cons1"]
26+
assert getLocalConss(model)[1][1] == model.data["local_cons2"]
27+
assert getLocalConss(model)[1][2] == model.data["local_cons3"]
28+
29+
elif model.getCurrentNode().getParent().getNumber() not in [2,4]:
30+
assert getLocalConss(model) == [model.getConss(), []]
31+
32+
def test_getLocalConss():
33+
model = random_mip_1(node_lim=4)
34+
model.data = {}
35+
36+
model.attachEventHandlerCallback(localconss, [SCIP_EVENTTYPE.NODEFOCUSED])
37+
model.optimize()
38+
assert len(model.data) == 3

0 commit comments

Comments
 (0)