Skip to content

Commit

Permalink
Merge branch 'main' into add_highs_support
Browse files Browse the repository at this point in the history
  • Loading branch information
ZedongPeng authored Mar 12, 2024
2 parents d12d69b + b06ddea commit daaf767
Show file tree
Hide file tree
Showing 31 changed files with 558 additions and 825 deletions.
2 changes: 1 addition & 1 deletion doc/OnlineDocs/src/expr/managing.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def clone_expression(expr):
# x[0] + 5*x[1]
print(str(ce))
# x[0] + 5*x[1]
print(e.arg(0) is not ce.arg(0))
print(e.arg(0) is ce.arg(0))
# True
print(e.arg(1) is not ce.arg(1))
# True
Expand Down
9 changes: 8 additions & 1 deletion pyomo/contrib/pyros/master_problem_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,10 +398,17 @@ def construct_dr_polishing_problem(model_data, config):
all_ub_cons.append(polishing_absolute_value_ub_cons)

# get monomials; ensure second-stage variable term excluded
#
# the dr_eq is a linear sum where the first term is the
# second-stage variable: the remainder of the terms will be
# either MonomialTermExpressions or bare VarData
dr_expr_terms = dr_eq.body.args[:-1]

for dr_eq_term in dr_expr_terms:
dr_var_in_term = dr_eq_term.args[-1]
if dr_eq_term.is_expression_type():
dr_var_in_term = dr_eq_term.args[-1]
else:
dr_var_in_term = dr_eq_term
dr_var_in_term_idx = dr_var_in_term.index()

