Skip to content

Commit

Permalink
Merge pull request #3292 from mrmundt/numpy-update
Browse files Browse the repository at this point in the history
Update Pyomo for NumPy 2
  • Loading branch information
blnicho authored Jun 25, 2024
2 parents 7bbe3a7 + 3ea998a commit ae354ae
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 52 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/test_branches.yml
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,9 @@ jobs:
token: ${{ secrets.PYOMO_CODECOV_TOKEN }}
name: ${{ matrix.TARGET }}
flags: ${{ matrix.TARGET }}
# downgrading after v0.7.0 broke tokenless upload
# see codecov/codecov-action#1487
version: v0.6.0
fail_ci_if_error: true

- name: Upload other coverage reports
Expand All @@ -867,4 +870,7 @@ jobs:
token: ${{ secrets.PYOMO_CODECOV_TOKEN }}
name: ${{ matrix.TARGET }}/other
flags: ${{ matrix.TARGET }},other
# downgrading after v0.7.0 broke tokenless upload
# see codecov/codecov-action#1487
version: v0.6.0
fail_ci_if_error: true
6 changes: 6 additions & 0 deletions .github/workflows/test_pr_and_main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,9 @@ jobs:
token: ${{ secrets.PYOMO_CODECOV_TOKEN }}
name: ${{ matrix.TARGET }}
flags: ${{ matrix.TARGET }}
# downgrading after v0.7.0 broke tokenless upload
# see codecov/codecov-action#1487
version: v0.6.0
fail_ci_if_error: true

- name: Upload other coverage reports
Expand All @@ -911,4 +914,7 @@ jobs:
token: ${{ secrets.PYOMO_CODECOV_TOKEN }}
name: ${{ matrix.TARGET }}/other
flags: ${{ matrix.TARGET }},other
# downgrading after v0.7.0 broke tokenless upload
# see codecov/codecov-action#1487
version: v0.6.0
fail_ci_if_error: true
10 changes: 8 additions & 2 deletions pyomo/common/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -999,10 +999,13 @@ def _finalize_numpy(np, available):
# registration here (to bypass the deprecation warning) until we
# finally remove all support for it
numeric_types._native_boolean_types.add(t)
_floats = [np.float_, np.float16, np.float32, np.float64]
_floats = [np.float16, np.float32, np.float64]
# float96 and float128 may or may not be defined in this particular
# numpy build (it depends on platform and version).
# Register them only if they are present
if hasattr(np, 'float_'):
# Prepend to preserve previous functionality
_floats.insert(0, np.float_)
if hasattr(np, 'float96'):
_floats.append(np.float96)
if hasattr(np, 'float128'):
Expand All @@ -1013,10 +1016,13 @@ def _finalize_numpy(np, available):
# registration here (to bypass the deprecation warning) until we
# finally remove all support for it
numeric_types._native_boolean_types.add(t)
_complex = [np.complex_, np.complex64, np.complex128]
_complex = [np.complex64, np.complex128]
# complex192 and complex256 may or may not be defined in this
# particular numpy build (it depends on platform and version).
# Register them only if they are present
if hasattr(np, 'np.complex_'):
# Prepend to preserve functionality
_complex.insert(0, np.complex_)
if hasattr(np, 'complex192'):
_complex.append(np.complex192)
if hasattr(np, 'complex256'):
Expand Down
35 changes: 28 additions & 7 deletions pyomo/common/unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@ def filter_fcn(self, line):
return False

def filter_file_contents(self, lines, abstol=None):
_numpy_scalar_re = re.compile(r'np.(int|float)\d+\(([^\)]+)\)')
filtered = []
deprecated = None
for line in lines:
Expand All @@ -807,29 +808,49 @@ def filter_file_contents(self, lines, abstol=None):
item_list = []
items = line.strip().split()
for i in items:
# Split up lists, dicts, and sets
while i and i[0] in '[{':
item_list.append(i[0])
i = i[1:]
tail = []
while i and i[-1] in ',:]}':
tail.append(i[-1])
i = i[:-1]

# A few substitutions to get tests passing on pypy3
if ".inf" in i:
i = i.replace(".inf", "inf")
if "null" in i:
i = i.replace("null", "None")

try:
item_list.append(float(i))
# Numpy 2.x changed the repr for scalars. Convert
# the new scalar reprs back to the original (which
# were indistinguishable from python floats/ints)
np_match = _numpy_scalar_re.match(i)
if np_match:
item_list.append(float(np_match.group(2)))
else:
item_list.append(float(i))
except:
item_list.append(i)
if tail:
tail.reverse()
item_list.extend(tail)

