Esempio n. 1
0
 def _apply_to(self, instance, **kwds):
     self._deterministic = kwds.get('deterministic', False)
     #
     # Process options
     #
     submodel = self._preprocess('bilevel.linear_mpec', instance, **kwds)
     instance.reclassify_component_type(submodel, Block)
     #
     # Create a block with optimality conditions
     #
     setattr(instance, self._submodel + '_kkt',
             self._add_optimality_conditions(instance, submodel))
     instance._transformation_data[
         'bilevel.linear_mpec'].submodel_cuid = ComponentUID(submodel)
     instance._transformation_data[
         'bilevel.linear_mpec'].block_cuid = ComponentUID(
             getattr(instance, self._submodel + '_kkt'))
     #-------------------------------------------------------------------------------
     #
     # Disable the original submodel and
     #
     #instance.reclassify_component_type(submodel, SubModel)
     #submodel.deactivate()
     # TODO: Cache the list of components that were deactivated
     for (name, data) in submodel.component_map(active=True).items():
         if not isinstance(data, Var) and not isinstance(data, Set):
             data.deactivate()
Esempio n. 2
0
    def pyro_sample_sp(self, size, **kwds):
        assert size > 0
        model = self.reference_model.clone()

        scenario_tree_model = \
            self._create_scenario_tree_model(size)
        factory = ScenarioTreeInstanceFactory(
            model=self.reference_model, scenario_tree=scenario_tree_model)
        options = \
            ScenarioTreeManagerClientPyro.register_options()
        for key in kwds:
            options[key] = kwds[key]
        manager = ScenarioTreeManagerClientPyro(options, factory=factory)
        try:
            init = manager.initialize(async_call=True)
            pcuids = ComponentMap()
            for param in self.stochastic_data:
                pcuids[param] = ComponentUID(param)
            init.complete()
            for scenario in manager.scenario_tree.scenarios:
                data = []
                for param, dist in self.stochastic_data.items():
                    data.append((pcuids[param], dist.sample()))
                manager.invoke_function(
                    "_update_data",
                    thisfile,
                    invocation_type=InvocationType.OnScenario(scenario.name),
                    function_args=(data, ),
                    oneway_call=True)
            manager.reference_model = model
        except:
            manager.close()
            raise
        return manager
Esempio n. 3
0
 def _apply_to(self, instance, **kwds):
     options = kwds.pop('options', {})
     #
     # Find the free variables
     #
     free_vars = {}
     id_list = []
     for vdata in instance.component_data_objects(
             Var, active=True, sort=SortComponents.deterministic):
         id_list.append(id(vdata))
         free_vars[id(vdata)] = vdata
     #
     # Iterate over the Complementarity components
     #
     cobjs = []
     for bdata in instance.block_data_objects(
             active=True, sort=SortComponents.deterministic):
         for cobj in bdata.component_objects(Complementarity,
                                             active=True,
                                             descend_into=False):
             cobjs.append(cobj)
             for index in sorted(iterkeys(cobj)):
                 _cdata = cobj[index]
                 if not _cdata.active:
                     continue
                 #
                 # Apply a variant of the standard form logic
                 #
                 self.to_common_form(_cdata, free_vars)
     #
     tdata = instance._transformation_data['mpec.nl']
     tdata.compl_cuids = []
     for cobj in cobjs:
         tdata.compl_cuids.append(ComponentUID(cobj))
         cobj.parent_block().reclassify_component_type(cobj, Block)
