Skip to content

Commit

Permalink
Merge branch 'main' into solvers
Browse files Browse the repository at this point in the history
  • Loading branch information
mrmundt authored Feb 18, 2025
2 parents 824a316 + cc2185b commit 3f15181
Show file tree
Hide file tree
Showing 32 changed files with 1,207 additions and 297 deletions.
37 changes: 12 additions & 25 deletions doc/OnlineDocs/explanation/philosophy/expressions/managing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,6 @@ tree:
seven event callbacks that users can hook into, providing very
fine-grained control over the expression walker.

:class:`SimpleExpressionVisitor <pyomo.core.expr.SimpleExpressionVisitor>`
A :func:`visitor` method is called for each node in the tree,
and the visitor class collects information about the tree.

:class:`ExpressionValueVisitor <pyomo.core.expr.ExpressionValueVisitor>`
When the :func:`visitor` method is called on each node in the
tree, the *values* of its children have been computed. The
Expand All @@ -166,25 +162,18 @@ These classes define a variety of suitable tree search methods:

* ``walk_expression``: depth-first traversal of the expression tree.

* :class:`SimpleExpressionVisitor <pyomo.core.expr.SimpleExpressionVisitor>`

* ``xbfs``: breadth-first search where leaf nodes are immediately visited
* ``xbfs_yield_leaves``: breadth-first search where leaf nodes are
immediately visited, and the visit method yields a value

* :class:`ExpressionValueVisitor <pyomo.core.expr.ExpressionValueVisitor>`

* ``dfs_postorder_stack``: postorder depth-first search using a
nonrecursive stack


To implement a visitor object, a user needs to provide specializations
for specific events. For legacy visitors based on the PyUtilib
visitor pattern (e.g., :class:`SimpleExpressionVisitor` and
:class:`ExpressionValueVisitor`), one must create a subclass of one of these
classes and override at least one of the following:
for specific events. For legacy visitors based on the PyUtilib visitor
pattern (e.g., :class:`ExpressionValueVisitor`), one must create a
subclass and override at least one of the following:

:func:`visitor`
:func:`visit`
Defines the operation that is performed when a node is visited. In
the :class:`ExpressionValueVisitor
<pyomo.core.expr.ExpressionValueVisitor>` and
Expand All @@ -196,10 +185,7 @@ classes and override at least one of the following:
Checks if the search should terminate with this node. If no,
then this method returns the tuple ``(False, None)``. If yes,
then this method returns ``(False, value)``, where *value* is
computed by this method. This method is not used in the
:class:`SimpleExpressionVisitor
<pyomo.core.expr.SimpleExpressionVisitor>` visitor
class.
computed by this method.

:func:`finalize`
This method defines the final value that is returned from the
Expand All @@ -216,19 +202,20 @@ callbacks, which are documented in the class documentation.
Detailed documentation of the APIs for these methods is provided
with the class documentation for these visitors.

SimpleExpressionVisitor Example
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
StreamBasedExpressionVisitor Example
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In this example, we describe an visitor class that counts the number
of nodes in an expression (including leaf nodes). Consider the following
class:

.. literalinclude:: /src/expr/managing_visitor1.spy

The class constructor creates a counter, and the :func:`visit` method
increments this counter for every node that is visited. The :func:`finalize`
method returns the value of this counter after the tree has been walked. The
following function illustrates this use of this visitor class:
The :func:`initializeWalker` method creates a counter, and the
:func:`exitNode` method increments this counter for every node that is
visited. The :func:`finalizeResult` method returns the value of this
counter after the tree has been walked. The following function
illustrates this use of this visitor class:

.. literalinclude:: /src/expr/managing_visitor2.spy

Expand Down
45 changes: 21 additions & 24 deletions doc/OnlineDocs/explanation/solvers/pyros.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,8 @@ where:
PyROS accepts models in which there are:

