Skip to content

Commit

Permalink
Merge branch 'main' into mis
Browse files Browse the repository at this point in the history
  • Loading branch information
DLWoodruff authored Apr 21, 2024
2 parents ac3d587 + bd640f8 commit 2b75576
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test_branches.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:
skip_doctest: 1
TARGET: linux
PYENV: conda
PACKAGES: mpi4py==3.1.5
PACKAGES: openmpi mpi4py

- os: ubuntu-latest
python: '3.10'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_pr_and_main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ jobs:
skip_doctest: 1
TARGET: linux
PYENV: conda
PACKAGES: mpi4py==3.1.5
PACKAGES: openmpi mpi4py

- os: ubuntu-latest
python: '3.11'
Expand Down
12 changes: 9 additions & 3 deletions pyomo/repn/plugins/nl_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
TOL = 1e-8
inf = float('inf')
minus_inf = -inf
allowable_binary_var_bounds = {(0, 0), (0, 1), (1, 1)}

_CONSTANT = ExprType.CONSTANT
_MONOMIAL = ExprType.MONOMIAL
Expand Down Expand Up @@ -882,7 +883,12 @@ def write(self, model):
elif v.is_binary():
binary_vars.add(_id)
elif v.is_integer():
integer_vars.add(_id)
# Note: integer variables whose bounds are in {0, 1}
# should be classified as binary
if var_bounds[_id] in allowable_binary_var_bounds:
binary_vars.add(_id)
else:
integer_vars.add(_id)
else:
raise ValueError(
f"Variable '{v.name}' has a domain that is not Real, "
Expand Down Expand Up @@ -1277,8 +1283,8 @@ def write(self, model):
len(linear_binary_vars),
len(linear_integer_vars),
len(both_vars_nonlinear.intersection(discrete_vars)),
len(con_vars_nonlinear.intersection(discrete_vars)),
len(obj_vars_nonlinear.intersection(discrete_vars)),
len(con_only_nonlinear_vars.intersection(discrete_vars)),
len(obj_only_nonlinear_vars.intersection(discrete_vars)),
)
)
#
Expand Down
141 changes: 140 additions & 1 deletion pyomo/repn/tests/ampl/test_nlv2.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
Suffix,
Constraint,
Expression,
Binary,
Integers,
)
import pyomo.environ as pyo