# We can get printed results objects where the baseline is
# exactly 0 (and omitted) and the test is slightly non-zero.
# We will look for the pattern of values printed from
# results objects and remote them if they are within
# tolerance of 0
if (
len(item_list) == 2
and item_list[0] == 'Value:'
and type(item_list[1]) is float
and abs(item_list[1]) < (abstol or 0)
and len(filtered[-1]) == 1
and filtered[-1][0][-1] == ':'
len(item_list) == 3
and item_list[0] == 'Value'
and item_list[1] == ':'
and type(item_list[2]) is float
and abs(item_list[2]) < (abstol or 0)
and len(filtered[-1]) == 2
and filtered[-1][1] == ':'
):
filtered.pop()
else:
Expand Down
10 changes: 6 additions & 4 deletions pyomo/core/kernel/register_numpy_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@
# Historically, the lists included several numpy aliases
numpy_int_names.extend(('int_', 'intc', 'intp'))
numpy_int.extend((numpy.int_, numpy.intc, numpy.intp))
numpy_float_names.append('float_')
numpy_float.append(numpy.float_)
numpy_complex_names.append('complex_')
numpy_complex.append(numpy.complex_)
if hasattr(numpy, 'float_'):
numpy_float_names.append('float_')
numpy_float.append(numpy.float_)
if hasattr(numpy, 'complex_'):
numpy_complex_names.append('complex_')
numpy_complex.append(numpy.complex_)

# Re-build the old numpy_* lists
for t in native_boolean_types:
Expand Down
11 changes: 8 additions & 3 deletions pyomo/core/tests/unit/test_kernel_register_numpy_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
# Boolean
numpy_bool_names = []
if numpy_available:
numpy_bool_names.append('bool_')
if numpy.__version__[0] == '2':
numpy_bool_names.append('bool')
else:
numpy_bool_names.append('bool_')
# Integers
numpy_int_names = []
if numpy_available:
Expand All @@ -34,7 +37,8 @@
# Reals
numpy_float_names = []
if numpy_available:
numpy_float_names.append('float_')
if hasattr(numpy, 'float_'):
numpy_float_names.append('float_')
numpy_float_names.append('float16')
numpy_float_names.append('float32')
numpy_float_names.append('float64')
Expand All @@ -46,7 +50,8 @@
# Complex
numpy_complex_names = []
if numpy_available:
numpy_complex_names.append('complex_')
if hasattr(numpy, 'complex_'):
numpy_complex_names.append('complex_')
numpy_complex_names.append('complex64')
numpy_complex_names.append('complex128')
if hasattr(numpy, 'complex192'):
Expand Down
8 changes: 4 additions & 4 deletions pyomo/core/tests/unit/test_numvalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -552,10 +552,10 @@ def test_unknownNumericType(self):

@unittest.skipUnless(numpy_available, "This test requires NumPy")
def test_numpy_basic_float_registration(self):
self.assertIn(numpy.float_, native_numeric_types)
self.assertNotIn(numpy.float_, native_integer_types)
self.assertIn(numpy.float_, _native_boolean_types)
self.assertIn(numpy.float_, native_types)
self.assertIn(numpy.float64, native_numeric_types)
self.assertNotIn(numpy.float64, native_integer_types)
self.assertIn(numpy.float64, _native_boolean_types)
self.assertIn(numpy.float64, native_types)

@unittest.skipUnless(numpy_available, "This test requires NumPy")
def test_numpy_basic_int_registration(self):
Expand Down
12 changes: 6 additions & 6 deletions pyomo/core/tests/unit/test_sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1051,7 +1051,7 @@ def setUp(self):
self.instance = self.model.create_instance(currdir + "setA.dat")
self.e1 = numpy.bool_(1)
self.e2 = numpy.int_(2)
self.e3 = numpy.float_(3.0)
self.e3 = numpy.float64(3.0)
self.e4 = numpy.int_(4)
self.e5 = numpy.int_(5)
self.e6 = numpy.int_(6)
Expand All @@ -1068,7 +1068,7 @@ def test_numpy_int(self):

def test_numpy_float(self):
model = ConcreteModel()
model.A = Set(initialize=[numpy.float_(1.0), numpy.float_(0.0)])
model.A = Set(initialize=[numpy.float64(1.0), numpy.float64(0.0)])
self.assertEqual(model.A.bounds(), (0, 1))


Expand Down Expand Up @@ -3213,7 +3213,7 @@ def test_numpy_membership(self):
self.assertEqual(numpy.int_(1) in Boolean, True)
self.assertEqual(numpy.bool_(True) in Boolean, True)
self.assertEqual(numpy.bool_(False) in Boolean, True)
self.assertEqual(numpy.float_(1.1) in Boolean, False)
self.assertEqual(numpy.float64(1.1) in Boolean, False)
self.assertEqual(numpy.int_(2) in Boolean, False)

self.assertEqual(numpy.int_(0) in Integers, True)
Expand All @@ -3222,7 +3222,7 @@ def test_numpy_membership(self):
# identically to 1
self.assertEqual(numpy.bool_(True) in Integers, True)
self.assertEqual(numpy.bool_(False) in Integers, True)
self.assertEqual(numpy.float_(1.1) in Integers, False)
self.assertEqual(numpy.float64(1.1) in Integers, False)
self.assertEqual(numpy.int_(2) in Integers, True)