Esempio n. 4
0
 def _preprocess(self, tname, instance, **kwds):
     options = kwds.pop('options', {})
     sub = options.get('submodel', None)
     #
     # Iterate over the model collecting variable data,
     # until the submodel is found.
     #
     var = {}
     submodel = None
     for (name, data) in instance.component_map(active=True).items():
         if isinstance(data, Var):
             var[name] = data
         elif isinstance(data, SubModel):
             if sub is None or sub == name:
                 sub = name
                 submodel = data
                 break
     if submodel is None:
         raise RuntimeError("Missing submodel: " + str(sub))
     #
     instance._transformation_data[tname].submodel = [name]
     #
     # Fix variables
     #
     if submodel._fixed:
         fixed = [i.name for i in submodel._fixed]
         unfixed = []
         for v in var:
             if not v in fixed:
                 unfixed.append((v, getattr(submodel._parent(),
                                            v).is_indexed()))
     elif submodel._var:
         # NOTE: This argument is undocumented
         _var = set(submodel._var)
         unfixed = [(v, getattr(submodel._parent(), v).is_indexed())
                    for v in _var]
         fixed = []
         for v in var:
             if not v in _var:
                 fixed.append(v)
     else:
         #
         # By default, we assume that variables are fixed if they are not part of the
         # local model.
         #
         fixed = [v for v in var]
         unfixed = []
         for (name, data) in submodel.component_map(active=True).items():
             if isinstance(data, Var):
                 unfixed.append((data.cname(), data.is_indexed()))
     #
     self._submodel = sub
     self._upper_vars = var
     self._fixed_upper_vars = fixed
     self._unfixed_upper_vars = unfixed
     instance._transformation_data[tname].fixed = [
         ComponentUID(var[v]) for v in fixed
     ]
     return submodel
Esempio n. 5
0
 def _apply_to(self, instance, **kwds):
     options = kwds.pop('options', {})
     bound = kwds.pop('mpec_bound', 0.0)
     #
     # Create a mutable parameter that defines the value of the upper bound
     # on the constraints
     #
     bound = options.get('mpec_bound', bound)
     instance.mpec_bound = Param(mutable=True, initialize=bound)
     #
     # Setup transformation data
     #
     tdata = instance._transformation_data['mpec.simple_nonlinear']
     tdata.compl_cuids = []
     #
     # Iterate over the model finding Complementarity components
     #
     for complementarity in instance.component_objects(
             Complementarity,
             active=True,
             descend_into=(Block, Disjunct),
             sort=SortComponents.deterministic):
         block = complementarity.parent_block()
         for index in sorted(complementarity.keys()):
             _data = complementarity[index]
             if not _data.active:
                 continue
             _data.to_standard_form()
             #
             _type = getattr(_data.c, "_complementarity_type", 0)
             if _type == 1:
                 #
                 # Constraint expression is bounded below, so we can replace
                 # constraint c with a constraint that ensures that either
                 # constraint c is active or variable v is at its lower bound.
                 #
                 _data.ccon = Constraint(
                     expr=(_data.c.body - _data.c.lower) *
                     _data.v <= instance.mpec_bound)
                 del _data.c._complementarity_type
             elif _type == 3:
                 #
                 # Variable v is bounded above and below.  We can define
                 #
                 _data.ccon_l = Constraint(
                     expr=(_data.v - _data.v.bounds[0]) *
                     _data.c.body <= instance.mpec_bound)
                 _data.ccon_u = Constraint(
                     expr=(_data.v - _data.v.bounds[1]) *
                     _data.c.body <= instance.mpec_bound)
                 del _data.c._complementarity_type
             elif _type == 2:  #pragma:nocover
                 raise ValueError(
                     "to_standard_form does not generate _type 2 expressions"
                 )
         tdata.compl_cuids.append(ComponentUID(complementarity))
         block.reclassify_component_type(complementarity, Block)
Esempio n. 6
0
 def _preprocess(self, tname, instance, **kwds):
     options = kwds.pop('options', {})
     sub = options.get('submodel', None)
     #
     # Iterate over the model collecting variable data,
     # until the submodel is found.
     #
     var = {}
     submodel = None
     for (name, data) in instance.component_map(active=True).items():
         if isinstance(data, Var):
             var[name] = data
         elif isinstance(data, SubModel):
             if sub is None or sub == name:
                 sub = name
                 submodel = data
                 break
     if submodel is None:
         raise RuntimeError("Missing submodel: " + str(sub))
     #
     instance._transformation_data[tname].submodel = [name]
     #
     # Fix variables
     #
     if submodel._fixed:
         fixed = [i.name for i in submodel._fixed]
         unfixed = []
         for v in var:
             if not v in fixed:
                 unfixed.append((v, getattr(submodel._parent(),
                                            v).is_indexed()))
     elif submodel._var:
         _var = set(submodel._var)
         unfixed = [(v, getattr(submodel._parent(), v).is_indexed())
                    for v in _var]
         fixed = []
         for v in var:
             if not v in _var:
                 fixed.append(v)
     else:
         raise RuntimeError(
             "Must specify either 'fixed' or 'var' option for SubModel")
     #
     self._submodel = sub
     self._upper_vars = var
     self._fixed_upper_vars = fixed
     self._unfixed_upper_vars = unfixed
     instance._transformation_data[tname].fixed = [
         ComponentUID(var[v]) for v in fixed
     ]
     return submodel