Expand Down Expand Up @@ -1266,7 +1268,7 @@ def test_nonfloat_constants(self):
0 0 #network constraints: nonlinear, linear
0 0 0 #nonlinear vars in constraints, objectives, both
0 0 0 1 #linear network variables; functions; arith, flags
0 4 0 0 0 #discrete variables: binary, integer, nonlinear (b,c,o)
4 0 0 0 0 #discrete variables: binary, integer, nonlinear (b,c,o)
4 4 #nonzeros in Jacobian, obj. gradient
6 4 #max name lengths: constraints, variables
0 0 0 0 0 #common exprs: b,c,o,c1,o1
Expand Down Expand Up @@ -2165,6 +2167,143 @@ def test_named_expressions(self):
0 0
1 0
2 0
""",
OUT.getvalue(),
)
)

def test_discrete_var_tabulation(self):
# This tests an error reported in #3235
#
# Among other issues, this verifies that nonlinear discrete
# variables are tabulated correctly (header line 7), and that
# integer variables with bounds in {0, 1} are mapped to binary
# variables.
m = ConcreteModel()
m.p1 = Var(bounds=(0.85, 1.15))
m.p2 = Var(bounds=(0.68, 0.92))
m.c1 = Var(bounds=(-0.0, 0.7))
m.c2 = Var(bounds=(-0.0, 0.7))
m.t1 = Var(within=Binary, bounds=(0, 1))
m.t2 = Var(within=Binary, bounds=(0, 1))
m.t3 = Var(within=Binary, bounds=(0, 1))
m.t4 = Var(within=Binary, bounds=(0, 1))
m.t5 = Var(within=Integers, bounds=(0, None))
m.t6 = Var(within=Integers, bounds=(0, None))
m.x1 = Var(within=Binary)
m.x2 = Var(within=Integers, bounds=(0, 1))
m.x3 = Var(within=Integers, bounds=(0, None))
m.const = Constraint(
expr=(
(0.7 - (m.c1 * m.t1 + m.c2 * m.t2))
<= (m.p1 * m.t1 + m.p2 * m.t2 + m.p1 * m.t4 + m.t6 * m.t5)
)
)
m.OBJ = Objective(
expr=(m.p1 * m.t1 + m.p2 * m.t2 + m.p2 * m.t3 + m.x1 + m.x2 + m.x3)
)

OUT = io.StringIO()
nl_writer.NLWriter().write(m, OUT, symbolic_solver_labels=True)

self.assertEqual(
*nl_diff(
"""g3 1 1 0 # problem unknown
13 1 1 0 0 #vars, constraints, objectives, ranges, eqns
1 1 0 0 0 0 #nonlinear constrs, objs; ccons: lin, nonlin, nd, nzlb
0 0 #network constraints: nonlinear, linear
9 10 4 #nonlinear vars in constraints, objectives, both
0 0 0 1 #linear network variables; functions; arith, flags
2 1 2 3 1 #discrete variables: binary, integer, nonlinear (b,c,o)
9 8 #nonzeros in Jacobian, obj. gradient
5 2 #max name lengths: constraints, variables
0 0 0 0 0 #common exprs: b,c,o,c1,o1
C0 #const
o0 #+
o16 #-
o0 #+
o2 #*
v4 #c1
v2 #t1
o2 #*
v5 #c2
v3 #t2
o16 #-
o54 #sumlist
4 #(n)
o2 #*
v0 #p1
v2 #t1
o2 #*
v1 #p2
v3 #t2
o2 #*
v0 #p1
v6 #t4
o2 #*
v7 #t6
v8 #t5
O0 0 #OBJ
o54 #sumlist
3 #(n)
o2 #*
v0 #p1
v2 #t1
o2 #*
v1 #p2
v3 #t2
o2 #*
v1 #p2
v9 #t3
x0 #initial guess
r #1 ranges (rhs's)
1 -0.7 #const
b #13 bounds (on variables)
0 0.85 1.15 #p1
0 0.68 0.92 #p2
0 0 1 #t1
0 0 1 #t2
0 -0.0 0.7 #c1
0 -0.0 0.7 #c2
0 0 1 #t4
2 0 #t6
2 0 #t5
0 0 1 #t3
0 0 1 #x1
0 0 1 #x2
2 0 #x3
k12 #intermediate Jacobian column lengths
1
2
3
4
5
6
7
8
9
9
9
9
J0 9 #const
0 0
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
G0 8 #OBJ
0 0
1 0
2 0
3 0
9 0
10 1
11 1
12 1
""",
OUT.getvalue(),
)
Expand Down
2 changes: 1 addition & 1 deletion pyomo/solvers/plugins/solvers/SCIPAMPL.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ def read_scip_log(filename: str):
solver_status = scip_lines[0][colon_position + 2 : scip_lines[0].index('\n')]

solving_time = float(
scip_lines[1][colon_position + 2 : scip_lines[1].index('\n')]
scip_lines[1][colon_position + 2 : scip_lines[1].index('\n')].split(' ')[0]
)

try:
Expand Down
6 changes: 6 additions & 0 deletions pyomo/solvers/tests/mip/test_scip.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ def test_scip_solve_from_instance_options(self):
results.write(filename=_out, times=False, format='json')
self.compare_json(_out, join(currdir, "test_scip_solve_from_instance.baseline"))

def test_scip_solve_from_instance_with_reoptimization(self):
# Test scip with re-optimization option enabled
# This case changes the Scip output results which may break the results parser
self.scip.options['reoptimization/enable'] = True
self.test_scip_solve_from_instance()


if __name__ == "__main__":
deleteFiles = False
Expand Down

0 comments on commit 2b75576

Please sign in to comment.