Skip to content

Commit

Permalink
Merge branch 'master' into add-eventtype-Nodedelete
Browse files Browse the repository at this point in the history
  • Loading branch information
Joao-Dionisio authored Nov 28, 2023
2 parents 8e7a4b9 + e9a242e commit 1a3a0fe
Show file tree
Hide file tree
Showing 11 changed files with 305 additions and 56 deletions.
29 changes: 29 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**System**
- OS: [e.g. iOS]
- Version [e.g. 22]
- SCIP version
- How did you install `pyscipopt`?

**Additional context**
Add any other context about the problem here.
17 changes: 17 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Additional context**
Add any other context or screenshots about the feature request here.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Added SCIP functions hasPrimalRay, getPrimalRay, getPrimalRayVal
### Fixed
- Fixed mistake with outdated values for several enums
- Correctly set result, lowerbound in PyRelaxExec
- Fixed typo in documentation of chgRhs
- Pricer plugin fundamental callbacks now raise an error if not implemented
- Brachrule plugin fundamental callbacks now raise an error if not implemented
Expand Down
10 changes: 6 additions & 4 deletions src/pyscipopt/relax.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ cdef class Relax:
'''callls execution method of relaxation handler'''
print("python error in relaxexec: this method needs to be implemented")
return{}


cdef SCIP_RETCODE PyRelaxCopy (SCIP* scip, SCIP_RELAX* relax) with gil:
return SCIP_OKAY
Expand Down Expand Up @@ -73,6 +73,8 @@ cdef SCIP_RETCODE PyRelaxExec (SCIP* scip, SCIP_RELAX* relax, SCIP_Real* lowerbo
cdef SCIP_RELAXDATA* relaxdata
relaxdata = SCIPrelaxGetData(relax)
PyRelax = <Relax>relaxdata
PyRelax.relaxexec()
return SCIP_OKAY

result_dict = PyRelax.relaxexec()
assert isinstance(result_dict, dict), "relaxexec() must return a dictionary."
lowerbound[0] = result_dict.get("lowerbound", <SCIP_Real>lowerbound[0])
result[0] = result_dict.get("result", <SCIP_RESULT>result[0])
return SCIP_OKAY
6 changes: 3 additions & 3 deletions src/pyscipopt/scip.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -2169,7 +2169,7 @@ cdef class Model:
SCIPgetConsNVars(self._scip, constraint.scip_cons, &_nvars, &success)

cdef SCIP_VAR** _vars = <SCIP_VAR**> malloc(_nvars * sizeof(SCIP_VAR*))
SCIPgetConsVars(self._scip, constraint.scip_cons, _vars, _nvars*sizeof(SCIP_VAR), &success)
SCIPgetConsVars(self._scip, constraint.scip_cons, _vars, _nvars*sizeof(SCIP_VAR*), &success)

vars = []
for i in range(_nvars):
Expand Down Expand Up @@ -2722,7 +2722,6 @@ cdef class Model:

return pyCons


def addConsIndicator(self, cons, binvar=None, activeone=True, name="IndicatorCons",
initial=True, separate=True, enforce=True, check=True,
propagate=True, local=False, dynamic=False,
Expand Down Expand Up @@ -3323,7 +3322,6 @@ cdef class Model:

return _dualsol


def optimize(self):
"""Optimize the problem."""
PY_SCIP_CALL(SCIPsolve(self._scip))
Expand Down Expand Up @@ -4459,6 +4457,8 @@ cdef class Model:
"""
cdef SCIP_SOL* _sol
_sol = <SCIP_SOL*>solution.sol

assert _sol != NULL, "Cannot set value to a freed solution."
PY_SCIP_CALL(SCIPsetSolVal(self._scip, _sol, var.scip_var, val))

def trySol(self, Solution solution, printreason=True, completely=False, checkbounds=True, checkintegrality=True, checklprows=True, free=True):
Expand Down
134 changes: 126 additions & 8 deletions tests/test_cons.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
from pyscipopt import Model, quicksum
import random
import pytest


def test_getConsNVars():
n_vars = random.randint(100,1000)
n_vars = random.randint(100, 1000)
m = Model()
x = {}
for i in range(n_vars):
x[i] = m.addVar("%i"%i)
x[i] = m.addVar("%i" % i)

c = m.addCons(quicksum(x[i] for i in x) <= 10)
assert m.getConsNVars(c) == n_vars

m.optimize()
assert m.getConsNVars(c) == n_vars


def test_getConsVars():
n_vars = random.randint(100,1000)
n_vars = random.randint(100, 1000)
m = Model()
x = {}
for i in range(n_vars):
x[i] = m.addVar("%i"%i)
x[i] = m.addVar("%i" % i)

c = m.addCons(quicksum(x[i] for i in x) <= 1)
assert m.getConsVars(c) == [x[i] for i in x]



def test_constraint_option_setting():
m = Model()
x = m.addVar()
Expand All @@ -39,4 +42,119 @@ def test_constraint_option_setting():
assert c.isChecked() == option
assert c.isEnforced() == option
assert c.isRemovable() == option
assert c.isInitial() == option
assert c.isInitial() == option


def test_cons_logical():
m = Model()
x1 = m.addVar(vtype="B")
x2 = m.addVar(vtype="B")
x3 = m.addVar(vtype="B")
x4 = m.addVar(vtype="B")
result1 = m.addVar(vtype="B")
result2 = m.addVar(vtype="B")

m.addCons(x3 == 1 - x1)
m.addCons(x4 == 1 - x2)

# result1 true
m.addConsAnd([x1, x2], result1)
m.addConsOr([x1, x2], result1)
m.addConsXor([x1, x3], True)

# result2 false
m.addConsOr([x3, x4], result2)
m.addConsAnd([x1, x3], result2)
m.addConsXor([x1, x2], False)

m.optimize()

assert m.isEQ(m.getVal(result1), 1)
assert m.isEQ(m.getVal(result2), 0)


def test_SOScons():
m = Model()
x = {}
for i in range(6):
x[i] = m.addVar(vtype="B", obj=-i)

c1 = m.addConsSOS1([x[0]], [1])
c2 = m.addConsSOS2([x[1]], [1])

m.addVarSOS1(c1, x[2], 1)
m.addVarSOS2(c2, x[3], 1)

m.appendVarSOS1(c1, x[4])
m.appendVarSOS2(c2, x[5])

m.optimize()

assert m.isEQ(m.getVal(x[0]), 0)
assert m.isEQ(m.getVal(x[1]), 0)
assert m.isEQ(m.getVal(x[2]), 0)
assert m.isEQ(m.getVal(x[3]), 1)
assert m.isEQ(m.getVal(x[4]), 1)
assert m.isEQ(m.getVal(x[5]), 1)


def test_cons_indicator():
m = Model()
x = m.addVar(lb=0)
binvar = m.addVar(vtype="B", lb=1)

c = m.addConsIndicator(x >= 1, binvar)

slack = m.getSlackVarIndicator(c)

m.optimize()

assert m.isEQ(m.getVal(slack), 0)
assert m.isEQ(m.getVal(binvar), 1)
assert m.isEQ(m.getVal(x), 1)


@pytest.mark.xfail(reason="addConsIndicator doesn't behave as expected when binary variable is False. See Issue #717.")
def test_cons_indicator_fail():
m = Model()
binvar = m.addVar(vtype="B")
x = m.addVar(vtype="C", lb=1, ub=3)
m.addConsIndicator(x <= 2, binvar)

m.setObjective(x, "maximize")

sol = m.createSol(None)
m.setSolVal(sol, x, 3)
m.setSolVal(sol, binvar, 0)
assert m.checkSol(sol) # solution should be feasible


def test_addConsCardinality():
m = Model()
x = {}
for i in range(5):
x[i] = m.addVar(ub=1, obj=-1)

m.addConsCardinality([x[i] for i in range(5)], 3)
m.optimize()

assert m.isEQ(m.getVal(quicksum(x[i] for i in range(5))), 3)


def test_printCons():
m = Model()
x = m.addVar()
y = m.addVar()
c = m.addCons(x * y <= 5)

m.printCons(c)


@pytest.mark.skip(reason="TODO: test getValsLinear()")
def test_getValsLinear():
assert True


@pytest.mark.skip(reason="TODO: test getRowLinear()")
def test_getRowLinear():
assert True
2 changes: 1 addition & 1 deletion tests/test_logical.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def getVarByName(m, name):
return [v for v in m.getVars() if name == v.name][0]
except IndexError:
return None


def getAllVarsByName(m, name):
try:
Expand Down
19 changes: 18 additions & 1 deletion tests/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def test_model():
assert s.getRhs(c) == 6.0

# solve problem
s.presolve() # to test presolve method
s.optimize()

solution = s.getBestSol()
Expand Down Expand Up @@ -118,7 +119,6 @@ def assert_conss_eq(a, b):
s.freeProb()
m.freeProb()


def test_multiple_cons_names():
m = Model()
x = m.addVar("x", vtype = 'C', obj = 1.0)
Expand Down Expand Up @@ -251,3 +251,20 @@ def test_getVarsDict():
for v in x.values():
assert v.name in var_dict
assert model.getVal(v) == var_dict[v.name]

def test_objLim():
m = Model()

x = m.addVar(obj=1, lb=2)
m.setObjlimit(1)

m.optimize()
assert m.getNLimSolsFound() == 0

m = Model()
x = m.addVar(obj=1, lb=2)

m.setObjlimit(2)
m.optimize()
assert m.getNLimSolsFound() == 1

8 changes: 8 additions & 0 deletions tests/test_pricer.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ def pricerredcost(self):

objval = 1 + subMIP.getObjVal()

assert type(self.model.getNSolsFound()) == int
assert type(self.model.getNBestSolsFound()) == int
assert self.model.getNBestSolsFound() <= self.model.getNSolsFound()
self.model.data["nSols"] = self.model.getNSolsFound()

# Adding the column to the master problem
if objval < -1e-08:
Expand Down Expand Up @@ -74,6 +78,8 @@ def test_cuttingstock():
s = Model("CuttingStock")

s.setPresolve(0)
s.data = {}
s.data["nSols"] = 0

# creating a pricer
pricer = CutPricer()
Expand Down Expand Up @@ -149,6 +155,8 @@ def test_cuttingstock():
print('\t\t\tTotal Output:\t', '\t'.join(str(e) for e in widthOutput))

assert s.getObjVal() == 452.25
assert type(s.getNSols()) == int
assert s.getNSols() == s.data["nSols"]

def test_incomplete_pricer():
class IncompletePricer(Pricer):
Expand Down
Loading

0 comments on commit 1a3a0fe

Please sign in to comment.