1. Bounds declared on the ``Var`` objects representing
components of the variable vectors :math:`z` and :math:`y`.
These bounds are reformulated to inequality constraints.
2. Ranged inequality constraints. These are easily reformulated to
single inequality constraints.
components of the variable vectors
2. Ranged inequality constraints

In order to cast the robust optimization counterpart of the
:ref:`deterministic model <deterministic-model>`,
Expand Down Expand Up @@ -186,9 +184,9 @@ The PyROS solver is invoked through the
Upon successful convergence of PyROS, the solution returned is
certified to be robust optimal only if:

1. master problems are solved to global optimality
1. Master problems are solved to global optimality
(by specifying ``solve_master_globally=True``)
2. a worst-case objective focus is chosen
2. A worst-case objective focus is chosen
(by specifying ``objective_focus=ObjectiveType.worst_case``)

Otherwise, the solution returned is certified to only be robust feasible.
Expand Down Expand Up @@ -958,10 +956,10 @@ Observe that the log contains the following information:
:linenos:
==============================================================================
PyROS: The Pyomo Robust Optimization Solver, v1.3.2.
PyROS: The Pyomo Robust Optimization Solver, v1.3.4.
Pyomo version: 6.9.0
Commit hash: unknown
Invoked at UTC 2024-11-01T00:00:00.000000
Invoked at UTC 2025-02-13T00:00:00.000000
Developed by: Natalie M. Isenberg (1), Jason A. F. Sherman (1),
John D. Siirola (2), Chrysanthos E. Gounaris (1)
Expand Down Expand Up @@ -998,7 +996,7 @@ Observe that the log contains the following information:
p_robustness={}
------------------------------------------------------------------------------
Preprocessing...
Done preprocessing; required wall time of 0.018s.
Done preprocessing; required wall time of 0.009s.
------------------------------------------------------------------------------
Model Statistics:
Number of variables : 62
Expand All @@ -1020,34 +1018,33 @@ Observe that the log contains the following information:
------------------------------------------------------------------------------
Itn Objective 1-Stg Shift 2-Stg Shift #CViol Max Viol Wall Time (s)
------------------------------------------------------------------------------
0 3.5838e+07 - - 1 2.7000e+02 0.657
1 3.6087e+07 8.0199e-01 1.2807e-01 5 4.1852e+04 1.460
2 3.6125e+07 8.7068e-01 2.7098e-01 8 2.7711e+01 3.041
3 3.6174e+07 7.6526e-01 2.2357e-01 4 1.3893e+02 4.186
4 3.6285e+07 2.8923e-01 3.4064e-01 0 1.2670e-09g 7.162
0 3.5838e+07 - - 5 1.8832e+04 0.412
1 3.5838e+07 1.2289e-09 1.5886e-12 5 2.8919e+02 0.992
2 3.6269e+07 3.1647e-01 1.0432e-01 4 2.9020e+02 1.865
3 3.6285e+07 7.6526e-01 2.2258e-01 0 2.3874e-12g 3.508
------------------------------------------------------------------------------
Robust optimal solution identified.
------------------------------------------------------------------------------
Timing breakdown:
Identifier ncalls cumtime percall %
-----------------------------------------------------------
main 1 7.163 7.163 100.0
main 1 3.509 3.509 100.0
------------------------------------------------------
dr_polishing 4 0.293 0.073 4.1
global_separation 27 1.106 0.041 15.4
local_separation 135 3.385 0.025 47.3
master 5 1.396 0.279 19.5
master_feasibility 4 0.155 0.039 2.2
preprocessing 1 0.018 0.018 0.2
other n/a 0.811 n/a 11.3
dr_polishing 3 0.209 0.070 6.0
global_separation 27 0.590 0.022 16.8
local_separation 108 1.569 0.015 44.7
master 4 0.654 0.163 18.6
master_feasibility 3 0.083 0.028 2.4
preprocessing 1 0.009 0.009 0.3
other n/a 0.394 n/a 11.2
======================================================
===========================================================
------------------------------------------------------------------------------
Termination stats:
Iterations : 5
Solve time (wall s) : 7.163
Iterations : 4
Solve time (wall s) : 3.509
Final objective value : 3.6285e+07
Termination condition : pyrosTerminationCondition.robust_optimal
------------------------------------------------------------------------------
Expand Down
1 change: 0 additions & 1 deletion doc/OnlineDocs/reference/topical/expressions/visitors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ Visitor Classes
.. autosummary::

