Ejemplo n.º 1
0
    def _setup_subproblems(self, instance, bigM):
        # create transformation block
        transBlockName, transBlock = self._add_relaxation_block(
            instance, '_pyomo_gdp_cuttingplane_relaxation')

        # We store a list of all vars so that we can efficiently
        # generate maps among the subproblems
        transBlock.all_vars = list(v for v in instance.component_data_objects(
            Var,
            descend_into=(Block, Disjunct),
            sort=SortComponents.deterministic) if not v.is_fixed())

        # we'll store all the cuts we add together
        transBlock.cuts = Constraint(Any)

        # get bigM and chull relaxations
        bigMRelaxation = TransformationFactory('gdp.bigm')
        chullRelaxation = TransformationFactory('gdp.chull')
        relaxIntegrality = TransformationFactory('core.relax_integrality')

        # HACK: for the current writers, we need to also apply gdp.reclassify so
        # that the indicator variables stay where they are in the big M model
        # (since that is what we are eventually going to solve after we add our
        # cuts).
        reclassify = TransformationFactory('gdp.reclassify')

        #
        # Generalte the CHull relaxation (used for the separation
        # problem to generate cutting planes
        #
        instance_rCHull = chullRelaxation.create_using(instance)
        # This relies on relaxIntegrality relaxing variables on deactivated
        # blocks, which should be fine.
        reclassify.apply_to(instance_rCHull)
        relaxIntegrality.apply_to(instance_rCHull)

        #
        # Reformulate the instance using the BigM relaxation (this will
        # be the final instance returned to the user)
        #
        bigMRelaxation.apply_to(instance, bigM=bigM)
        reclassify.apply_to(instance)

        #
        # Generate the continuous relaxation of the BigM transformation
        #
        instance_rBigM = relaxIntegrality.create_using(instance)

        #
        # Add the xstar parameter for the CHull problem
        #
        transBlock_rCHull = instance_rCHull.component(transBlockName)
        #
        # this will hold the solution to rbigm each time we solve it. We
        # add it to the transformation block so that we don't have to
        # worry about name conflicts.
        transBlock_rCHull.xstar = Param(range(len(transBlock.all_vars)),
                                        mutable=True,
                                        default=None)

        transBlock_rBigM = instance_rBigM.component(transBlockName)

        #
        # Generate the mapping between the variables on all the
        # instances and the xstar parameter
        #
        var_info = tuple(
            (v, transBlock_rBigM.all_vars[i], transBlock_rCHull.all_vars[i],
             transBlock_rCHull.xstar[i])
            for i, v in enumerate(transBlock.all_vars))

        #
        # Add the separation objective to the chull subproblem
        #
        self._add_separation_objective(var_info, transBlock_rCHull)

        return instance_rBigM, instance_rCHull, var_info, transBlockName
Ejemplo n.º 2
0
    def _apply_to(self, model, **kwds):
        submodel_name = kwds.pop('submodel', None)
        use_dual_objective = kwds.pop('use_dual_objective', False)
        #
        # Process options
        #
        self._preprocess('pao.bilevel.linear_dual', model)
        self._fix_all()

        for key, sub in self.submodel.items():
            #
            # Generate the dual block
            #
            transform = TransformationFactory('pao.duality.linear_dual')
            dual = transform.create_using(sub, fixed=self._fixed_vardata)
            #
            # Figure out which objective is being used
            #
            if use_dual_objective:
                #
                # Deactivate the upper-level objective
                #
                # TODO: Warn if there are multiple objectives?
                #
                for odata in sub._parent().component_map(Objective, active=True).values():
                    odata.deactivate()
            else:
                #
                # Add a constraint that maps the dual objective to the primal objective
                #
                # NOTE: It might be numerically more stable to replace the upper
                # objective with a variable, and then set the dual equal to that variable.
                # But that transformation would not be limited to the submodel.  If that's
                # an issue for a user, they can make that change, and see the benefit.
                #
                dual_obj = None
                for odata in dual.component_objects(Objective, active=True):
                    dual_obj = odata
                    dual_obj.deactivate()
                    break
                primal_obj = None
                for odata in sub.component_objects(Objective, active=True):
                    primal_obj = odata
                    break
                dual.equiv_objs = Constraint(expr=dual_obj.expr == primal_obj.expr)
            #
            # Add the dual block
            #
            setattr(model, key +'_dual', dual)
            model.reclassify_component_type(key +'_dual', Block)
            #
            # Unfix the upper variables
            #
            self._unfix_all()
            #
            # Disable the original submodel
            #
            # Q: Are the last steps redundant?  Will we recurse into deactivated blocks?
            #
            sub.deactivate()
            for data in sub.component_map(active=True).values():
                if not isinstance(data, Var) and not isinstance(data, Set):
                    data.deactivate()
