Пример #1
0
 def domain(self, domain):
     """Sets the domain for all variables in this container."""
     # TODO: Ideally we would pass valid arguments to the initializer
     # that we just created.  However at the moment, getting the
     # index() is expensive (see #1228).  As a result, for the moment
     # we will only support constant initializers
     domain = SetInitializer(domain)(None, None)
     for vardata in self.values():
         vardata.domain = domain
Пример #2
0
 def domain(self, domain):
     try:
         self._domain = SetInitializer(domain)(None, None)
     except:
         logger.error(
             "%s is not a valid domain. Variable domains must be an "
             "instance of a Pyomo Set or convertable to a Pyomo Set."
             % (domain,),
             extra={'id': 'E2001'})
         raise
Пример #3
0
 def __init__(self, *args, **kwargs):
     #
     # Default keyword values
     #
     self._rule_init = Initializer(self._pop_from_kwargs(
         'Var', kwargs, ('rule', 'initialize'), None))
     self._rule_domain = SetInitializer(self._pop_from_kwargs(
         'Var', kwargs, ('domain', 'within'), Reals))
     _bounds_arg = kwargs.pop('bounds', None)
     self._dense = kwargs.pop('dense', True)
     self._units = kwargs.pop('units', None)
     if self._units is not None:
         self._units = units.get_units(self._units)
     #
     # Initialize the base class
     #
     kwargs.setdefault('ctype', Var)
     IndexedComponent.__init__(self, *args, **kwargs)
     #
     # Now that we can call is_indexed(), process bounds initializer
     #
     if self.is_indexed():
         treat_bounds_sequences_as_mappings = not (
             isinstance(_bounds_arg, Sequence)
             and len(_bounds_arg) == 2
             and not isinstance(_bounds_arg[0], Sequence)
         )
     else:
         treat_bounds_sequences_as_mappings = False
         if not self._dense:
             logger.warning(
                 "ScalarVar object '%s': dense=False is not allowed "
                 "for scalar variables; converting to dense=True"
                 % (self.name,))
             self._dense = True
     self._rule_bounds = Initializer(
         _bounds_arg,
         treat_sequences_as_mappings=treat_bounds_sequences_as_mappings
     )