pyomo.core.expr.StreamBasedExpressionVisitor
pyomo.core.expr.SimpleExpressionVisitor
pyomo.core.expr.ExpressionValueVisitor
pyomo.core.expr.ExpressionReplacementVisitor
24 changes: 16 additions & 8 deletions doc/OnlineDocs/src/expr/managing.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,17 @@
import pyomo.core.expr as EXPR


class SizeofVisitor(EXPR.SimpleExpressionVisitor):
def __init__(self):
class SizeofVisitor(EXPR.StreamBasedExpressionVisitor):
def initializeWalker(self, expr):
self.counter = 0
return True, expr

def visit(self, node):
def exitNode(self, node, data):
self.counter += 1

def finalize(self):
def finalizeResult(self, result):
return self.counter

# @visitor1
# @visitor1


# ---------------------------------------------
Expand All @@ -122,12 +122,20 @@ def sizeof_expression(expr):
#
visitor = SizeofVisitor()
#
# Compute the value using the :func:`xbfs` search method.
# Compute the value using the :func:`walk_expression` search method.
#
return visitor.xbfs(expr)
return visitor.walk_expression(expr)
# @visitor2


# Test:
m = ConcreteModel()
m.x = Var()
m.p = Param(mutable=True)
assert sizeof_expression(m.x) == 1
assert sizeof_expression(m.x + m.p) == 3
assert sizeof_expression(2 * m.x + m.p) == 5

# ---------------------------------------------
# @visitor3
import pyomo.core.expr as EXPR
Expand Down
2 changes: 1 addition & 1 deletion examples/mpec/munson1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ runtime:
logfile: null # Redirect output to the specified file.
catch errors: true # Trigger failures for exceptions to print
# the program stack.
disable gc: false # Disable the garbage collecter.
disable gc: false # Disable the garbage collector.
interactive: false # After executing Pyomo, launch an
# interactive Python shell. If IPython is
# installed, this shell is an IPython
Expand Down
6 changes: 3 additions & 3 deletions pyomo/common/tee.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ def __init__(self, fd=1, output=None, synchronize=True):

def __enter__(self):
if self.std:
# important: flush the current file buffer when redirecting
getattr(sys, self.std).flush()
self.original_file = getattr(sys, self.std)
# important: flush the current file buffer when redirecting
self.original_file.flush()
# Duplicate the original standard file descriptor(file
# descriptor 1 or 2) to a different file descriptor number
self.original_fd = os.dup(self.fd)
Expand All @@ -135,7 +135,7 @@ def __enter__(self):
os.dup2(out_fd, self.fd, inheritable=bool(self.std))

# We no longer need this original file descriptor
if out_fd is not self.target:
if not isinstance(self.target, int):
os.close(out_fd)

if self.std:
Expand Down
2 changes: 2 additions & 0 deletions pyomo/contrib/cp/scheduling_expr/precedence_expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@


class PrecedenceExpression(BooleanExpression):
PRECEDENCE = None

def nargs(self):
return 3

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
)
from pyomo.core.expr.base import ExpressionBase
from pyomo.core.expr.logical_expr import BooleanExpression
from pyomo.core.expr.numeric_expr import SumExpression


