Skip to content

Commit

Permalink
Merge pull request #3015 from jsiirola/repn-dry-lp-nl-beforechild
Browse files Browse the repository at this point in the history
Consolidate walker logic in LP/NL representations
  • Loading branch information
jsiirola authored Oct 23, 2023
2 parents 12be54c + db38e94 commit 47a9b26
Show file tree
Hide file tree
Showing 12 changed files with 905 additions and 672 deletions.
22 changes: 21 additions & 1 deletion pyomo/common/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,8 @@ def _finalize_matplotlib(module, available):
def _finalize_numpy(np, available):
if not available:
return
# Register ndarray as a native type to prevent 1-element ndarrays
# from accidentally registering ndarray as a native_numeric_type.
numeric_types.native_types.add(np.ndarray)
numeric_types.RegisterLogicalType(np.bool_)
for t in (
Expand All @@ -798,12 +800,30 @@ 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)
for t in (np.float_, np.float16, np.float32, np.float64):
_floats = [np.float_, 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, 'float96'):
_floats.append(np.float96)
if hasattr(np, 'float128'):
_floats.append(np.float128)
for t in _floats:
numeric_types.RegisterNumericType(t)
# We have deprecated RegisterBooleanType, so we will mock up the
# 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]
# 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, 'complex192'):
_complex.append(np.complex192)
if hasattr(np, 'complex256'):
_complex.append(np.complex256)
for t in _complex:
numeric_types.RegisterComplexType(t)


dill, dill_available = attempt_import('dill')
Expand Down
155 changes: 109 additions & 46 deletions pyomo/common/numeric_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@
#: Python set used to identify numeric constants. This set includes
#: native Python types as well as numeric types from Python packages
#: like numpy, which may be registered by users.
native_numeric_types = {int, float, complex}
#:
#: Note that :data:`native_numeric_types` does NOT include
#: :py:`complex`, as that is not a valid constant in Pyomo numeric
#: expressions.
native_numeric_types = {int, float}
native_integer_types = {int}
native_logical_types = {bool}
native_complex_types = {complex}
pyomo_constant_types = set() # includes NumericConstant

_native_boolean_types = {int, bool, str, bytes}
Expand All @@ -64,34 +69,53 @@
#: like numpy.
#:
#: :data:`native_types` = :data:`native_numeric_types <pyomo.core.expr.numvalue.native_numeric_types>` + { str }
native_types = set([bool, str, type(None), slice, bytes])
native_types = {bool, str, type(None), slice, bytes}
native_types.update(native_numeric_types)
native_types.update(native_integer_types)
native_types.update(_native_boolean_types)
native_types.update(native_complex_types)
native_types.update(native_logical_types)
native_types.update(_native_boolean_types)

nonpyomo_leaf_types.update(native_types)


