Exemple #1
0
class RobustConstraintData(_BlockData):
    """ RobustConstraint contains:
            - attribute _cons: constraint
            - _uncset: uncertainty set
            - _uncparam: uncertain parameter
            - _vars: variables the constraint contains
    """
    def __init__(self, component, cons=None):
        super().__init__(component)
        self._cons = None
        self._uncparam = None
        self._uncset = None
        self._vars = []
        self._sep = None

    def build(self, lower, expr, upper):
        # Collect uncertain parameter and uncertainty set
        self.lower = lower
        self.upper = upper
        self._uncparam = _collect_uncparam(expr)
        self._uncset = [self._uncparam[0]._uncset]
        self._rule = self.construct_rule(expr)
        self._bounds = (lower, upper)
        # Generate nominal constraint
        nominal_expr = self.nominal_constraint_expr()
        self._constraints = ConstraintList()
        self._constraints.add(nominal_expr)

    def add_cut(self):
        """ Solve separation problem and add cut. """
        sep = self.construct_separation_problem()
        # TODO: pass option
        opt = SolverFactory('gurobi')
        opt.options['NonConvex'] = 2
        res = opt.solve(sep)
        # Check results are okay

        # Check feasibility?

        # Generate cut expression:
        # uncparam = self._uncparam[0]
        uncparam = sep.uncparam
        expr = self._rule({i: uncparam[i].value for i in uncparam})

        self._constraints.add((self.lower, expr, self.upper))
        self.feasible = value(sep.obj <= self.upper)

        return self.feasible

    def construct_separation_problem(self):
        m = ConcreteModel()
        uncparam = self._uncparam[0]
        index = uncparam.index_set()
        # Create inner problem variables
        # TODO: use setattr to give uncparam same name as in model
        m.uncparam = Var(index)
        # collect current coefficient values
        expr = self._rule(m.uncparam, compute_values=True)
        # construct objective with current coefficient values
        m.obj = Objective(expr=expr, sense=maximize)

        # construct constraints from uncertainty set
        uncset = self._uncset[0]
        m.cons = ConstraintList()
        substitution_map = {id(uncparam[i]): m.uncparam[i] for i in index}
        for c in uncset.component_data_objects(Constraint):
            m.cons.add(
                (c.lower, replace_expressions(c.body,
                                              substitution_map), c.upper))
        return m
        # What should we do with the constraints? Replace UncParams by the
        # Vars? Or restructure UncParam so that we can solve directly.k

    # !!!
    def nominal_constraint_expr(self):
        """ Generate the nominal constraint. """
        # TODO: replace p.value by p.nominal
        uncparam = self._uncparam[0]
        expr = self._rule({i: uncparam[i].nominal for i in uncparam})
        return (self._bounds[0], expr, self._bounds[1])

    # !!!
    def is_feasible(self):
        return False

    def construct_rule(self, expr):
        repn = generate_linear_repn(expr)
        linear_vars = repn.linear_vars
        linear_coefs = repn.linear_coefs
        constant = repn.constant
        id_coef_dict = {id(v): c for v, c in zip(linear_vars, linear_coefs)}
        param = self._uncparam[0]
        index_coef_dict = {i: id_coef_dict.get(id(param[i]), 0) for i in param}

        def rule(x, compute_values=False):
            if compute_values:
                return (quicksum(value(index_coef_dict[i]) * x[i]
                                 for i in x) + value(constant))
            else:
                return quicksum(index_coef_dict[i] * x[i]
                                for i in x) + constant

        return rule
