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
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
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 )
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 ] )