def _sum_two_units(_self, _other):
Expand Down Expand Up @@ -119,6 +120,7 @@ class StepFunction(ExpressionBase):
"""

__slots__ = ()
PRECEDENCE = None

def __add__(self, other):
return _generate_sum_expression(self, other)
Expand Down Expand Up @@ -287,6 +289,7 @@ class CumulativeFunction(StepFunction):
"""

__slots__ = ('_args_', '_nargs')
PRECEDENCE = SumExpression.PRECEDENCE

def __init__(self, args, nargs=None):
# We make sure args are a list because we might add to them later, if
Expand Down Expand Up @@ -363,7 +366,7 @@ def nargs(self):
return 5

def _to_string(self, values, verbose, smap):
return "(%s).within(bounds=(%s, %s), times=(%s, %s))" % (
return "%s.within(bounds=(%s, %s), times=(%s, %s))" % (
values[0],
values[1],
values[2],
Expand Down
5 changes: 1 addition & 4 deletions pyomo/contrib/mpc/data/get_cuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,8 @@ def get_indexed_cuid(var, sets=None, dereference=None, context=None):
ComponentUID corresponding to the provided ``var`` and sets
"""
# TODO: Does this function have a good name?
# Should this function be generalized beyond a single indexing set?
if isinstance(var, ComponentUID):
return var
elif isinstance(var, (str, IndexedComponent_slice)):
if isinstance(var, (str, IndexedComponent_slice, ComponentUID)):
# TODO: Raise error if string and context is None
return ComponentUID(var, context=context)
# At this point we are assuming var is a Pyomo Var or VarData object.
Expand Down
2 changes: 1 addition & 1 deletion pyomo/contrib/pynumero/sparse/block_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,7 @@ def ravel(self, order='C'):

def argmax(self, axis=None, out=None):
"""
Returns the index of the larges element.
Returns the index of the largest element.
"""
assert_block_structure(self)
return self.flatten().argmax(axis=axis, out=out)
Expand Down
35 changes: 35 additions & 0 deletions pyomo/contrib/pyros/CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,41 @@
PyROS CHANGELOG
===============


-------------------------------------------------------------------------------
PyROS 1.3.5 13 Feb 2025
-------------------------------------------------------------------------------
- Tweak online documentation, including solver logging output example
- Adjust UTC invocation time retrieval in PyROS logging to
deprecation of `datetime.datetime.now()` in Python 3.12
- Suppress error message emitted to console when PyROS
attempts to retrieve git commit hash of Pyomo installation that
is not a git repository
- Add more information to solver output logging message emitted upon
failure to solve deterministic (i.e., initial master) problem


-------------------------------------------------------------------------------
PyROS 1.3.4 22 Jan 2025
-------------------------------------------------------------------------------
- Fix typo that prevents fixed Vars from being included in model scope
- Fix typo that prevents proper initialization of auxiliary uncertain
parameters in the separation problems
- Unit test method for initializing separation problems
- Add tests checking model scope determined by solver argument validation
routine


-------------------------------------------------------------------------------
PyROS 1.3.3 03 Dec 2024
-------------------------------------------------------------------------------
- Add efficiency for handling PyROS separation problem sub-solver errors
- Add logger warnings to report sub-solver errors and inform that PyROS
will continue to solve if a violation is found
- Add unit tests for new sub-solver error handling for continuous
and discrete uncertainty sets


-------------------------------------------------------------------------------
PyROS 1.3.2 29 Nov 2024
-------------------------------------------------------------------------------
Expand Down
4 changes: 3 additions & 1 deletion pyomo/contrib/pyros/master_problem_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,9 @@ def solver_call_master(master_data):
)
deterministic_msg = (
(
" Please ensure your deterministic model "
" Please ensure that your deterministic model, "
"subject to the nominal uncertain parameter realization "
"you have provided, "
f"is solvable by at least one of the subordinate {solve_mode} "
"optimizers provided."
)
Expand Down
Loading

0 comments on commit 3f15181

Please sign in to comment.