Exemple #2
0
class RobustConstraintData(_BlockData):
    """ RobustConstraint contains:
            - attribute _cons: constraint
            - _uncset: uncertainty set
            - _uncparam: uncertain parameter
            - _vars: variables the constraint contains
    """
    def __init__(self, component, cons=None):
        super().__init__(component)
        self._cons = None
        self._uncparam = None
        self._uncset = None
        self._vars = []
        self._sep = None

    def build(self, lower, expr, upper):
        # Collect uncertain parameter and uncertainty set
        self.lower = lower
        self.upper = upper
        self._uncparam = _collect_uncparam(expr)
        self._uncset = [self._uncparam[0]._uncset]
        self._rule = self.construct_rule(expr)
        self._bounds = (lower, upper)
        # Generate nominal constraint
        nominal_expr = self.nominal_constraint_expr()
        self._constraints = ConstraintList()
        self._constraints.add(nominal_expr)

    def has_lb(self):
        if self.lower is None or self.lower is float('-inf'):
            return False
        else:
            return True

    def has_ub(self):
        if self.upper is None or self.upper is float('inf'):
            return False
        else:
            return True

    def _add_cut(self, sense):
        sep = self.construct_separation_problem(sense=sense)
        sep.name = "Sep"
        if not sep.obj.expr.is_constant():
            res = self.opt.solve(sep)
            if (res.solver.termination_condition
                    is not TerminationCondition.optimal):
                raise RuntimeError("Solver '{}' failed to solve separation "
                                   "problem.".format('gurobi'))

        if sense is minimize:
            feasible = value(self.lower <= sep.obj + self.eps)
        else:
            feasible = value(sep.obj <= self.upper + self.eps)

        if not feasible:
            uncparam = sep.uncparam
            expr = self._rule({i: uncparam[i].value for i in uncparam})
            self._constraints.add((self.lower, expr, self.upper))

        return feasible

    def add_cut(self, solver='gurobi', options={}):
        """ Solve separation problem and add cut. """
        self.opt = SolverFactory(solver)
        for key, val in options.items():
            self.opt.options[key] = val

        if 'subsolver_tolerance' in options:
            self.eps = options['subsolver_tolerance']
        else:
            self.eps = 1e-5

        feasible = True
        if self.has_ub():
            feasible = feasible and self._add_cut(maximize)

        if self.has_lb():
            feasible = feasible and self._add_cut(minimize)

        if feasible is None:
            import ipdb
            ipdb.set_trace()

        self.feasible = feasible

        return feasible

    def construct_separation_problem(self, sense=maximize):
        m = ConcreteModel()
        uncparam = self._uncparam[0]
        index = uncparam.index_set()
        # Create inner problem variables
        # TODO: use setattr to give uncparam same name as in model
        m.uncparam = Var(index)
        # collect current coefficient values
        expr = self._rule(m.uncparam, compute_values=True)
        # construct objective with current coefficient values
        m.obj = Objective(expr=expr, sense=sense)

        # construct constraints from uncertainty set
        uncset = self._uncset[0]
        m.cons = ConstraintList()
        substitution_map = {id(uncparam[i]): m.uncparam[i] for i in index}
        if not uncset.is_lib():
            for c in uncset.component_data_objects(Constraint):
                m.cons.add(
                    (c.lower, replace_expressions(c.body,
                                                  substitution_map), c.upper))
        else:
            for cons in uncset.generate_cons_from_lib(m.uncparam):
                m.cons.add(cons)
        return m
        # What should we do with the constraints? Replace UncParams by the
        # Vars? Or restructure UncParam so that we can solve directly.k

    # !!!
    def nominal_constraint_expr(self):
        """ Generate the nominal constraint. """
        # TODO: replace p.value by p.nominal
        uncparam = self._uncparam[0]
        expr = self._rule({i: uncparam[i].nominal for i in uncparam})
        return (self._bounds[0], expr, self._bounds[1])

    # !!!
    def is_feasible(self):
        return False

    def construct_rule(self, expr):
        repn = generate_linear_repn(expr)
        linear_vars = repn.linear_vars
        linear_coefs = repn.linear_coefs
        constant = repn.constant
        id_coef_dict = {id(v): c for v, c in zip(linear_vars, linear_coefs)}
        param = self._uncparam[0]
        index_coef_dict = {i: id_coef_dict.get(id(param[i]), 0) for i in param}

        def rule(x, compute_values=False):
            if compute_values:
                return (quicksum(value(index_coef_dict[i]) * x[i]
                                 for i in x) + value(constant))
            else:
                return quicksum(index_coef_dict[i] * x[i]
                                for i in x) + constant

        return rule