Source code for pyomo.contrib.preprocessing.plugins.deactivate_trivial_constraints

# -*- coding: utf-8 -*-
#  ___________________________________________________________________________
#
#  Pyomo: Python Optimization Modeling Objects
#  Copyright (c) 2008-2024
#  National Technology and Engineering Solutions of Sandia, LLC
#  Under the terms of Contract DE-NA0003525 with National Technology and
#  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
#  rights in this software.
#  This software is distributed under the 3-clause BSD License.
#  ___________________________________________________________________________

"""Transformation to deactivate trivial constraints."""
import logging

from pyomo.common.collections import ComponentSet
from pyomo.common.config import (
    ConfigBlock,
    ConfigValue,
    NonNegativeFloat,
    document_kwargs_from_configdict,
)
from pyomo.common.errors import InfeasibleConstraintException
from pyomo.core.base.constraint import Constraint
from pyomo.core.base.transformation import TransformationFactory
from pyomo.core.expr.numvalue import value
from pyomo.core.plugins.transform.hierarchy import IsomorphicTransformation
from pyomo.repn import generate_standard_repn

logger = logging.getLogger('pyomo.contrib.preprocessing')


[docs]@TransformationFactory.register( 'contrib.deactivate_trivial_constraints', doc="Deactivate trivial constraints." ) @document_kwargs_from_configdict('CONFIG') class TrivialConstraintDeactivator(IsomorphicTransformation): """Deactivates trivial constraints. Trivial constraints take form :math:`k_1 = k_2` or :math:`k_1 \\leq k_2`, where :math:`k_1` and :math:`k_2` are constants. These constraints typically arise when variables are fixed. Keyword arguments below are specified for the ``apply_to`` and ``create_using`` functions. """ CONFIG = ConfigBlock("TrivialConstraintDeactivator") CONFIG.declare( "tmp", ConfigValue( default=False, domain=bool, description="True to store a set of transformed constraints for future" " reversion of the transformation.", ), ) CONFIG.declare( "ignore_infeasible", ConfigValue( default=False, domain=bool, description="True to skip over trivial constraints that are " "infeasible instead of raising an InfeasibleConstraintException.", ), ) CONFIG.declare( "return_trivial", ConfigValue( default=[], description="a list to which the deactivated trivial" "constraints are appended (side effect)", ), ) CONFIG.declare( "tolerance", ConfigValue( default=1e-13, domain=NonNegativeFloat, description="tolerance on constraint violations", ), ) def _apply_to(self, instance, **kwargs): config = self.CONFIG(kwargs) if config.tmp and not hasattr(instance, '_tmp_trivial_deactivated_constrs'): instance._tmp_trivial_deactivated_constrs = ComponentSet() elif config.tmp: logger.warning( 'Deactivating trivial constraints on the block {} for which ' 'trivial constraints were previously deactivated. ' 'Reversion will affect all deactivated constraints.'.format( instance.name ) ) # Trivial constraints are those that do not contain any variables, ie. # the polynomial degree is 0 for constr in instance.component_data_objects( ctype=Constraint, active=True, descend_into=True ): repn = generate_standard_repn(constr.body) if not repn.is_constant(): # This constraint is not trivial continue # We need to check each constraint to sure that it is not violated. constr_lb = value(constr.lower) if constr.has_lb() else float('-inf') constr_ub = value(constr.upper) if constr.has_ub() else float('inf') constr_value = repn.constant # Check if the lower bound is violated outside a given tolerance if constr_value + config.tolerance <= constr_lb: if config.ignore_infeasible: continue else: raise InfeasibleConstraintException( 'Trivial constraint {} violates LB {} ≤ BODY {}.'.format( constr.name, constr_lb, constr_value ) ) # Check if the upper bound is violated outside a given tolerance if constr_value >= constr_ub + config.tolerance: if config.ignore_infeasible: continue else: raise InfeasibleConstraintException( 'Trivial constraint {} violates BODY {} ≤ UB {}.'.format( constr.name, constr_value, constr_ub ) ) # Constraint is not infeasible. Deactivate it. if config.tmp: instance._tmp_trivial_deactivated_constrs.add(constr) config.return_trivial.append(constr) constr.deactivate()
[docs] def revert(self, instance): """Revert constraints deactivated by the transformation. Args: instance: the model instance on which trivial constraints were earlier deactivated. """ for constr in instance._tmp_trivial_deactivated_constrs: constr.activate() del instance._tmp_trivial_deactivated_constrs