# get corresponding polishing variable
Expand Down
20 changes: 12 additions & 8 deletions pyomo/contrib/pyros/pyros_algorithm_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
)
from pyomo.contrib.pyros.util import get_main_elapsed_time, coefficient_matching
from pyomo.core.base import value
from pyomo.core.expr import MonomialTermExpression
from pyomo.common.collections import ComponentSet, ComponentMap
from pyomo.core.base.var import _VarData as VarData
from itertools import chain
Expand Down Expand Up @@ -69,14 +70,17 @@ def get_dr_var_to_scaled_expr_map(
ssv_dr_eq_zip = zip(second_stage_vars, decision_rule_eqns)
for ssv_idx, (ssv, dr_eq) in enumerate(ssv_dr_eq_zip):
for term in dr_eq.body.args:
is_ssv_term = (
isinstance(term.args[0], int)
and term.args[0] == -1
and isinstance(term.args[1], VarData)
)
if not is_ssv_term:
dr_var = term.args[1]
var_to_scaled_expr_map[dr_var] = term
if isinstance(term, MonomialTermExpression):
is_ssv_term = (
isinstance(term.args[0], int)
and term.args[0] == -1
and isinstance(term.args[1], VarData)
)
if not is_ssv_term:
dr_var = term.args[1]
var_to_scaled_expr_map[dr_var] = term
elif isinstance(term, VarData):
var_to_scaled_expr_map[term] = MonomialTermExpression((1, term))

return var_to_scaled_expr_map

Expand Down
40 changes: 24 additions & 16 deletions pyomo/contrib/pyros/tests/test_grcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from pyomo.common.collections import ComponentSet, ComponentMap
from pyomo.common.config import ConfigBlock, ConfigValue
from pyomo.core.base.set_types import NonNegativeIntegers
from pyomo.core.base.var import _VarData
from pyomo.core.expr import (
identify_variables,
identify_mutable_parameters,
Expand Down Expand Up @@ -571,22 +572,30 @@ def test_dr_eqns_form_correct(self):
dr_polynomial_terms, indexed_dr_var.values(), dr_monomial_param_combos
)
for idx, (term, dr_var, param_combo) in enumerate(dr_polynomial_zip):
# term should be a monomial expression of form
# (uncertain parameter product) * (decision rule variable)
# so length of expression object should be 2
self.assertEqual(
len(term.args),
2,
msg=(
f"Length of `args` attribute of term {str(term)} "
f"of DR equation {dr_eq.name!r} is not as expected. "
f"Args: {term.args}"
),
)
# term should be either a monomial expression or scalar variable
if isinstance(term, MonomialTermExpression):
# should be of form (uncertain parameter product) *
# (decision rule variable) so length of expression
# object should be 2
self.assertEqual(
len(term.args),
2,
msg=(
f"Length of `args` attribute of term {str(term)} "
f"of DR equation {dr_eq.name!r} is not as expected. "
f"Args: {term.args}"
),
)

# check that uncertain parameters participating in
# the monomial are as expected
param_product_multiplicand = term.args[0]
dr_var_multiplicand = term.args[1]
else:
self.assertIsInstance(term, _VarData)
param_product_multiplicand = 1
dr_var_multiplicand = term

# check that uncertain parameters participating in
# the monomial are as expected
param_product_multiplicand = term.args[0]
if idx == 0:
# static DR term
param_combo_found_in_term = (param_product_multiplicand,)
Expand All @@ -612,7 +621,6 @@ def test_dr_eqns_form_correct(self):

# check that DR variable participating in the monomial
# is as expected
dr_var_multiplicand = term.args[1]
self.assertIs(
dr_var_multiplicand,
dr_var,
Expand Down
11 changes: 8 additions & 3 deletions pyomo/contrib/solver/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
class SolverBase(abc.ABC):
"""
This base class defines the methods required for all solvers:
- available: Determines whether the solver is able to be run, combining both whether it can be found on the system and if the license is valid.
- available: Determines whether the solver is able to be run,
combining both whether it can be found on the system and if the license is valid.
- solve: The main method of every solver
- version: The version of the solver
- is_persistent: Set to false for all non-persistent solvers.
Expand Down Expand Up @@ -418,8 +419,12 @@ def _map_results(self, model, results):
]
legacy_soln.status = legacy_solution_status_map[results.solution_status]
legacy_results.solver.termination_message = str(results.termination_condition)
obj = get_objective(model)
if len(list(obj)) > 0:
legacy_results.problem.number_of_constraints = model.nconstraints()
legacy_results.problem.number_of_variables = model.nvariables()
number_of_objectives = model.nobjectives()
legacy_results.problem.number_of_objectives = number_of_objectives
if number_of_objectives == 1:
obj = get_objective(model)
legacy_results.problem.sense = obj.sense

if obj.sense == minimize:
Expand Down
23 changes: 21 additions & 2 deletions pyomo/core/base/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# This software is distributed under the 3-clause BSD License.
# ___________________________________________________________________________

from __future__ import annotations
import copy
import logging
import sys
Expand All @@ -21,6 +22,7 @@
from io import StringIO
from itertools import filterfalse, chain
from operator import itemgetter, attrgetter
from typing import Union, Any, Type

from pyomo.common.autoslots import AutoSlots
from pyomo.common.collections import Mapping
Expand All @@ -44,6 +46,7 @@
from pyomo.core.base.indexed_component import (
ActiveIndexedComponent,
UnindexedComponent_set,
IndexedComponent,
)

from pyomo.opt.base import ProblemFormat, guess_format
Expand Down Expand Up @@ -539,7 +542,7 @@ def __init__(self, component):
super(_BlockData, self).__setattr__('_decl_order', [])
self._private_data = None

def __getattr__(self, val):
def __getattr__(self, val) -> Union[Component, IndexedComponent, Any]:
if val in ModelComponentFactory:
return _component_decorator(self, ModelComponentFactory.get_class(val))
# Since the base classes don't support getattr, we can just
Expand All @@ -548,7 +551,7 @@ def __getattr__(self, val):
"'%s' object has no attribute '%s'" % (self.__class__.__name__, val)
)

def __setattr__(self, name, val):
def __setattr__(self, name: str, val: Union[Component, IndexedComponent, Any]):
"""
Set an attribute of a block data object.
"""
Expand Down Expand Up @@ -2007,6 +2010,17 @@ class Block(ActiveIndexedComponent):
_ComponentDataClass = _BlockData
_private_data_initializers = defaultdict(lambda: dict)

@overload
def __new__(
cls: Type[Block], *args, **kwds
) -> Union[ScalarBlock, IndexedBlock]: ...

@overload
def __new__(cls: Type[ScalarBlock], *args, **kwds) -> ScalarBlock: ...

@overload
def __new__(cls: Type[IndexedBlock], *args, **kwds) -> IndexedBlock: ...

def __new__(cls, *args, **kwds):
if cls != Block:
return super(Block, cls).__new__(cls)
Expand Down Expand Up @@ -2251,6 +2265,11 @@ class IndexedBlock(Block):
def __init__(self, *args, **kwds):
Block.__init__(self, *args, **kwds)

@overload
def __getitem__(self, index) -> _BlockData: ...

__getitem__ = IndexedComponent.__getitem__ # type: ignore


#
# Deprecated functions.
Expand Down
19 changes: 19 additions & 0 deletions pyomo/core/base/constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
# This software is distributed under the 3-clause BSD License.
# ___________________________________________________________________________

from __future__ import annotations
import sys
import logging
from weakref import ref as weakref_ref
from pyomo.common.pyomo_typing import overload
from typing import Union, Type

from pyomo.common.deprecation import RenamedClass
from pyomo.common.errors import DeveloperError
Expand Down Expand Up @@ -42,6 +44,7 @@
ActiveIndexedComponent,
UnindexedComponent_set,
rule_wrapper,
IndexedComponent,
)
from pyomo.core.base.set import Set
from pyomo.core.base.disable_methods import disable_methods
Expand Down Expand Up @@ -728,6 +731,17 @@ class Infeasible(object):
Violated = Infeasible
Satisfied = Feasible

@overload
def __new__(
cls: Type[Constraint], *args, **kwds
) -> Union[ScalarConstraint, IndexedConstraint]: ...

@overload
def __new__(cls: Type[ScalarConstraint], *args, **kwds) -> ScalarConstraint: ...

@overload
def __new__(cls: Type[IndexedConstraint], *args, **kwds) -> IndexedConstraint: ...

def __new__(cls, *args, **kwds):
if cls != Constraint:
return super(Constraint, cls).__new__(cls)
Expand Down Expand Up @@ -1020,6 +1034,11 @@ def add(self, index, expr):
"""Add a constraint with a given index."""
return self.__setitem__(index, expr)

@overload
def __getitem__(self, index) -> _GeneralConstraintData: ...

__getitem__ = IndexedComponent.__getitem__ # type: ignore


@ModelComponentFactory.register("A list of constraint expressions.")
class ConstraintList(IndexedConstraint):
Expand Down
4 changes: 2 additions & 2 deletions pyomo/core/base/indexed_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import pyomo.core.base as BASE
from pyomo.core.base.indexed_component_slice import IndexedComponent_slice
from pyomo.core.base.initializer import Initializer
from pyomo.core.base.component import Component, ActiveComponent
from pyomo.core.base.component import Component, ActiveComponent, ComponentData
from pyomo.core.base.config import PyomoOptions
from pyomo.core.base.enums import SortComponents
from pyomo.core.base.global_set import UnindexedComponent_set
Expand Down Expand Up @@ -606,7 +606,7 @@ def iteritems(self):
"""Return a list (index,data) tuples from the dictionary"""
return self.items()

def __getitem__(self, index):
def __getitem__(self, index) -> ComponentData:
"""
This method returns the data corresponding to the given index.
"""
Expand Down
15 changes: 14 additions & 1 deletion pyomo/core/base/param.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
# This software is distributed under the 3-clause BSD License.
# ___________________________________________________________________________

from __future__ import annotations
import sys
import types
import logging
from weakref import ref as weakref_ref
from pyomo.common.pyomo_typing import overload
from typing import Union, Type

from pyomo.common.autoslots import AutoSlots
from pyomo.common.deprecation import deprecation_warning, RenamedClass
Expand Down Expand Up @@ -291,6 +293,17 @@ class NoValue(object):

pass

@overload
def __new__(
cls: Type[Param], *args, **kwds
) -> Union[ScalarParam, IndexedParam]: ...

@overload
def __new__(cls: Type[ScalarParam], *args, **kwds) -> ScalarParam: ...

@overload
def __new__(cls: Type[IndexedParam], *args, **kwds) -> IndexedParam: ...

def __new__(cls, *args, **kwds):
if cls != Param:
return super(Param, cls).__new__(cls)
Expand Down Expand Up @@ -983,7 +996,7 @@ def _create_objects_for_deepcopy(self, memo, component_list):
# between potentially variable GetItemExpression objects and
# "constant" GetItemExpression objects. That will need to wait for
# the expression rework [JDS; Nov 22].
def __getitem__(self, args):
def __getitem__(self, args) -> _ParamData:
try:
return super().__getitem__(args)
except:
Expand Down
16 changes: 15 additions & 1 deletion pyomo/core/base/set.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@
# This software is distributed under the 3-clause BSD License.
# ___________________________________________________________________________

from __future__ import annotations
import inspect
import itertools
import logging
import math
import sys
import weakref
from pyomo.common.pyomo_typing import overload
from typing import Union, Type, Any as typingAny
from collections.abc import Iterator

from pyomo.common.collections import ComponentSet
from pyomo.common.deprecation import deprecated, deprecation_warning, RenamedClass
Expand Down Expand Up @@ -569,7 +572,7 @@ def isordered(self):
def subsets(self, expand_all_set_operators=None):
return iter((self,))

def __iter__(self):
def __iter__(self) -> Iterator[typingAny]:
"""Iterate over the set members
Raises AttributeError for non-finite sets. This must be
Expand Down Expand Up @@ -1967,6 +1970,12 @@ class SortedOrder(object):
_ValidOrderedAuguments = {True, False, InsertionOrder, SortedOrder}
_UnorderedInitializers = {set}

@overload
def __new__(cls: Type[Set], *args, **kwds) -> Union[_SetData, IndexedSet]: ...

@overload
def __new__(cls: Type[OrderedScalarSet], *args, **kwds) -> OrderedScalarSet: ...

def __new__(cls, *args, **kwds):
if cls is not Set:
return super(Set, cls).__new__(cls)
Expand Down Expand Up @@ -2373,6 +2382,11 @@ def data(self):
"Return a dict containing the data() of each Set in this IndexedSet"
return {k: v.data() for k, v in self.items()}

@overload
def __getitem__(self, index) -> _SetData: ...

__getitem__ = IndexedComponent.__getitem__ # type: ignore


class FiniteScalarSet(_FiniteSetData, Set):
def __init__(self, **kwds):
Expand Down
Loading

0 comments on commit daaf767

Please sign in to comment.