Skip to content

Work with the changes in the output format of MibS and add test examples for MibS solver #101

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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
2 changes: 2 additions & 0 deletions pao/mpr/examples/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from . import besancon27_shifted
from . import getachew_ex1
from . import getachew_ex2
from . import mibs
from . import moore
from . import pineda
from . import toyexample1
from . import toyexample2
Expand Down
24 changes: 17 additions & 7 deletions pao/mpr/solvers/mibs.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,23 +124,33 @@ def _initialize_results(self, ans, M):
#print(state, line)
line = line.strip()
if state == 0:
if line.startswith("ERROR"):
solv.termination_condition = pao.common.TerminationCondition.error
raise RuntimeError(line)
if line.startswith("Optimal solution:"):
solv.termination_condition = pao.common.TerminationCondition.optimal
state = 1
elif state == 1:
solv.best_feasible_objective = float(line.split("=")[1])
state = 2
elif state == 2:
state = 2.1
first_index = 0
elif state == 2.1:
if line.startswith("Second"):
state = 2.2
second_index = 0
continue
name, _, val = line.split(" ")
M.U.x.values[first_index] = float(val)
first_index += 1
elif state == 2.2:
if line.startswith("Number"):
state = 3
continue
name, _, val = line.split(" ")
vname, index = name[:-1].split("[")
#print(vname, index, val)
if vname == "x":
M.U.x.values[int(index)] = float(val)
else:
M.U.LL.x.values[int(index)] = float(val)
M.U.LL.x.values[second_index] = float(val)
second_index += 1
return results

def _debug(self, M): # pragma: no cover
Expand Down Expand Up @@ -223,7 +233,7 @@ def create_mibs_model(self, repn, mps_filename, aux_filename):
OUTPUT.write("OS 1\n")
else:
OUTPUT.write("OS -1\n")

return M

pao.common.SolverAPI._generate_solve_docstring(LinearMultilevelSolver_MIBS)
161 changes: 158 additions & 3 deletions pao/mpr/tests/test_solve.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import pyomo.opt


solvers = pyomo.opt.check_available_solvers('glpk','cbc','ipopt')
solvers = pyomo.opt.check_available_solvers('glpk','cbc','ipopt','mibs')


class Test_bilevel_FA(unittest.TestCase):
Expand Down Expand Up @@ -43,7 +43,7 @@ def test_barguel(self):
except AssertionError:
pass

lmp, soln = linearize_bilinear_terms(qmp, 1e6)
lmp, soln = linearize_bilinear_terms(qmp, 1e6)
lmp.check()
opt.solve(lmp)
soln.copy(From=lmp, To=qmp)
Expand Down Expand Up @@ -225,7 +225,7 @@ def test_barguel(self):
except AssertionError:
pass

lmp, soln = linearize_bilinear_terms(qmp, 1e6)
lmp, soln = linearize_bilinear_terms(qmp, 1e6)
lmp.check()
opt.solve(lmp)
soln.copy(From=lmp, To=qmp)
Expand Down Expand Up @@ -317,6 +317,161 @@ def test_toyexample3(self):
self.assertEqual(mpr.U.LL.x.values[1], 0)


@unittest.skipIf('mibs' not in solvers, "MibS solver is not available")
class Test_bilevel_MIBS(unittest.TestCase):

def test_bard511(self):
mpr = examples.bard511.create()
mpr.check()

opt = Solver('pao.mpr.MIBS')
with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(mpr)

# self.assertTrue(math.isclose(mpr.U.x.values[0], 4, abs_tol=1e-4))
# self.assertTrue(math.isclose(mpr.U.LL.x.values[0], 4, abs_tol=1e-4))

def test_bard511_list(self):
mpr = examples.bard511_list.create()
mpr.check()

opt = Solver('pao.mpr.MIBS')
with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(mpr)

# self.assertTrue(math.isclose(mpr.U.x.values[0], 4, abs_tol=1e-4))
# self.assertTrue(math.isclose(mpr.U.LL.x.values[0], 4, abs_tol=1e-4))