Esempio n. 7
0
 def _apply_to(self, instance, **kwds):
     self._deterministic = kwds.get('deterministic', False)
     #
     # Process options
     #
     submodel = self._preprocess('bilevel.linear_mpec', instance, **kwds)
     instance.reclassify_component_type(submodel, Block)
     #
     # Create a block with optimality conditions
     #
     setattr(instance, self._submodel + '_kkt',
             self._add_optimality_conditions(instance, submodel))
     instance._transformation_data[
         'bilevel.linear_mpec'].submodel_cuid = ComponentUID(submodel)
     instance._transformation_data[
         'bilevel.linear_mpec'].block_cuid = ComponentUID(
             getattr(instance, self._submodel + '_kkt'))
     #-------------------------------------------------------------------------------
     #
     # Disable the original submodel and
     # execute the preprocessor
     #
     instance.reclassify_component_type(submodel, SubModel)
     submodel.deactivate()
Esempio n. 8
0
 def _apply_to(self, instance, **kwds):
     #
     # Find the free variables
     #
     free_vars = {}
     id_list = []
     # [ESJ 07/12/2019] Look on the whole model in case instance is a Block or a Disjunct
     for vdata in instance.model().component_data_objects(
             Var,
             active=True,
             sort=SortComponents.deterministic,
             descend_into=(Block, Disjunct)):
         id_list.append(id(vdata))
         free_vars[id(vdata)] = vdata
     #
     # Iterate over the Complementarity components
     #
     cobjs = []
     for cobj in instance.component_objects(
             Complementarity,
             active=True,
             descend_into=(Block, Disjunct),
             sort=SortComponents.deterministic):
         cobjs.append(cobj)
         for index in sorted(cobj.keys()):
             _cdata = cobj[index]
             if not _cdata.active:
                 continue
             #
             # Apply a variant of the standard form logic
             #
             self.to_common_form(_cdata, free_vars)
             #
     tdata = instance._transformation_data['mpec.nl']
     tdata.compl_cuids = []
     for cobj in cobjs:
         tdata.compl_cuids.append(ComponentUID(cobj))
         cobj.parent_block().reclassify_component_type(cobj, Block)
Esempio n. 9
0
    def _apply_to(self, instance, **kwds):
        #
        # Setup transformation data
        #
        tdata = instance._transformation_data['mpec.simple_disjunction']
        tdata.compl_cuids = []
        #
        # Iterate over the model finding Complementarity components
        #
        for complementarity in instance.component_objects(Complementarity, active=True,
                                                          descend_into=(Block, Disjunct),
                                                          sort=SortComponents.deterministic):
            block = complementarity.parent_block()

            for index in sorted(complementarity.keys()):
                _data = complementarity[index]
                if not _data.active:
                    continue
                #
                _e1 = _data._canonical_expression(_data._args[0])
                _e2 = _data._canonical_expression(_data._args[1])
                if len(_e1)==3 and len(_e2) == 3 and (_e1[0] is None) + (_e1[2] is None) + (_e2[0] is None) + (_e2[2] is None) != 2:
                    raise RuntimeError("Complementarity condition %s must have exactly two finite bounds" % _data.name)
                if len(_e1) == 3 and _e1[0] is None and _e1[2] is None:
                    #
                    # Swap _e1 and _e2.  The ensures that 
                    # only e2 will be an unconstrained expression
                    #
                    _e1, _e2 = _e2, _e1
                if _e2[0] is None and _e2[2] is None:
                    if len(_e1) == 2:
                        _data.c = Constraint(expr=_e1)
                    else:
                        _data.expr1 = Disjunct()
                        _data.expr1.c0 = Constraint(expr= _e1[0] == _e1[1])
                        _data.expr1.c1 = Constraint(expr= _e2[1] >= 0)
                        #
                        _data.expr2 = Disjunct()
                        _data.expr2.c0 = Constraint(expr= _e1[1] == _e1[2])
                        _data.expr2.c1 = Constraint(expr= _e2[1] <= 0)
                        #
                        _data.expr3 = Disjunct()
                        _data.expr3.c0 = Constraint(expr= inequality(_e1[0], _e1[1], _e1[2]))
                        _data.expr3.c1 = Constraint(expr= _e2[1] == 0)
                        _data.complements = Disjunction(expr=(_data.expr1, _data.expr2, _data.expr3))
                else:
                    if _e1[0] is None:
                        tmp1 = _e1[2] - _e1[1]
                    else:
                        tmp1 = _e1[1] - _e1[0]
                    if _e2[0] is None:
                        tmp2 = _e2[2] - _e2[1]
                    else:
                        tmp2 = _e2[1] - _e2[0]
                    _data.expr1 = Disjunct()
                    _data.expr1.c0 = Constraint(expr= tmp1 >= 0)
                    _data.expr1.c1 = Constraint(expr= tmp2 == 0)
                    #
                    _data.expr2 = Disjunct()
                    _data.expr2.c0 = Constraint(expr= tmp1 == 0)
                    _data.expr2.c1 = Constraint(expr= tmp2 >= 0)
                    #
                    _data.complements = Disjunction(expr=(_data.expr1, _data.expr2))
            tdata.compl_cuids.append( ComponentUID(complementarity) )
            block.reclassify_component_type(complementarity, Block)
