Skip to content
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

Expand testing #751

Merged
merged 18 commits into from
Nov 17, 2023
Merged
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 @@ -7,6 +7,7 @@
- Add SCIP function SCIPgetSolTime and wrapper getSolTime
- Add convenience methods relax and getVarDict
- Add SCIP functions hasPrimalRay, getPrimalRay, getPrimalRayVal
- Add multiple tests for constraints and for solutions
### Fixed
- Fixed typo in documentation of chgRhs
- Pricer plugin fundamental callbacks now raise an error if not implemented
Expand Down
2 changes: 0 additions & 2 deletions src/pyscipopt/scip.pxi
Original file line number Diff line number Diff line change
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
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