"""Transformation to detect variables fixed by bounds and fix them.""" import textwrap from math import fabs from six import iteritems from pyomo.core.base.plugin import TransformationFactory from pyomo.common.collections import ComponentMap from pyomo.common.config import (ConfigBlock, ConfigValue, NonNegativeFloat, add_docstring_list) from pyomo.core.base.var import Var from pyomo.core.expr.numvalue import value from pyomo.core.plugins.transform.hierarchy import IsomorphicTransformation @TransformationFactory.register( 'contrib.detect_fixed_vars', doc="Detect variables that are de-facto fixed but not considered fixed.") class FixedVarDetector(IsomorphicTransformation): """Detects variables that are de-facto fixed but not considered fixed. For each variable :math:`v` found on the model, check to see if its lower bound :math:`v^{LB}` is within some tolerance of its upper bound :math:`v^{UB}`. If so, fix the variable to the value of :math:`v^{LB}`. Keyword arguments below are specified for the ``apply_to`` and ``create_using`` functions. """ CONFIG = ConfigBlock("FixedVarDetector") CONFIG.declare("tmp", ConfigValue( default=False, domain=bool, description="True to store the set of transformed variables and " "their old values so that they can be restored." )) CONFIG.declare("tolerance", ConfigValue( default=1E-13, domain=NonNegativeFloat, description="tolerance on bound equality (LB == UB)" )) __doc__ = add_docstring_list(__doc__, CONFIG) def _apply_to(self, instance, **kwargs): config = self.CONFIG(kwargs) if config.tmp: instance._xfrm_detect_fixed_vars_old_values = ComponentMap() for var in instance.component_data_objects( ctype=Var, descend_into=True): if var.fixed or var.lb is None or var.ub is None: # if the variable is already fixed, or if it is missing a # bound, we skip it. continue if fabs(value(var.lb) - value(var.ub)) <= config.tolerance: if config.tmp: instance._xfrm_detect_fixed_vars_old_values[var] = \ var.value var.fix(var.lb) def revert(self, instance): """Revert variables fixed by the transformation.""" for var, var_value in iteritems( instance._xfrm_detect_fixed_vars_old_values): var.unfix() var.set_value(var_value) del instance._xfrm_detect_fixed_vars_old_values