Пример #4
0
class Var(IndexedComponent, IndexedComponent_NDArrayMixin):
    """A numeric variable, which may be defined over an index.

    Args:
        domain (Set or function, optional): A Set that defines valid
            values for the variable (e.g., ``Reals``, ``NonNegativeReals``,
            ``Binary``), or a rule that returns Sets.  Defaults to ``Reals``.
        within (Set or function, optional): An alias for ``domain``.
        bounds (tuple or function, optional): A tuple of ``(lower, upper)``
            bounds for the variable, or a rule that returns tuples.
            Defaults to ``(None, None)``.
        initialize (float or function, optional): The initial value for
            the variable, or a rule that returns initial values.
        rule (float or function, optional): An alias for ``initialize``.
        dense (bool, optional): Instantiate all elements from
            :meth:`index_set` when constructing the Var (True) or just the
            variables returned by ``initialize``/``rule`` (False).  Defaults
            to ``True``.
        units (pyomo units expression, optional): Set the units corresponding
            to the entries in this variable.
        name (str, optional): Name for this component.
        doc (str, optional): Text describing this component.
    """

    _ComponentDataClass = _GeneralVarData

    def __new__(cls, *args, **kwargs):
        if cls is not Var:
            return super(Var, cls).__new__(cls)
        if not args or (args[0] is UnindexedComponent_set and len(args)==1):
            return super(Var, cls).__new__(AbstractScalarVar)
        else:
            return super(Var, cls).__new__(IndexedVar)

    @overload
    def __init__(self, *indexes, domain=Reals, within=Reals, bounds=None,
                 initialize=None, rule=None, dense=True, units=None,
                 name=None, doc=None): ...
    
    def __init__(self, *args, **kwargs):
        #
        # Default keyword values
        #
        self._rule_init = Initializer(self._pop_from_kwargs(
            'Var', kwargs, ('rule', 'initialize'), None))
        self._rule_domain = SetInitializer(self._pop_from_kwargs(
            'Var', kwargs, ('domain', 'within'), Reals))
        _bounds_arg = kwargs.pop('bounds', None)
        self._dense = kwargs.pop('dense', True)
        self._units = kwargs.pop('units', None)
        if self._units is not None:
            self._units = units.get_units(self._units)
        #
        # Initialize the base class
        #
        kwargs.setdefault('ctype', Var)
        IndexedComponent.__init__(self, *args, **kwargs)
        #
        # Now that we can call is_indexed(), process bounds initializer
        #
        if self.is_indexed():
            treat_bounds_sequences_as_mappings = not (
                isinstance(_bounds_arg, Sequence)
                and len(_bounds_arg) == 2
                and not isinstance(_bounds_arg[0], Sequence)
            )
        else:
            treat_bounds_sequences_as_mappings = False
            if not self._dense:
                logger.warning(
                    "ScalarVar object '%s': dense=False is not allowed "
                    "for scalar variables; converting to dense=True"
                    % (self.name,))
                self._dense = True
        self._rule_bounds = Initializer(
            _bounds_arg,
            treat_sequences_as_mappings=treat_bounds_sequences_as_mappings
        )

    def flag_as_stale(self):
        """
        Set the 'stale' attribute of every variable data object to True.
        """
        for var_data in self._data.values():
            var_data.stale = True

    def get_values(self, include_fixed_values=True):
        """
        Return a dictionary of index-value pairs.
        """
        if include_fixed_values:
            return {idx:vardata.value for idx,vardata in self._data.items()}
        return {idx:vardata.value
                            for idx, vardata in self._data.items()
                                                if not vardata.fixed}

    extract_values = get_values

    def set_values(self, new_values, skip_validation=False):
        """
        Set the values of a dictionary.

        The default behavior is to validate the values in the
        dictionary.
        """
        for index, new_value in new_values.items():
            self[index].set_value(new_value, skip_validation)

    def get_units(self):
        """Return the units expression for this Var."""
        return self._units

    # TODO: deprecate this?  __getitem__ is generally preferable"
    def add(self, index):
        """Add a variable with a particular index."""
        return self[index]

    def construct(self, data=None):
        """
        Construct the _VarData objects for this variable
        """
        if self._constructed:
            return
        self._constructed=True

        timer = ConstructionTimer(self)
        if is_debug_set(logger):
            logger.debug("Constructing Variable %s" % (self.name,))

        # Note: define 'index' to avoid 'variable referenced before
        # assignment' in the error message generated in the 'except:'
        # block below.
        index = None
        try:
            # We do not (currently) accept data for constructing Variables
            assert data is None

            if not self.index_set().isfinite() and self._dense:
                # Note: if the index is not finite, then we cannot
                # iterate over it.  This used to be fatal; now we
                # just warn
                logger.warning(
                    "Var '%s' indexed by a non-finite set, but declared "
                    "with 'dense=True'.  Reverting to 'dense=False' as "
                    "it is not possible to make this variable dense.  "
                    "This warning can be suppressed by specifying "
                    "'dense=False'" % (self.name,))
                self._dense = False

            if ( self._rule_init is not None and
                 self._rule_init.contains_indices() ):
                # Historically we have allowed Vars to be initialized by
                # a sparse map (i.e., a dict containing only some of the
                # keys).  We will wrap the incoming initializer to map
                # KeyErrors to None
                self._rule_init = DefaultInitializer(
                    self._rule_init, None, KeyError)
                # The index is coming in externally; we need to validate it
                for index in self._rule_init.indices():
                    self[index]
                # If this is a dense object, we need to ensure that it
                # has been filled in.
                if self._dense:
                    for index in self.index_set():
                        if index not in self._data:
                            self._getitem_when_not_present(index)
            elif not self.is_indexed():
                # As there is only a single VarData to populate, just do
                # so and bypass all special-case testing below
                self._getitem_when_not_present(None)
            elif self._dense:
                # Special case: initialize every VarData.  For the
                # initializers that are constant, we can avoid
                # re-calling (and re-validating) the inputs in certain
                # cases.  To support this, we will create the first
                # _VarData and then use it as a template to initialize
                # (constant portions of) every VarData so as to not
                # repeat all the domain/bounds validation.
                try:
                    ref = self._getitem_when_not_present(
                        next(iter(self.index_set())))
                except StopIteration:
                    # Empty index!
                    return
                call_domain_rule = not self._rule_domain.constant()
                call_bounds_rule = self._rule_bounds is not None and (
                        not self._rule_bounds.constant())
                call_init_rule = self._rule_init is not None and (
                    not self._rule_init.constant()
                    # If either the domain or bounds change, then we
                    # need to re-verify the initial value, even if it is
                    # constant:
                    or call_domain_rule or call_bounds_rule
                )
                # Initialize all the component datas with the common data
                for index in self.index_set():
                    self._data[index] = self._ComponentDataClass.copy(ref)
                # Now go back and initialize any index-specific data
                block = self.parent_block()
                if call_domain_rule:
                    for index, obj in self._data.items():
                        # We can directly set the attribute (not the
                        # property) because the SetInitializer ensures
                        # that the value is a proper Set.
                        obj._domain = self._rule_domain(block, index)
                if call_bounds_rule:
                    for index, obj in self._data.items():
                        obj.lower, obj.upper = self._rule_bounds(block, index)
                if call_init_rule:
                    for index, obj in self._data.items():
                        obj.set_value(self._rule_init(block, index))
            else:
                # non-dense indexed var with generic
                # (non-index-containing) initializer: nothing to do
                pass

        except Exception:
            err = sys.exc_info()[1]
            logger.error(
                "Rule failed when initializing variable for "
                "Var %s with index %s:\n%s: %s"
                % (self.name,
                   str(index),
                   type(err).__name__,
                   err))
            raise
        finally:
            timer.report()

    #
    # This method must be defined on subclasses of
    # IndexedComponent that support implicit definition
    #
    def _getitem_when_not_present(self, index):
        """Returns the default component data value."""
        if index is None and not self.is_indexed():
            obj = self._data[index] = self
        else:
            obj = self._data[index] = self._ComponentDataClass(component=self)
        parent = self.parent_block()
        # We can directly set the attribute (not the property) because
        # the SetInitializer ensures that the value is a proper Set.
        obj._domain = self._rule_domain(parent, index)
        if self._rule_bounds is not None:
            obj.lower, obj.upper = self._rule_bounds(parent, index)
        if self._rule_init is not None:
            obj.set_value(self._rule_init(parent, index))
        return obj

    #
    # Because we need to do more initialization than simply calling
    # set_value(), we need to override _setitem_when_not_present
    #
    def _setitem_when_not_present(self, index, value=NOTSET):
        if value is self.Skip:
            return None
        try:
            obj = self._getitem_when_not_present(index)
            if value is not NOTSET:
                obj.set_value(value)
        except:
            self._data.pop(index, None)
            raise
        return obj

    def _pprint(self):
        """Print component information."""
        headers = [
            ("Size", len(self)),
            ("Index", self._index if self.is_indexed() else None),
        ]
        if self._units is not None:
            headers.append(('Units', str(self._units)))
        return ( headers,
                 self._data.items(),
                 ( "Lower","Value","Upper","Fixed","Stale","Domain"),
                 lambda k, v: [ value(v.lb),
                                v.value,
                                value(v.ub),
                                v.fixed,
                                v.stale,
                                v.domain
                                ]
                 )