def RegisterNumericType(new_type):
"""
A utility function for updating the set of types that are
recognized to handle numeric values.
def RegisterNumericType(new_type: type):
"""Register the specified type as a "numeric type".
A utility function for registering new types as "native numeric
types" that can be leaf nodes in Pyomo numeric expressions. The
type should be compatible with :py:class:`float` (that is, store a
scalar and be castable to a Python float).
Parameters
----------
new_type: type
The new numeric type (e.g, numpy.float64)
The argument should be a class (e.g, numpy.float64).
"""
native_numeric_types.add(new_type)
native_types.add(new_type)
nonpyomo_leaf_types.add(new_type)


def RegisterIntegerType(new_type):
"""
A utility function for updating the set of types that are
recognized to handle integer values. This also registers the type
as numeric but does not register it as boolean.
def RegisterIntegerType(new_type: type):
"""Register the specified type as an "integer type".
A utility function for registering new types as "native integer
types". Integer types can be leaf nodes in Pyomo numeric
expressions. The type should be compatible with :py:class:`float`
(that is, store a scalar and be castable to a Python float).
Registering a type as an integer type implies
:py:func:`RegisterNumericType`.
Note that integer types are NOT registered as logical / Boolean types.
Parameters
----------
new_type: type
The new integer type (e.g, numpy.int64)
The argument should be a class (e.g., numpy.int64).
"""
native_numeric_types.add(new_type)
native_integer_types.add(new_type)
Expand All @@ -104,26 +128,64 @@ def RegisterIntegerType(new_type):
"is deprecated. Users likely should use RegisterLogicalType.",
version='6.6.0',
)
def RegisterBooleanType(new_type):
"""
A utility function for updating the set of types that are
recognized as handling boolean values. This function does not
register the type of integer or numeric.
def RegisterBooleanType(new_type: type):
"""Register the specified type as a "logical type".
A utility function for registering new types as "native logical
types". Logical types can be leaf nodes in Pyomo logical
expressions. The type should be compatible with :py:class:`bool`
(that is, store a scalar and be castable to a Python bool).
Note that logical types are NOT registered as numeric types.
Parameters
----------
new_type: type
The new logical type (e.g, numpy.bool_)
The argument should be a class (e.g., numpy.bool_).
"""
_native_boolean_types.add(new_type)
native_types.add(new_type)
nonpyomo_leaf_types.add(new_type)


def RegisterLogicalType(new_type):
def RegisterComplexType(new_type: type):
"""Register the specified type as an "complex type".
A utility function for registering new types as "native complex
types". Complex types can NOT be leaf nodes in Pyomo numeric
expressions. The type should be compatible with :py:class:`complex`
(that is, store a scalar complex value and be castable to a Python
complex).
Note that complex types are NOT registered as logical or numeric types.
Parameters
----------
new_type: type
The new complex type (e.g, numpy.complex128)
"""
A utility function for updating the set of types that are
recognized as handling boolean values. This function does not
register the type of integer or numeric.
native_types.add(new_type)
native_complex_types.add(new_type)
nonpyomo_leaf_types.add(new_type)


def RegisterLogicalType(new_type: type):
"""Register the specified type as a "logical type".
A utility function for registering new types as "native logical
types". Logical types can be leaf nodes in Pyomo logical
expressions. The type should be compatible with :py:class:`bool`
(that is, store a scalar and be castable to a Python bool).
Note that logical types are NOT registered as numeric types.
Parameters
----------
new_type: type
The new logical type (e.g, numpy.bool_)
The argument should be a class (e.g., numpy.bool_).
"""
_native_boolean_types.add(new_type)
native_logical_types.add(new_type)
Expand All @@ -135,8 +197,9 @@ def check_if_numeric_type(obj):
"""Test if the argument behaves like a numeric type.
We check for "numeric types" by checking if we can add zero to it
without changing the object's type. If that works, then we register
the type in native_numeric_types.
without changing the object's type, and that the object compares to
0 in a meaningful way. If that works, then we register the type in
:py:attr:`native_numeric_types`.
"""
obj_class = obj.__class__
Expand Down Expand Up @@ -181,25 +244,25 @@ def check_if_numeric_type(obj):

def value(obj, exception=True):
"""
A utility function that returns the value of a Pyomo object or
expression.
Args:
obj: The argument to evaluate. If it is None, a
string, or any other primitive numeric type,
then this function simply returns the argument.
Otherwise, if the argument is a NumericValue
then the __call__ method is executed.
exception (bool): If :const:`True`, then an exception should
be raised when instances of NumericValue fail to
s evaluate due to one or more objects not being
initialized to a numeric value (e.g, one or more
variables in an algebraic expression having the
value None). If :const:`False`, then the function
returns :const:`None` when an exception occurs.
Default is True.
Returns: A numeric value or None.
A utility function that returns the value of a Pyomo object or
expression.
Args:
obj: The argument to evaluate. If it is None, a
string, or any other primitive numeric type,
then this function simply returns the argument.
Otherwise, if the argument is a NumericValue
then the __call__ method is executed.
exception (bool): If :const:`True`, then an exception should
be raised when instances of NumericValue fail to
evaluate due to one or more objects not being
initialized to a numeric value (e.g, one or more
variables in an algebraic expression having the
value None). If :const:`False`, then the function
returns :const:`None` when an exception occurs.
Default is True.
Returns: A numeric value or None.
"""
if obj.__class__ in native_types:
return obj
Expand Down
22 changes: 11 additions & 11 deletions pyomo/core/kernel/register_numpy_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
version='6.1',
)

from pyomo.core.expr.numvalue import (
from pyomo.common.numeric_types import (
RegisterNumericType,
RegisterIntegerType,
RegisterBooleanType,
native_complex_types,
native_numeric_types,
native_integer_types,
native_boolean_types,
Expand All @@ -37,13 +38,17 @@
numpy_float = []
numpy_bool_names = []
numpy_bool = []
numpy_complex_names = []
numpy_complex = []

if _has_numpy:
# 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_)

# Re-build the old numpy_* lists
for t in native_boolean_types:
Expand All @@ -63,13 +68,8 @@


# Complex
numpy_complex_names = []
numpy_complex = []
if _has_numpy:
numpy_complex_names.extend(('complex_', 'complex64', 'complex128'))
for _type_name in numpy_complex_names:
try:
_type = getattr(numpy, _type_name)
numpy_complex.append(_type)
except: # pragma:nocover
pass
for t in native_complex_types:
if t.__module__ == 'numpy':
if t.__name__ not in numpy_complex_names:
numpy_complex.append(t)
numpy_complex_names.append(t.__name__)
12 changes: 11 additions & 1 deletion pyomo/core/tests/unit/test_kernel_register_numpy_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# ___________________________________________________________________________

import pyomo.common.unittest as unittest
from pyomo.common.dependencies import numpy_available
from pyomo.common.dependencies import numpy, numpy_available
from pyomo.common.log import LoggingIntercept

# Boolean
Expand Down Expand Up @@ -38,12 +38,22 @@
numpy_float_names.append('float16')
numpy_float_names.append('float32')
numpy_float_names.append('float64')
if hasattr(numpy, 'float96'):
numpy_float_names.append('float96')
if hasattr(numpy, 'float128'):
# On some numpy builds, the name of float128 is longdouble
numpy_float_names.append(numpy.float128.__name__)
# Complex
numpy_complex_names = []
if numpy_available:
numpy_complex_names.append('complex_')
numpy_complex_names.append('complex64')
numpy_complex_names.append('complex128')
if hasattr(numpy, 'complex192'):
numpy_complex_names.append('complex192')
if hasattr(numpy, 'complex256'):
# On some numpy builds, the name of complex256 is clongdouble
numpy_complex_names.append(numpy.complex256.__name__)


class TestNumpyRegistration(unittest.TestCase):
Expand Down
Loading

0 comments on commit 47a9b26

Please sign in to comment.