Ejemplo n.º 3
0
    def _apply_to(self, model, **kwds):
        submodel_name = kwds.pop('submodel', None)
        use_dual_objective = kwds.pop('use_dual_objective', False)
        subproblem_objective_weights = kwds.pop('subproblem_objective_weights', None)
        #
        # Process options
        #
        self._preprocess('pao.pyomo.linear_dual', model)
        self._fix_all()

        _dual_obj = 0.
        _dual_sense = None
        _primal_obj = 0.
        for key, sub in self.submodel.items():
            _parent = sub.parent_block()
            #
            # Generate the dual block
            #
            transform = TransformationFactory('pao.duality.linear_dual')
            dual = transform.create_using(sub, fixed=self._fixed_vardata[sub.name])
            #
            # Figure out which objective is being used
            #
            for odata in dual.component_objects(Objective, active=True):
                if subproblem_objective_weights:
                    _dual_obj += subproblem_objective_weights[key] * odata.expr
                else:
                    _dual_obj += odata.expr
                _dual_sense = odata.sense  # TODO: currently assumes all subproblems have same sense
                odata.deactivate()

            if use_dual_objective:
                #
                # Deactivate the upper-level objective, and
                # defaults to use the aggregate objective of the SubModels.
                #
                for odata in model.component_objects(Objective, active=True):
                    odata.deactivate()
            else:
                #
                # Add a constraint that maps the dual objective to the primal objective
                #
                # NOTE: It might be numerically more stable to replace the upper
                # objective with a variable, and then set the dual equal to that variable.
                # But that transformation would not be limited to the submodel.  If that's
                # an issue for a user, they can make that change, and see the benefit.
                #
                for odata in sub.component_objects(Objective, active=True):
                    if subproblem_objective_weights:
                        _primal_obj += subproblem_objective_weights[key]*odata.expr
                    else:
                        _primal_obj += odata.expr

            #
            # Add the dual block
            #

            # first check if the submodel exists on a _BlockData,
            # otherwise add the dual block to the model directly
            _dual_name = key +'_dual'
            if type(_parent) == _BlockData:
                _dual_name = _dual_name.replace(_parent.name+".","")
                _parent.add_component(_dual_name, dual)
                _parent.reclassify_component_type(_dual_name, Block)
            else:
                model.add_component(_dual_name, dual)
                model.reclassify_component_type(_dual_name, Block)

            #
            # Unfix the upper variables
            #
            self._unfix_all()
            #
            # Disable the original submodel
            #
            sub.deactivate()
            for data in sub.component_map(active=True).values():
                if not isinstance(data, Var) and not isinstance(data, Set):
                    data.deactivate()

        # TODO: with multiple sub-problems, put the _obj or equiv_objs on a separate block
        if use_dual_objective:
            dual._obj = Objective(expr=_dual_obj, sense=_dual_sense)
        else:
            dual.equiv_objs = Constraint(expr=_dual_obj == _primal_obj)
