Skip to content

Commit

Permalink
Merge branch 'main' into copt_pyomo
Browse files Browse the repository at this point in the history
  • Loading branch information
mrmundt authored Oct 24, 2023
2 parents e6d784b + 02674df commit 3efaaed
Show file tree
Hide file tree
Showing 21 changed files with 976 additions and 690 deletions.
9 changes: 6 additions & 3 deletions .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 openmpi
PACKAGES: mpi4py

- os: ubuntu-latest
python: '3.10'
Expand Down Expand Up @@ -318,6 +318,8 @@ jobs:
fi
done
echo "*** Install Pyomo dependencies ***"
# Note: this will fail the build if any installation fails (or
# possibly if it outputs messages to stderr)
conda install --update-deps -q -y $CONDA_DEPENDENCIES
if test -z "${{matrix.slim}}"; then
PYVER=$(echo "py${{matrix.python}}" | sed 's/\.//g')
Expand All @@ -334,7 +336,7 @@ jobs:
echo "$_PKGLIST"
_BASE=$(echo "$PKG" | sed 's/[=<>].*//')
_BUILDS=$(echo "$_PKGLIST" | grep "^$_BASE " \
| sed -r 's/\s+/ /g' | cut -d\ -f3) || echo ""x
| sed -r 's/\s+/ /g' | cut -d\ -f3) || echo ""
if test -n "$_BUILDS"; then
_ISPY=$(echo "$_BUILDS" | grep "^py") \
|| echo "No python build detected"
Expand Down Expand Up @@ -630,7 +632,8 @@ jobs:
# is fully generated by a single process before invoking MPI
$PYTHON_EXE -c "from pyomo.dataportal.parse_datacmds import \
parse_data_commands; parse_data_commands(data='')"
mpirun -np ${{matrix.mpi}} --oversubscribe pytest -v \
# Note: if we are testing with openmpi, add '--oversubscribe'
mpirun -np ${{matrix.mpi}} pytest -v \
--junit-xml=TEST-pyomo-mpi.xml \
-m "mpi" -W ignore::Warning \
pyomo `pwd`/pyomo-model-libraries
Expand Down
7 changes: 5 additions & 2 deletions .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 openmpi
PACKAGES: mpi4py

- os: ubuntu-latest
python: 3.11
Expand Down Expand Up @@ -348,6 +348,8 @@ jobs:
fi
done
echo "*** Install Pyomo dependencies ***"
# Note: this will fail the build if any installation fails (or
# possibly if it outputs messages to stderr)
conda install --update-deps -q -y $CONDA_DEPENDENCIES
if test -z "${{matrix.slim}}"; then
PYVER=$(echo "py${{matrix.python}}" | sed 's/\.//g')
Expand Down Expand Up @@ -660,7 +662,8 @@ jobs:
# is fully generated by a single process before invoking MPI
$PYTHON_EXE -c "from pyomo.dataportal.parse_datacmds import \
parse_data_commands; parse_data_commands(data='')"
mpirun -np ${{matrix.mpi}} --oversubscribe pytest -v \
# Note: if we are testing with openmpi, add '--oversubscribe'
mpirun -np ${{matrix.mpi}} pytest -v \
--junit-xml=TEST-pyomo-mpi.xml \
-m "mpi" -W ignore::Warning \
pyomo `pwd`/pyomo-model-libraries
Expand Down
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
3 changes: 2 additions & 1 deletion pyomo/common/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,8 @@ def writelines(self, sequence):
r'|(?:\[\s*[A-Za-z0-9\.]+\s*\] +)' # [PASS]|[FAIL]|[ OK ]
)
_verbatim_line_start = re.compile(
r'(\| )' r'|(\+((-{3,})|(={3,}))\+)' # line blocks # grid table
r'(\| )' # line blocks
r'|(\+((-{3,})|(={3,}))\+)' # grid table
)
_verbatim_line = re.compile(
r'(={3,}[ =]+)' # simple tables, ======== sections
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
16 changes: 9 additions & 7 deletions pyomo/contrib/pynumero/examples/tests/test_mpi_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,28 @@
)

SKIPTESTS = []
if numpy_available and scipy_available:
pass
else:
SKIPTESTS.append("Pynumero needs scipy and numpy>=1.13.0 to run BlockMatrix tests")
if not numpy_available:
SKIPTESTS.append("Pynumero needs numpy>=1.13.0 to run BlockMatrix tests")
if not scipy_available:
SKIPTESTS.append("Pynumero needs scipy to run BlockMatrix tests")

try:
from mpi4py import MPI

comm = MPI.COMM_WORLD
if comm.Get_size() != 3:
SKIPTESTS.append("Pynumero MPI examples require exactly 3 processes")
SKIPTESTS.append(
f"Pynumero MPI examples require 3 MPI processes (got {comm.Get_size()})"
)
except ImportError:
SKIPTESTS.append("Pynumero MPI examples require exactly 3 processes")
SKIPTESTS.append("Pynumero MPI examples require mpi4py")

if not SKIPTESTS:
from pyomo.contrib.pynumero.examples import parallel_vector_ops, parallel_matvec


@unittest.pytest.mark.mpi
@unittest.skipIf(SKIPTESTS, SKIPTESTS)
@unittest.skipIf(SKIPTESTS, "\n".join(SKIPTESTS))
class TestExamples(unittest.TestCase):
def test_parallel_vector_ops(self):
z1_local, z2, z3 = parallel_vector_ops.main()
Expand Down
2 changes: 1 addition & 1 deletion pyomo/core/expr/calculus/diff_with_pyomo.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ def _diff_GeneralExpression(node, val_dict, der_dict):
val_dict: ComponentMap
der_dict: ComponentMap
"""
der_dict[node.expr] += der_dict[node]
der_dict[node.arg(0)] += der_dict[node]


def _diff_ExternalFunctionExpression(node, val_dict, der_dict):
Expand Down
Loading

0 comments on commit 3efaaed

Please sign in to comment.