示例#1
0
    def create_subproblem(self, scenario_name):
        """ the subproblem creation function passed into the
            BendersCutsGenerator 
        """
        instance = self.local_scenarios[scenario_name]

        nonant_list, nonant_ids = _get_nonant_ids(instance)

        # NOTE: since we use generate_standard_repn below, we need
        #       to unfix any nonants so they'll properly appear
        #       in the objective
        fixed_nonants = [var for var in nonant_list if var.fixed]
        for var in fixed_nonants:
            var.fixed = False

        # pulls the scenario objective expression, removes the first stage variables, and sets the new objective
        obj = find_active_objective(instance)

        if not hasattr(instance, "PySP_prob"):
            instance.PySP_prob = 1. / self.scenario_count
        PySP_prob = instance.PySP_prob

        repn = generate_standard_repn(obj.expr, quadratic=True)
        if len(repn.nonlinear_vars) > 0:
            raise ValueError(
                "LShaped does not support models with nonlinear objective functions"
            )

        linear_vars = list()
        linear_coefs = list()
        quadratic_vars = list()
        quadratic_coefs = list()
        ## we'll assume the constant is part of stage 1 (wlog it is), just
        ## like the first-stage bits of the objective
        constant = repn.constant

        ## only keep the second stage variables in the objective
        for coef, var in zip(repn.linear_coefs, repn.linear_vars):
            id_var = id(var)
            if id_var not in nonant_ids:
                linear_vars.append(var)
                linear_coefs.append(PySP_prob * coef)
        for coef, (x, y) in zip(repn.quadratic_coefs, repn.quadratic_vars):
            id_x = id(x)
            id_y = id(y)
            if id_x not in nonant_ids or id_y not in nonant_ids:
                quadratic_coefs.append(PySP_prob * coef)
                quadratic_vars.append((x, y))

        # checks if model sense is max, if so negates the objective
        if not self.is_minimizing:
            for i, coef in enumerate(linear_coefs):
                linear_coefs[i] = -coef
            for i, coef in enumerate(quadratic_coefs):
                quadratic_coefs[i] = -coef

        expr = LinearExpression(constant=constant,
                                linear_coefs=linear_coefs,
                                linear_vars=linear_vars)
        if quadratic_coefs:
            expr += pyo.quicksum(
                (coef * x * y
                 for coef, (x, y) in zip(quadratic_coefs, quadratic_vars)))

        instance.del_component(obj)

        # set subproblem objective function
        instance.obj = pyo.Objective(expr=expr, sense=pyo.minimize)

        ## need to do this here for validity if computing the eta bound
        if self.relax_master:
            # relaxes any integrality constraints for the subproblem
            RelaxIntegerVars().apply_to(instance)

        if self.compute_eta_bound:
            for var in fixed_nonants:
                var.fixed = True
            opt = pyo.SolverFactory(self.options["sp_solver"])
            if self.options["sp_solver_options"]:
                for k, v in self.options["sp_solver_options"].items():
                    opt.options[k] = v

            if sputils.is_persistent(opt):
                opt.set_instance(instance)
                res = opt.solve(tee=False)
            else:
                res = opt.solve(instance, tee=False)

            eta_lb = res.Problem[0].Lower_bound

            self.valid_eta_lb[scenario_name] = eta_lb

        # if not done above
        if not self.relax_master:
            # relaxes any integrality constraints for the subproblem
            RelaxIntegerVars().apply_to(instance)

        # iterates through constraints and removes first stage constraints from the model
        # the id dict is used to improve the speed of identifying the stage each variables belongs to
        for constr_data in list(
                itertools.chain(
                    instance.component_data_objects(SOSConstraint,
                                                    active=True,
                                                    descend_into=True),
                    instance.component_data_objects(Constraint,
                                                    active=True,
                                                    descend_into=True))):
            if _first_stage_only(constr_data, nonant_ids):
                _del_con(constr_data)

        # creates the sub map to remove first stage variables from objective expression
        complicating_vars_map = pyo.ComponentMap()
        subproblem_to_master_vars_map = pyo.ComponentMap()

        # creates the complicating var map that connects the first stage variables in the sub problem to those in
        # the master problem -- also set the bounds on the subproblem master vars to be none for better cuts
        for var, mvar in zip(nonant_list, self.master_vars):
            if var.name not in mvar.name:  # mvar.name may be part of a bundle
                raise Exception(
                    "Error: Complicating variable mismatch, sub-problem variables changed order"
                )
            complicating_vars_map[mvar] = var
            subproblem_to_master_vars_map[var] = mvar

            # these are already enforced in the master
            # don't need to be enfored in the subproblems
            var.setlb(None)
            var.setub(None)
            var.fixed = False

        # this is for interefacing with PH code
        instance._subproblem_to_master_vars_map = subproblem_to_master_vars_map

        if self.store_subproblems:
            self.subproblems[scenario_name] = instance

        return instance, complicating_vars_map