Ejemplo n.º 4
0
    def _setup_subproblems(self, instance, bigM, tighten_relaxation_callback):
        # create transformation block
        transBlockName, transBlock = self._add_transformation_block(instance)

        # We store a list of all vars so that we can efficiently
        # generate maps among the subproblems

        transBlock.all_vars = list(v for v in instance.component_data_objects(
            Var,
            descend_into=(Block, Disjunct),
            sort=SortComponents.deterministic) if not v.is_fixed())

        # we'll store all the cuts we add together
        nm = self._config.cuts_name
        if nm is None:
            cuts_obj = transBlock.cuts = Constraint(NonNegativeIntegers)
        else:
            # check that this really is an available name
            if instance.component(nm) is not None:
                raise GDP_Error("cuts_name was specified as '%s', but this is "
                                "already a component on the instance! Please "
                                "specify a unique name." % nm)
            instance.add_component(nm, Constraint(NonNegativeIntegers))
            cuts_obj = instance.component(nm)

        # get bigM and hull relaxations
        bigMRelaxation = TransformationFactory('gdp.bigm')
        hullRelaxation = TransformationFactory('gdp.hull')
        relaxIntegrality = TransformationFactory('core.relax_integer_vars')

        #
        # Generate the Hull relaxation (used for the separation
        # problem to generate cutting planes)
        #
        tighter_instance = tighten_relaxation_callback(instance)
        instance_rHull = hullRelaxation.create_using(tighter_instance)
        relaxIntegrality.apply_to(instance_rHull,
                                  transform_deactivated_blocks=True)

        #
        # Reformulate the instance using the BigM relaxation (this will
        # be the final instance returned to the user)
        #
        bigMRelaxation.apply_to(instance, bigM=bigM)

        #
        # Generate the continuous relaxation of the BigM transformation. We'll
        # restore it at the end.
        #
        relaxIntegrality.apply_to(instance, transform_deactivated_blocks=True)

        #
        # Add the xstar parameter for the Hull problem
        #
        transBlock_rHull = instance_rHull.component(transBlockName)
        #
        # this will hold the solution to rbigm each time we solve it. We
        # add it to the transformation block so that we don't have to
        # worry about name conflicts.
        transBlock_rHull.xstar = Param( range(len(transBlock.all_vars)),
                                        mutable=True, default=0, within=Reals)

        # we will add a block that we will deactivate to use to store the
        # extended space cuts. We never need to solve these, but we need them to
        # be constructed for the sake of Fourier-Motzkin Elimination
        extendedSpaceCuts = transBlock_rHull.extendedSpaceCuts = Block()
        extendedSpaceCuts.deactivate()
        extendedSpaceCuts.cuts = Constraint(Any)

        #
        # Generate the mapping between the variables on all the
        # instances and the xstar parameter.
        #
        var_info = [
            (v, # this is the bigM variable
             transBlock_rHull.all_vars[i],
             transBlock_rHull.xstar[i])
            for i,v in enumerate(transBlock.all_vars)]

        # NOTE: we wait to add the separation objective to the rHull problem
        # because it is best to do it in the first iteration, so that we can
        # skip stale variables.

        return (instance, cuts_obj, instance_rHull, var_info, transBlockName)
Ejemplo n.º 5
0
    def _setup_subproblems(self, instance, bigM):
        # create transformation block
        transBlockName, transBlock = self._add_relaxation_block(
            instance,
            '_pyomo_gdp_cuttingplane_relaxation')

        # We store a list of all vars so that we can efficiently
        # generate maps among the subproblems
        transBlock.all_vars = list(v for v in instance.component_data_objects(
            Var,
            descend_into=(Block, Disjunct),
            sort=SortComponents.deterministic) if not v.is_fixed())

        # we'll store all the cuts we add together
        transBlock.cuts = Constraint(Any)

        # get bigM and chull relaxations
        bigMRelaxation = TransformationFactory('gdp.bigm')
        chullRelaxation = TransformationFactory('gdp.chull')
        relaxIntegrality = TransformationFactory('core.relax_integrality')

        # HACK: for the current writers, we need to also apply gdp.reclassify so
        # that the indicator variables stay where they are in the big M model
        # (since that is what we are eventually going to solve after we add our
        # cuts).
        reclassify = TransformationFactory('gdp.reclassify')

        #
        # Generalte the CHull relaxation (used for the separation
        # problem to generate cutting planes
        #
        instance_rCHull = chullRelaxation.create_using(instance)
        # This relies on relaxIntegrality relaxing variables on deactivated
        # blocks, which should be fine.
        reclassify.apply_to(instance_rCHull)
        relaxIntegrality.apply_to(instance_rCHull)

        #
        # Reformulate the instance using the BigM relaxation (this will
        # be the final instance returned to the user)
        #
        bigMRelaxation.apply_to(instance, bigM=bigM)
        reclassify.apply_to(instance)

        #
        # Generate the continuous relaxation of the BigM transformation
        #
        instance_rBigM = relaxIntegrality.create_using(instance)

        #
        # Add the xstar parameter for the CHull problem
        #
        transBlock_rCHull = instance_rCHull.component(transBlockName)
        #
        # this will hold the solution to rbigm each time we solve it. We
        # add it to the transformation block so that we don't have to
        # worry about name conflicts.
        transBlock_rCHull.xstar = Param(
            range(len(transBlock.all_vars)), mutable=True, default=None)

        transBlock_rBigM = instance_rBigM.component(transBlockName)

        #
        # Generate the mapping between the variables on all the
        # instances and the xstar parameter
        #
        var_info = tuple(
            (v,
             transBlock_rBigM.all_vars[i],
             transBlock_rCHull.all_vars[i],
             transBlock_rCHull.xstar[i])
            for i,v in enumerate(transBlock.all_vars))

        #
        # Add the separation objective to the chull subproblem
        #
        self._add_separation_objective(var_info, transBlock_rCHull)

        return instance_rBigM, instance_rCHull, var_info, transBlockName