self.assertEqual(numpy.int_(0) in Reals, True)
Expand All @@ -3231,14 +3231,14 @@ def test_numpy_membership(self):
# identically to 1
self.assertEqual(numpy.bool_(True) in Reals, True)
self.assertEqual(numpy.bool_(False) in Reals, True)
self.assertEqual(numpy.float_(1.1) in Reals, True)
self.assertEqual(numpy.float64(1.1) in Reals, True)
self.assertEqual(numpy.int_(2) in Reals, True)

self.assertEqual(numpy.int_(0) in Any, True)
self.assertEqual(numpy.int_(1) in Any, True)
self.assertEqual(numpy.bool_(True) in Any, True)
self.assertEqual(numpy.bool_(False) in Any, True)
self.assertEqual(numpy.float_(1.1) in Any, True)
self.assertEqual(numpy.float64(1.1) in Any, True)
self.assertEqual(numpy.int_(2) in Any, True)

def test_setargs1(self):
Expand Down
24 changes: 12 additions & 12 deletions pyomo/repn/plugins/lp_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,13 +458,13 @@ def write(self, model):
addSymbol(con, label)
ostream.write(f'\n{label}:\n')
self.write_expression(ostream, repn, False)
ostream.write(f'>= {(lb - offset)!r}\n')
ostream.write(f'>= {(lb - offset)!s}\n')
elif lb == ub:
label = f'c_e_{symbol}_'
addSymbol(con, label)
ostream.write(f'\n{label}:\n')
self.write_expression(ostream, repn, False)
ostream.write(f'= {(lb - offset)!r}\n')
ostream.write(f'= {(lb - offset)!s}\n')
else:
# We will need the constraint body twice. Generate
# in a buffer so we only have to do that once.
Expand All @@ -476,18 +476,18 @@ def write(self, model):
addSymbol(con, label)
ostream.write(f'\n{label}:\n')
ostream.write(buf)
ostream.write(f'>= {(lb - offset)!r}\n')
ostream.write(f'>= {(lb - offset)!s}\n')
label = f'r_u_{symbol}_'
aliasSymbol(con, label)
ostream.write(f'\n{label}:\n')
ostream.write(buf)
ostream.write(f'<= {(ub - offset)!r}\n')
ostream.write(f'<= {(ub - offset)!s}\n')
elif ub is not None:
label = f'c_u_{symbol}_'
addSymbol(con, label)
ostream.write(f'\n{label}:\n')
self.write_expression(ostream, repn, False)
ostream.write(f'<= {(ub - offset)!r}\n')
ostream.write(f'<= {(ub - offset)!s}\n')

if with_debug_timing:
# report the last constraint
Expand Down Expand Up @@ -527,8 +527,8 @@ def write(self, model):
# Note: Var.bounds guarantees the values are either (finite)
# native_numeric_types or None
lb, ub = v.bounds
lb = '-inf' if lb is None else repr(lb)
ub = '+inf' if ub is None else repr(ub)
lb = '-inf' if lb is None else str(lb)
ub = '+inf' if ub is None else str(ub)
ostream.write(f"\n {lb} <= {v_symbol} <= {ub}")

if integer_vars:
Expand Down Expand Up @@ -565,7 +565,7 @@ def write(self, model):
for v, w in getattr(soscon, 'get_items', soscon.items)():
if w.__class__ not in int_float:
w = float(f)
ostream.write(f" {getSymbol(v)}:{w!r}\n")
ostream.write(f" {getSymbol(v)}:{w!s}\n")

ostream.write("\nend\n")

Expand All @@ -584,9 +584,9 @@ def write_expression(self, ostream, expr, is_objective):
expr.linear.items(), key=lambda x: getVarOrder(x[0])
):
if coef < 0:
ostream.write(f'{coef!r} {getSymbol(getVar(vid))}\n')
ostream.write(f'{coef!s} {getSymbol(getVar(vid))}\n')
else:
ostream.write(f'+{coef!r} {getSymbol(getVar(vid))}\n')
ostream.write(f'+{coef!s} {getSymbol(getVar(vid))}\n')

quadratic = getattr(expr, 'quadratic', None)
if quadratic:
Expand All @@ -605,9 +605,9 @@ def _normalize_constraint(data):
col = c1, c2
sym = f' {getSymbol(getVar(vid1))} * {getSymbol(getVar(vid2))}\n'
if coef < 0:
return col, repr(coef) + sym
return col, str(coef) + sym
else:
return col, '+' + repr(coef) + sym
return col, f'+{coef!s}{sym}'

if is_objective:
#
Expand Down
Loading

0 comments on commit ae354ae

Please sign in to comment.