def test_barguel(self):
qmp = examples.barguel.create()
qmp.check()

opt = Solver('pao.mpr.MIBS')
try:
opt.solve(qmp)
self.fail("Expected an assertion error")
except AssertionError:
pass

lmp, soln = linearize_bilinear_terms(qmp, 1e6)
lmp.check()
opt.solve(lmp)
soln.copy(From=lmp, To=qmp)

self.assertTrue(math.isclose(qmp.U.x.values[0], 0))
self.assertTrue(math.isclose(qmp.U.LL.x.values[0], 0))
self.assertTrue(math.isclose(qmp.U.LL.x.values[1], 0))

def test_besancon27(self):
mpr = examples.besancon27.create()
mpr.check()

opt = Solver('pao.mpr.MIBS')
with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(mpr)

# self.assertTrue(math.isclose(mpr.U.x.values[0], 0, abs_tol=1e-4))
# self.assertTrue(math.isclose(mpr.U.LL.x.values[0], 1, abs_tol=1e-4))

def test_besancon27_shifted(self):
mpr = examples.besancon27_shifted.create()
mpr.check()

opt = Solver('pao.mpr.MIBS')
with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(mpr)

# self.assertTrue(math.isclose(mpr.U.x.values[0], 2.5, abs_tol=1e-4))
# self.assertTrue(math.isclose(mpr.U.LL.x.values[0], 1.25, abs_tol=1e-4))

def test_getachew_ex1(self):
mpr = examples.getachew_ex1.create()
mpr.check()

opt = Solver('pao.mpr.MIBS')
with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(mpr)

# self.assertTrue(math.isclose(mpr.U.x.values[0], 8, abs_tol=1e-4))
# self.assertTrue(math.isclose(mpr.U.LL.x.values[0], 6, abs_tol=1e-4))

def test_getachew_ex2(self):
mpr = examples.getachew_ex2.create()
mpr.check()

opt = Solver('pao.mpr.MIBS')
with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(mpr)

# self.assertTrue(math.isclose(mpr.U.x.values[0], 6, abs_tol=1e-4))
# self.assertTrue(math.isclose(mpr.U.LL.x.values[0], 8, abs_tol=1e-4))

def test_mibs(self):
mpr = examples.mibs.create()
mpr.check()

opt = Solver('pao.mpr.MIBS')
opt.solve(mpr)

self.assertTrue(math.isclose(mpr.U.x.values[0], 6, abs_tol=1e-4))
self.assertTrue(math.isclose(mpr.U.LL.x.values[0], 5, abs_tol=1e-4))

def test_moore(self):
mpr = examples.moore.create()
mpr.check()

opt = Solver('pao.mpr.MIBS')
opt.solve(mpr)

self.assertTrue(math.isclose(mpr.U.x.values[0], 2, abs_tol=1e-4))
self.assertTrue(math.isclose(mpr.U.LL.x.values[0], 2, abs_tol=1e-4))

def test_pineda(self):
mpr = examples.pineda.create()
mpr.check()

opt = Solver('pao.mpr.MIBS')
with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(mpr)

# self.assertTrue(math.isclose(mpr.U.x.values[0], 2, abs_tol=1e-4))
# self.assertTrue(math.isclose(mpr.U.LL.x.values[0], 100, abs_tol=1e-4))

def test_toyexample1(self):
mpr = examples.toyexample1.create()
mpr.check()

opt = Solver('pao.mpr.MIBS')
opt.solve(mpr)

self.assertEqual(mpr.U.x.values[0], 2)
self.assertEqual(mpr.U.LL.x.values[0], 2)

def test_toyexample2(self):
mpr = examples.toyexample2.create()
mpr.check()

opt = Solver('pao.mpr.MIBS')
opt.solve(mpr)

self.assertEqual(mpr.U.x.values[0], 8)
self.assertEqual(mpr.U.LL.x.values[0], 6)