Esempio n. 10
0
    def __init__(self, reference_model):

        self.reference_model = None
        self.objective = None
        self.time_stages = None

        self.stage_to_variables_map = {}
        self.variable_to_stage_map = {}

        # the set of stochastic data objects
        # (possibly mapped to some distribution)
        self.stochastic_data = None

        # maps between variables and objectives
        self.variable_to_objectives_map = ComponentMap()
        self.objective_to_variables_map = ComponentMap()

        # maps between variables and constraints
        self.variable_to_constraints_map = ComponentMap()
        self.constraint_to_variables_map = ComponentMap()

        # maps between stochastic data and objectives
        self.stochastic_data_to_objectives_map = ComponentMap()
        self.objective_to_stochastic_data_map = ComponentMap()

        # maps between stochastic data and constraints
        self.stochastic_data_to_constraints_map = ComponentMap()
        self.constraint_to_stochastic_data_map = ComponentMap()

        # maps between stochastic data and variable lower and upper bounds
        self.stochastic_data_to_variables_lb_map = ComponentMap()
        self.variable_to_stochastic_data_lb_map = ComponentMap()

        self.stochastic_data_to_variables_ub_map = ComponentMap()
        self.variable_to_stochastic_data_ub_map = ComponentMap()

        self.variable_symbols = ComponentMap()

        if not isinstance(reference_model, Block):
            raise TypeError("reference model input must be a Pyomo model")
        self.reference_model = reference_model

        #
        # Extract stochastic parameters from the
        # StochasticDataAnnotation object
        #
        self.stochastic_data = \
            _extract_stochastic_data(self.reference_model)

        #
        # Get the variable stages from the
        # VariableStageAnnotation object
        #
        (self.stage_to_variables_map,
         self.variable_to_stage_map,
         self._variable_stage_assignments) = \
            _map_variable_stages(self.reference_model)
        self.time_stages = tuple(sorted(self.stage_to_variables_map))
        assert self.time_stages[0] == 1
        self.variable_symbols = ComponentUID.generate_cuid_string_map(
            self.reference_model, ctype=Var,
            repr_version=tree_structure.CUID_repr_version)
        # remove the parent blocks from this map
        keys_to_delete = []
        for var in self.variable_symbols:
            if var.parent_component().ctype is not Var:
                keys_to_delete.append(var)
        for key in keys_to_delete:
            del self.variable_symbols[key]

        #
        # Get the stage cost components from the StageCostAnnotation
        # and generate a dummy single-scenario scenario tree
        #
        stage_cost_annotation = locate_annotations(
            self.reference_model,
            StageCostAnnotation,
            max_allowed=1)
        if len(stage_cost_annotation) == 0:
            raise ValueError(
                "Reference model is missing stage cost "
                "annotation: %s" % (StageCostAnnotation.__name__))
        else:
            assert len(stage_cost_annotation) == 1
            stage_cost_annotation = stage_cost_annotation[0][1]
        stage_cost_assignments = ComponentMap(
            stage_cost_annotation.expand_entries())

        stage1_cost = None
        stage2_cost = None
        for cdata, stagenum in stage_cost_assignments.items():
            if stagenum == 1:
                stage1_cost = cdata
            elif stagenum == 2:
                stage2_cost = cdata
        if stage1_cost is None:
            raise ValueError("Missing stage cost annotation "
                             "for time stage: 1")
        if stage2_cost is None:
            raise ValueError("Missing stage cost annotation "
                             "for time stage: 2")
        assert stage1_cost is not stage2_cost
        self._stage1_cost = stage1_cost
        self._stage2_cost = stage2_cost

        #
        # Extract the locations of variables and stochastic data
        # within the model
        #
        sto_obj = StochasticObjectiveAnnotation()
        for objcntr, obj in enumerate(
                  self.reference_model.component_data_objects(
                Objective,
                active=True,
                descend_into=True), 1):

            if objcntr > 1:
                raise ValueError(
                    "Reference model can not contain more than one "
                    "active objective")

            self.objective = obj
            self.objective_sense = obj.sense

            obj_params = tuple(
                self._collect_mutable_parameters(obj.expr).values())
            self.objective_to_stochastic_data_map[obj] = []
            for paramdata in obj_params:
                if paramdata in self.stochastic_data:
                    self.stochastic_data_to_objectives_map.\
                        setdefault(paramdata, []).append(obj)
                    self.objective_to_stochastic_data_map[obj].\
                        append(paramdata)
            if len(self.objective_to_stochastic_data_map[obj]) == 0:
                del self.objective_to_stochastic_data_map[obj]
            else:
                # TODO: Can we make this declaration sparse
                #       by identifying which variables have
                #       stochastic coefficients? How to handle
                #       non-linear expressions?
                sto_obj.declare(obj)

            obj_variables = tuple(
                self._collect_variables(obj.expr).values())
            self.objective_to_variables_map[obj] = []
            for var in obj_variables:
                self.variable_to_objectives_map.\
                    setdefault(var, []).append(obj)
                self.objective_to_variables_map[obj].append(var)
            if len(self.objective_to_variables_map[obj]) == 0:
                del self.objective_to_variables_map[obj]

        sto_conbounds = StochasticConstraintBoundsAnnotation()
        sto_conbody = StochasticConstraintBodyAnnotation()
        for con in self.reference_model.component_data_objects(
                Constraint,
                active=True,
                descend_into=True):

            lower_params = tuple(
                self._collect_mutable_parameters(con.lower).values())
            body_params = tuple(
                self._collect_mutable_parameters(con.body).values())
            upper_params = tuple(
                self._collect_mutable_parameters(con.upper).values())

            # TODO: Can we make this declaration sparse
            #       by idenfifying which variables have
            #       stochastic coefficients? How to handle
            #       non-linear expressions? Currently, this
            #       code also fails to detect that mutable
            #       "constant" expressions might fall out
            #       of the body and into the bounds.
            if len(body_params):
                sto_conbody.declare(con)
            if len(body_params) or \
               len(lower_params) or \
               len(upper_params):
                sto_conbounds.declare(con,
                                      lb=bool(len(lower_params) or len(body_params)),
                                      ub=bool(len(upper_params) or len(body_params)))

            all_stochastic_params = {}
            for param in itertools.chain(lower_params,
                                         body_params,
                                         upper_params):
                if param in self.stochastic_data:
                    all_stochastic_params[id(param)] = param

            if len(all_stochastic_params) > 0:
                self.constraint_to_stochastic_data_map[con] = []
                # no params will appear twice in this iteration
                for param in all_stochastic_params.values():
                    self.stochastic_data_to_constraints_map.\
                        setdefault(param, []).append(con)
                    self.constraint_to_stochastic_data_map[con].\
                        append(param)

            body_variables = tuple(
                self._collect_variables(con.body).values())
            self.constraint_to_variables_map[con] = []
            for var in body_variables:
                self.variable_to_constraints_map.\
                    setdefault(var, []).append(con)
                self.constraint_to_variables_map[con].append(var)

        # For now, it is okay to have SOSConstraints in the
        # representation of a problem, but the SOS
        # constraints can't have custom weights that
        # represent stochastic data
        for soscon in self.reference_model.component_data_objects(
                SOSConstraint,
                active=True,
                descend_into=True):
            for var, weight in soscon.get_items():
                weight_params = tuple(
                    self._collect_mutable_parameters(weight).values())
                if param in self.stochastic_data:
                    raise ValueError(
                        "SOSConstraints with stochastic data are currently"
                        " not supported in embedded stochastic programs. "
                        "The SOSConstraint component '%s' has a weight "
                        "term for variable '%s' that references stochastic"
                        " parameter '%s'"
                        % (soscon.name,
                           var.name,
                           param.name))
                self.variable_to_constraints_map.\
                    setdefault(var, []).append(soscon)
                self.constraint_to_variables_map.\
                    setdefault(soscon, []).append(var)

        sto_varbounds = StochasticVariableBoundsAnnotation()
        for var in self.reference_model.component_data_objects(
                Var,
                descend_into=True):

            lower_params = tuple(
                self._collect_mutable_parameters(var.lb).values())
            upper_params = tuple(
                self._collect_mutable_parameters(var.ub).values())

            if (len(lower_params) > 0) or \
               (len(upper_params) > 0):
                sto_varbounds.declare(var,
                                      lb=bool(len(lower_params) > 0),
                                      ub=bool(len(upper_params) > 0))

            self.variable_to_stochastic_data_lb_map[var] = []
            for param in lower_params:
                if param in self.stochastic_data:
                    self.stochastic_data_to_variables_lb_map.\
                        setdefault(param, []).append(var)
                    self.variable_to_stochastic_data_lb_map[var].\
                        append(param)
            if len(self.variable_to_stochastic_data_lb_map[var]) == 0:
                del self.variable_to_stochastic_data_lb_map[var]

            self.variable_to_stochastic_data_ub_map[var] = []
            for param in upper_params:
                if param in self.stochastic_data:
                    self.stochastic_data_to_variables_ub_map.\
                        setdefault(param, []).append(var)
                    self.variable_to_stochastic_data_ub_map[var].\
                        append(param)
            if len(self.variable_to_stochastic_data_ub_map[var]) == 0:
                del self.variable_to_stochastic_data_ub_map[var]

        #
        # Generate the explicit annotations
        #

        # first make sure these annotations do not already exist
        if len(locate_annotations(self.reference_model,
                                  StochasticConstraintBoundsAnnotation)) > 0:
            raise ValueError("Reference model can not contain "
                             "a StochasticConstraintBoundsAnnotation declaration.")
        if len(locate_annotations(self.reference_model,
                                  StochasticConstraintBodyAnnotation)) > 0:
            raise ValueError("Reference model can not contain "
                             "a StochasticConstraintBodyAnnotation declaration.")
        if len(locate_annotations(self.reference_model,
                                  StochasticObjectiveAnnotation)) > 0:
            raise ValueError("Reference model can not contain "
                             "a StochasticObjectiveAnnotation declaration.")

        # now add any necessary annotations
        if sto_obj.has_declarations:
            assert not hasattr(self.reference_model,
                               ".pyspembeddedsp_stochastic_objective_annotation")
            setattr(self.reference_model,
                    ".pyspembeddedsp_stochastic_objective_annotation",
                    sto_obj)
        if sto_conbody.has_declarations:
            assert not hasattr(self.reference_model,
                               ".pyspembeddedsp_stochastic_constraint_body_annotation")
            setattr(self.reference_model,
                    ".pyspembeddedsp_stochastic_constraint_body_annotation",
                    sto_conbody)
        if sto_conbounds.has_declarations:
            assert not hasattr(self.reference_model,
                               ".pyspembeddedsp_stochastic_constraint_bounds_annotation")
            setattr(self.reference_model,
                    ".pyspembeddedsp_stochastic_constraint_bounds_annotation",
                    sto_conbounds)
        if sto_varbounds.has_declarations:
            assert not hasattr(self.reference_model,
                               ".pyspembeddedsp_stochastic_variable_bounds_annotation")
            setattr(self.reference_model,
                    ".pyspembeddedsp_stochastic_variable_bounds_annotation",
                    sto_varbounds)