示例#2
0
    def _create_master_no_scenarios(self):

        # using the first scenario as a basis
        master = self.scenario_creator(self.all_scenario_names[0],
                                       node_names=None,
                                       cb_data=self.cb_data)

        if self.relax_master:
            RelaxIntegerVars().apply_to(master)

        nonant_list, nonant_ids = _get_nonant_ids(master)

        self.master_vars = nonant_list

        for constr_data in list(
                itertools.chain(
                    master.component_data_objects(SOSConstraint,
                                                  active=True,
                                                  descend_into=True),
                    master.component_data_objects(Constraint,
                                                  active=True,
                                                  descend_into=True))):
            if not _first_stage_only(constr_data, nonant_ids):
                _del_con(constr_data)

        # delete the second stage variables
        for var in list(
                master.component_data_objects(Var,
                                              active=True,
                                              descend_into=True)):
            if id(var) not in nonant_ids:
                _del_var(var)

        self._add_master_etas(master, self.all_scenario_names)

        # pulls the current objective expression, adds in the eta variables,
        # and removes the second stage variables from the expression
        obj = find_active_objective(master)

        repn = generate_standard_repn(obj.expr, quadratic=True)
        if len(repn.nonlinear_vars) > 0:
            raise ValueError(
                "LShaped does not support models with nonlinear objective functions"
            )

        linear_vars = list()
        linear_coefs = list()
        quadratic_vars = list()
        quadratic_coefs = list()
        ## we'll assume the constant is part of stage 1 (wlog it is), just
        ## like the first-stage bits of the objective
        constant = repn.constant

        ## only keep the first stage variables in the objective
        for coef, var in zip(repn.linear_coefs, repn.linear_vars):
            id_var = id(var)
            if id_var in nonant_ids:
                linear_vars.append(var)
                linear_coefs.append(coef)
        for coef, (x, y) in zip(repn.quadratic_coefs, repn.quadratic_vars):
            id_x = id(x)
            id_y = id(y)
            if id_x in nonant_ids and id_y in nonant_ids:
                quadratic_coefs.append(coef)
                quadratic_vars.append((x, y))

        # checks if model sense is max, if so negates the objective
        if not self.is_minimizing:
            for i, coef in enumerate(linear_coefs):
                linear_coefs[i] = -coef
            for i, coef in enumerate(quadratic_coefs):
                quadratic_coefs[i] = -coef

        # add the etas
        for var in master.eta.values():
            linear_vars.append(var)
            linear_coefs.append(1)

        expr = LinearExpression(constant=constant,
                                linear_coefs=linear_coefs,
                                linear_vars=linear_vars)
        if quadratic_coefs:
            expr += pyo.quicksum(
                (coef * x * y
                 for coef, (x, y) in zip(quadratic_coefs, quadratic_vars)))

        master.del_component(obj)

        # set master objective function
        master.obj = pyo.Objective(expr=expr, sense=pyo.minimize)

        self.master = master