def test_toyexample3(self):
mpr = examples.toyexample3.create()
mpr.check()

opt = Solver('pao.mpr.MIBS')

with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(mpr)

# self.assertTrue(math.isclose(mpr.U.x.values[0], 3, abs_tol=1e-4))
# self.assertTrue(math.isclose(mpr.U.LL.x.values[0], 0.5, abs_tol=1e-4))
# self.assertEqual(mpr.U.x.values[1], 8)
# self.assertEqual(mpr.U.LL.x.values[1], 0)


#class Test_bilevel_ld(unittest.TestCase):
class XTest_bilevel_ld(object):

Expand Down
2 changes: 2 additions & 0 deletions pao/pyomo/examples/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from . import besancon27
from . import getachew_ex1
from . import getachew_ex2
from . import mibs
from . import moore
from . import pineda
from . import sip_example1
from . import toyexample1
Expand Down
101 changes: 100 additions & 1 deletion pao/pyomo/tests/test_mpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import pyomo.opt


solvers = pyomo.opt.check_available_solvers('glpk','cbc','ipopt','gurobi')
solvers = pyomo.opt.check_available_solvers('glpk','cbc','ipopt','gurobi', 'mibs')


class Test_solver(unittest.TestCase):
Expand Down Expand Up @@ -259,5 +259,104 @@ def test_toyexample3(self):
self.assertEqual(M.L.xZ.value, 0)


@unittest.skipIf('mibs' not in solvers, "MibS solver is not available")
class Test_bilevel_MIBS(unittest.TestCase):

def test_bard511(self):
M = examples.bard511.create()

opt = Solver('pao.pyomo.MIBS')

with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(M)

def test_barguel(self):
M = examples.barguel.create()

opt = Solver('pao.pyomo.MIBS')
try:
opt.solve(M)
self.fail("Expected an assertion error")
except AssertionError:
pass

opt.solve(M, linearize_bigm=1e6)

self.assertTrue(math.isclose(M.u.value, 0))
self.assertTrue(math.isclose(M.x.value, 0))
self.assertTrue(math.isclose(M.y.value, 0))

def test_besancon27(self):
M = examples.besancon27.create()

opt = Solver('pao.pyomo.MIBS')
with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(M)

def test_getachew_ex1(self):
M = examples.getachew_ex1.create()

opt = Solver('pao.pyomo.MIBS')
with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(M)

def test_getachew_ex2(self):
M = examples.getachew_ex2.create()

opt = Solver('pao.pyomo.MIBS')
with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(M)

def test_mibs(self):
M = examples.mibs.create()

opt = Solver('pao.pyomo.MIBS')
opt.solve(M)

self.assertTrue(math.isclose(M.x.value, 6, abs_tol=1e-4))
self.assertTrue(math.isclose(M.y.value, 5, abs_tol=1e-4))

def test_moore(self):
M = examples.moore.create()

opt = Solver('pao.pyomo.MIBS')
opt.solve(M)

self.assertEqual(M.xZ.value, 2)
self.assertEqual(M.L.xZ.value, 2)

def test_pineda(self):
M = examples.pineda.create()

opt = Solver('pao.pyomo.MIBS')
with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(M)

def test_toyexample1(self):
M = examples.toyexample1.create()

opt = Solver('pao.pyomo.MIBS')
opt.solve(M)

self.assertEqual(M.xZ.value, 2)
self.assertEqual(M.L.xZ.value, 2)

def test_toyexample2(self):
M = examples.toyexample2.create()

opt = Solver('pao.pyomo.MIBS')
opt.solve(M)

self.assertEqual(M.xZ.value, 8)
self.assertEqual(M.L.xZ.value, 6)

def test_toyexample3(self):
M = examples.toyexample3.create()

opt = Solver('pao.pyomo.MIBS')
with self.assertRaisesRegex(RuntimeError, "ERROR:All linking variables should be integer"):
opt.solve(M)


if __name__ == "__main__":
unittest.main()