Exemplo n.º 1
0
def _build_equality_set(m):
    """Construct an equality set map.

    Maps all variables to the set of variables that are linked to them by
    equality. Mapping takes place using id(). That is, if you have x = y, then
    you would have id(x) -> ComponentSet([x, y]) and id(y) -> ComponentSet([x,
    y]) in the mapping.

    """
    #: dict: map of var UID to the set of all equality-linked var UIDs
    eq_var_map = ComponentMap()
    relevant_vars = ComponentSet()
    for constr in m.component_data_objects(ctype=Constraint,
                                           active=True,
                                           descend_into=True):
        # Check to make sure the constraint is of form v1 - v2 == 0
        if (value(constr.lower) == 0 and value(constr.upper) == 0
                and constr.body.polynomial_degree() == 1):
            repn = generate_canonical_repn(constr.body)
            # only take the variables with nonzero coefficients
            vars_ = [v for i, v in enumerate(repn.variables) if repn.linear[i]]
            if (len(vars_) == 2
                    and sorted(l for l in repn.linear if l) == [-1, 1]):
                # this is an a == b constraint.
                v1 = vars_[0]
                v2 = vars_[1]
                set1 = eq_var_map.get(v1, ComponentSet([v1]))
                set2 = eq_var_map.get(v2, ComponentSet([v2]))
                relevant_vars.update([v1, v2])
                set1.update(set2)  # set1 is now the union
                for v in set1:
                    eq_var_map[v] = set1

    return eq_var_map, relevant_vars
Exemplo n.º 2
0
 def test_induced_linearity_case2(self):
     m = ConcreteModel()
     m.x = Var([0], bounds=(-3, 8))
     m.y = Var(RangeSet(4), domain=Binary)
     m.z = Var(domain=Integers, bounds=(-1, 2))
     m.constr = Constraint(
         expr=m.x[0] == m.y[1] + 2 * m.y[2] + m.y[3] + 2 * m.y[4] + m.z)
     m.logical = ConstraintList()
     m.logical.add(expr=m.y[1] + m.y[2] == 1)
     m.logical.add(expr=m.y[3] + m.y[4] == 1)
     m.logical.add(expr=m.y[2] + m.y[4] <= 1)
     m.b = Var(bounds=(-2, 7))
     m.c = Var()
     m.bilinear = Constraint(
         expr=(m.x[0] - 3) * (m.b + 2) - (m.c + 4) * m.b +
         exp(m.b ** 2) * m.x[0] <= m.c)
     TransformationFactory('contrib.induced_linearity').apply_to(m)
     xfrmed_blk = m._induced_linearity_info.x0_b_bilinear
     self.assertSetEqual(
         set(xfrmed_blk.valid_values), set([1, 2, 3, 4, 5]))
     select_one_repn = generate_standard_repn(
         xfrmed_blk.select_one_value.body)
     self.assertEqual(
         ComponentSet(select_one_repn.linear_vars),
         ComponentSet(xfrmed_blk.x_active[i] for i in xfrmed_blk.valid_values))
Exemplo n.º 3
0
    def test_log(self):
        m = pe.ConcreteModel()
        m.x = pe.Var(bounds=(1, 2))
        m.y = pe.Var(bounds=(1, 2))
        m.z = pe.Var()
        m.w = pe.Var()
        m.c = pe.Constraint(expr=pe.log(m.x * m.y) + m.z == 0)
        m.c2 = pe.Constraint(expr=m.w - 3 * pe.log(m.x * m.y) == 0)

        rel = coramin.relaxations.relax(m)

        self.assertTrue(hasattr(rel, 'aux_cons'))
        self.assertTrue(hasattr(rel, 'aux_vars'))
        self.assertEqual(len(rel.aux_cons), 2)
        self.assertEqual(len(rel.aux_vars), 2)

        self.assertAlmostEqual(rel.aux_vars[1].lb, 1)
        self.assertAlmostEqual(rel.aux_vars[1].ub, 4)

        self.assertAlmostEqual(rel.aux_vars[2].lb, math.log(1))
        self.assertAlmostEqual(rel.aux_vars[2].ub, math.log(4))

        self.assertEqual(rel.aux_cons[1].lower, 0)
        self.assertEqual(rel.aux_cons[1].upper, 0)
        ders = reverse_sd(rel.aux_cons[1].body)
        self.assertEqual(ders[rel.z], 1)
        self.assertEqual(ders[rel.aux_vars[2]], 1)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[1].body))),
                         2)

        self.assertEqual(rel.aux_cons[2].lower, 0)
        self.assertEqual(rel.aux_cons[2].upper, 0)
        ders = reverse_sd(rel.aux_cons[2].body)
        self.assertEqual(ders[rel.w], 1)
        self.assertEqual(ders[rel.aux_vars[2]], -3)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[2].body))),
                         2)

        self.assertTrue(hasattr(rel, 'relaxations'))
        self.assertTrue(hasattr(rel.relaxations, 'rel0'))
        self.assertTrue(
            isinstance(rel.relaxations.rel0,
                       coramin.relaxations.PWMcCormickRelaxation))
        self.assertIn(rel.x, ComponentSet(rel.relaxations.rel0.get_rhs_vars()))
        self.assertIn(rel.y, ComponentSet(rel.relaxations.rel0.get_rhs_vars()))
        self.assertEqual(id(rel.aux_vars[1]),
                         id(rel.relaxations.rel0.get_aux_var()))

        self.assertTrue(hasattr(rel.relaxations, 'rel1'))
        self.assertTrue(
            isinstance(rel.relaxations.rel1,
                       coramin.relaxations.PWUnivariateRelaxation))
        self.assertIn(rel.aux_vars[1],
                      ComponentSet(rel.relaxations.rel1.get_rhs_vars()))
        self.assertEqual(id(rel.aux_vars[2]),
                         id(rel.relaxations.rel1.get_aux_var()))
        self.assertFalse(rel.relaxations.rel1.is_rhs_convex())
        self.assertTrue(rel.relaxations.rel1.is_rhs_concave())

        self.assertFalse(hasattr(rel.relaxations, 'rel2'))
Exemplo n.º 4
0
    def test_var_aggregate(self):
        """Test for transitivity in a variable equality set."""
        m = self.build_model()

        TransformationFactory('contrib.aggregate_vars').apply_to(m)
        z_to_vars = m._var_aggregator_info.z_to_vars
        var_to_z = m._var_aggregator_info.var_to_z
        z = m._var_aggregator_info.z
        self.assertEqual(z_to_vars[z[1]], ComponentSet([m.v3, m.v4]))
        self.assertEqual(z_to_vars[z[2]],
                         ComponentSet([m.x[1], m.x[2], m.x[3], m.x[4]]))
        self.assertEqual(z_to_vars[z[3]], ComponentSet([m.y[1], m.y[2]]))
        self.assertIs(var_to_z[m.v3], z[1])
        self.assertIs(var_to_z[m.v4], z[1])
        self.assertIs(var_to_z[m.x[1]], z[2])
        self.assertIs(var_to_z[m.x[2]], z[2])
        self.assertIs(var_to_z[m.x[3]], z[2])
        self.assertIs(var_to_z[m.x[4]], z[2])
        self.assertIs(var_to_z[m.y[1]], z[3])
        self.assertIs(var_to_z[m.y[2]], z[3])

        self.assertEqual(z[1].value, 2)
        self.assertEqual(z[1].lb, 2)
        self.assertEqual(z[1].ub, 4)

        self.assertEqual(z[3].value, 3.5)
Exemplo n.º 5
0
    def test_product1(self):
        m = pe.ConcreteModel()
        m.x = pe.Var(bounds=(-1, 1))
        m.y = pe.Var(bounds=(-1, 1))
        m.z = pe.Var()
        m.c = pe.Constraint(expr=m.z - m.x * m.y == 0)

        rel = coramin.relaxations.relax(m)

        self.assertTrue(hasattr(rel, 'aux_cons'))
        self.assertTrue(hasattr(rel, 'aux_vars'))
        self.assertEqual(len(rel.aux_cons), 1)
        self.assertEqual(len(rel.aux_vars), 1)
        self.assertAlmostEqual(rel.aux_vars[1].lb, -1)
        self.assertAlmostEqual(rel.aux_vars[1].ub, 1)
        self.assertEqual(rel.aux_cons[1].lower, 0)
        self.assertEqual(rel.aux_cons[1].upper, 0)
        ders = reverse_sd(rel.aux_cons[1].body)
        self.assertEqual(ders[rel.z], 1)
        self.assertEqual(ders[rel.aux_vars[1]], -1)
        self.assertEqual(len(list(identify_variables(rel.aux_cons[1].body))),
                         2)

        self.assertTrue(hasattr(rel, 'relaxations'))
        self.assertTrue(hasattr(rel.relaxations, 'rel0'))
        self.assertTrue(
            isinstance(rel.relaxations.rel0,
                       coramin.relaxations.PWMcCormickRelaxation))
        self.assertIn(rel.x, ComponentSet(rel.relaxations.rel0.get_rhs_vars()))
        self.assertIn(rel.y, ComponentSet(rel.relaxations.rel0.get_rhs_vars()))
        self.assertEqual(id(rel.aux_vars[1]),
                         id(rel.relaxations.rel0.get_aux_var()))
Exemplo n.º 6
0
    def _apply_to(self, instance, **kwds):
        config = self.CONFIG(kwds)
        if config.tmp and not hasattr(instance, '_tmp_propagate_fixed'):
            instance._tmp_propagate_fixed = ComponentSet()
        eq_var_map, relevant_vars = _build_equality_set(instance)
        #: ComponentSet: The set of all fixed variables
        fixed_vars = ComponentSet((v for v in relevant_vars if v.fixed))
        newly_fixed = _detect_fixed_variables(instance)
        if config.tmp:
            instance._tmp_propagate_fixed.update(newly_fixed)
        fixed_vars.update(newly_fixed)
        processed = ComponentSet()
        # Go through each fixed variable to propagate the 'fixed' status to all
        # equality-linked variabes.
        for v1 in fixed_vars:
            # If we have already processed the variable, skip it.
            if v1 in processed:
                continue

            eq_set = eq_var_map.get(v1, ComponentSet([v1]))
            for v2 in eq_set:
                if (v2.fixed and value(v1) != value(v2)):
                    raise ValueError(
                        'Variables {} and {} have conflicting fixed '
                        'values of {} and {}, but are linked by '
                        'equality constraints.'.format(v1.name, v2.name,
                                                       value(v1), value(v2)))
                elif not v2.fixed:
                    v2.fix(value(v1))
                    if config.tmp:
                        instance._tmp_propagate_fixed.add(v2)
            # Add all variables in the equality set to the set of processed
            # variables.
            processed.update(eq_set)
Exemplo n.º 7
0
    def run_order(self, G, order, function, ignore=None, use_guesses=False):
        """
        Run computations in the order provided by calling the function

        Arguments
        ---------
            G
                A networkx graph corresponding to order
            order
                The order in which to run each node in the graph
            function
                The function to be called on each block/node
            ignore
                Edge indexes to ignore when passing values
            use_guesses
                If True, will check the guesses dict when fixing
                free variables before calling function
        """
        fixed_inputs = self.fixed_inputs()
        fixed_outputs = ComponentSet()
        edge_map = self.edge_to_idx(G)
        guesses = self.options["guesses"]
        default = self.options["default_guess"]
        for lev in order:
            for unit in lev:
                if unit not in fixed_inputs:
                    fixed_inputs[unit] = ComponentSet()
                fixed_ins = fixed_inputs[unit]

                # make sure all inputs are fixed
                for port in unit.component_data_objects(Port):
                    if not len(port.sources()):
                        continue
                    if use_guesses and port in guesses:
                        self.load_guesses(guesses, port, fixed_ins)
                    self.load_values(port, default, fixed_ins, use_guesses)

                function(unit)

                # free the inputs that were not already fixed
                for var in fixed_ins:
                    var.free()
                fixed_ins.clear()

                # pass the values downstream for all outlet ports
                for port in unit.component_data_objects(Port):
                    dests = port.dests()
                    if not len(dests):
                        continue
                    for var in port.iter_vars(expr_vars=True, fixed=False):
                        fixed_outputs.add(var)
                        var.fix()
                    for arc in dests:
                        arc_map = self.arc_to_edge(G)
                        if edge_map[arc_map[arc]] not in ignore:
                            self.pass_values(arc, fixed_inputs)
                    for var in fixed_outputs:
                        var.free()
                    fixed_outputs.clear()
Exemplo n.º 8
0
 def test_len(self):
     cset = ComponentSet()
     self.assertEqual(len(cset), 0)
     cset.update(self._components)
     self.assertEqual(len(cset), len(self._components))
     cset = ComponentSet(self._components)
     self.assertEqual(len(cset), len(self._components))
     self.assertTrue(len(self._components) > 0)
Exemplo n.º 9
0
def add_integer_cut(var_values, target_model, solve_data, config, feasible=False):
    """Add an integer cut to the target GDP model."""
    with time_code(solve_data.timing, 'integer cut generation'):
        m = target_model
        GDPopt = m.GDPopt_utils
        var_value_is_one = ComponentSet()
        var_value_is_zero = ComponentSet()
        for var, val in zip(GDPopt.variable_list, var_values):
            if not var.is_binary():
                continue
            if var.fixed:
                if val is not None and var.value != val:
                    # val needs to be None or match var.value. Otherwise, we have a
                    # contradiction.
                    raise ValueError(
                        "Fixed variable %s has value %s != "
                        "provided value of %s." % (var.name, var.value, val))
                val = var.value

            if not config.force_subproblem_nlp:
                # Skip indicator variables
                # TODO we should implement this as a check among Disjuncts instead
                if not (var.local_name == 'indicator_var' and var.parent_block().type() == Disjunct):
                    continue

            if fabs(val - 1) <= config.integer_tolerance:
                var_value_is_one.add(var)
            elif fabs(val) <= config.integer_tolerance:
                var_value_is_zero.add(var)
            else:
                raise ValueError(
                    'Binary %s = %s is not 0 or 1' % (var.name, val))

        if not (var_value_is_one or var_value_is_zero):
            # if no remaining binary variables, then terminate algorithm.
            config.logger.info(
                'Adding integer cut to a model without discrete variables. '
                'Model is now infeasible.')
            if solve_data.objective_sense == minimize:
                solve_data.LB = float('inf')
            else:
                solve_data.UB = float('-inf')
            return False

        int_cut = (sum(1 - v for v in var_value_is_one) +
                   sum(v for v in var_value_is_zero)) >= 1

        if not feasible:
            config.logger.info('Adding integer cut')
            GDPopt.integer_cuts.add(expr=int_cut)
        else:
            backtracking_enabled = (
                "disabled" if GDPopt.no_backtracking.active else "allowed")
            config.logger.info(
                'Registering explored configuration. '
                'Backtracking is currently %s.' % backtracking_enabled)
            GDPopt.no_backtracking.add(expr=int_cut)
Exemplo n.º 10
0
    def _apply_to(self, instance, tmp=False):
        """Apply the transformation.

        Args:
            instance (Block): the block on which to search for x == y
                constraints. Note that variables may be located anywhere in
                the model.
            tmp (bool, optional): Whether the bound modifications will be
                temporary

        Returns:
            None

        """
        if tmp and not hasattr(instance, '_tmp_propagate_original_bounds'):
            instance._tmp_propagate_original_bounds = Suffix(
                direction=Suffix.LOCAL)
        eq_var_map, relevant_vars = _build_equality_set(instance)
        processed = ComponentSet()
        # Go through each variable in an equality set to propagate the variable
        # bounds to all equality-linked variables.
        for var in relevant_vars:
            # If we have already processed the variable, skip it.
            if var in processed:
                continue

            var_equality_set = eq_var_map.get(var, ComponentSet([var]))

            #: variable lower bounds in the equality set
            lbs = [v.lb for v in var_equality_set if v.has_lb()]
            max_lb = max(lbs) if len(lbs) > 0 else None
            #: variable upper bounds in the equality set
            ubs = [v.ub for v in var_equality_set if v.has_ub()]
            min_ub = min(ubs) if len(ubs) > 0 else None

            # Check  for error due to bound cross-over
            if max_lb is not None and min_ub is not None and max_lb > min_ub:
                # the lower bound is above the upper bound. Raise a ValueError.
                # get variable with the highest lower bound
                v1 = next(v for v in var_equality_set if v.lb == max_lb)
                # get variable with the lowest upper bound
                v2 = next(v for v in var_equality_set if v.ub == min_ub)
                raise ValueError(
                    'Variable {} has a lower bound {} '
                    ' > the upper bound {} of variable {}, '
                    'but they are linked by equality constraints.'
                    .format(v1.name, value(v1.lb), value(v2.ub), v2.name))

            for v in var_equality_set:
                if tmp:
                    # TODO warn if overwriting
                    instance._tmp_propagate_original_bounds[v] = (
                        v.lb, v.ub)
                v.setlb(max_lb)
                v.setub(min_ub)

            processed.update(var_equality_set)
Exemplo n.º 11
0
 def test_isdisjoint(self):
     cset1 = ComponentSet()
     cset2 = ComponentSet()
     self.assertTrue(cset1.isdisjoint(cset2))
     self.assertTrue(cset2.isdisjoint(cset1))
     v = variable()
     cset1.add(v)
     self.assertTrue(cset1.isdisjoint(cset2))
     self.assertTrue(cset2.isdisjoint(cset1))
     cset2.add(v)
     self.assertFalse(cset1.isdisjoint(cset2))
     self.assertFalse(cset2.isdisjoint(cset1))
Exemplo n.º 12
0
def _setup_subproblem(b, master_vars, relax_subproblem_cons):
    # first get the objective and turn it into a constraint
    master_vars = ComponentSet(master_vars)

    objs = list(
        b.component_data_objects(pe.Objective, descend_into=False,
                                 active=True))
    if len(objs) != 1:
        raise ValueError('Subproblem must have exactly one objective')
    orig_obj = objs[0]
    orig_obj_expr = orig_obj.expr
    b.del_component(orig_obj)

    b._z = pe.Var(bounds=(0, None))
    b.objective = pe.Objective(expr=b._z)
    b.dual = pe.Suffix(direction=pe.Suffix.IMPORT)
    b._eta = pe.Var()

    b.aux_cons = pe.ConstraintList()
    for c in list(
            b.component_data_objects(pe.Constraint,
                                     descend_into=True,
                                     active=True,
                                     sort=True)):
        if not relax_subproblem_cons:
            c_vars = ComponentSet(
                identify_variables(c.body, include_fixed=False))
            if not _any_common_elements(master_vars, c_vars):
                continue
        if c.equality:
            body = c.body
            rhs = pe.value(c.lower)
            body -= rhs
            b.aux_cons.add(body - b._z <= 0)
            b.aux_cons.add(-body - b._z <= 0)
            _del_con(c)
        else:
            body = c.body
            lower = pe.value(c.lower)
            upper = pe.value(c.upper)
            if upper is not None:
                body_upper = body - upper - b._z
                b.aux_cons.add(body_upper <= 0)
            if lower is not None:
                body_lower = body - lower
                body_lower = -body_lower
                body_lower -= b._z
                b.aux_cons.add(body_lower <= 0)
            _del_con(c)

    b.obj_con = pe.Constraint(expr=orig_obj_expr - b._eta - b._z <= 0)
Exemplo n.º 13
0
    def _apply_to(self, instance, tmp=False):
        """Apply the transformation.

        Args:
            instance (Block): the block on which to search for x == y
                constraints. Note that variables may be located anywhere in
                the model.
            tmp (bool, optional): Whether the variable fixing will be temporary

        Returns:
            None

        Raises:
            ValueError: if two fixed variables x = y have different values.

        """
        if tmp and not hasattr(instance, '_tmp_propagate_fixed'):
            instance._tmp_propagate_fixed = ComponentSet()
        eq_var_map, relevant_vars = _build_equality_set(instance)
        #: ComponentSet: The set of all fixed variables
        fixed_vars = ComponentSet((v for v in relevant_vars if v.fixed))
        newly_fixed = _detect_fixed_variables(instance)
        if tmp:
            instance._tmp_propagate_fixed.update(newly_fixed)
        fixed_vars.update(newly_fixed)
        processed = ComponentSet()
        # Go through each fixed variable to propagate the 'fixed' status to all
        # equality-linked variabes.
        for v1 in fixed_vars:
            # If we have already processed the variable, skip it.
            if v1 in processed:
                continue

            eq_set = eq_var_map.get(v1, ComponentSet([v1]))
            for v2 in eq_set:
                if (v2.fixed and value(v1) != value(v2)):
                    raise ValueError(
                        'Variables {} and {} have conflicting fixed '
                        'values of {} and {}, but are linked by '
                        'equality constraints.'
                        .format(v1.name,
                                v2.name,
                                value(v1),
                                value(v2)))
                elif not v2.fixed:
                    v2.fix(value(v1))
                    if tmp:
                        instance._tmp_propagate_fixed.add(v2)
            # Add all variables in the equality set to the set of processed
            # variables.
            processed.update(eq_set)
Exemplo n.º 14
0
def add_integer_cut(var_values, solve_data, config, feasible=False):
    """Add an integer cut to the linear GDP model."""
    m = solve_data.linear_GDP
    GDPopt = m.GDPopt_utils
    var_value_is_one = ComponentSet()
    var_value_is_zero = ComponentSet()
    for var, val in zip(GDPopt.working_var_list, var_values):
        if not var.is_binary():
            continue
        if var.fixed:
            if val is not None and var.value != val:
                # val needs to be None or match var.value. Otherwise, we have a
                # contradiction.
                raise ValueError(
                    "Fixed variable %s has value %s != "
                    "provided value of %s." % (var.name, var.value, val))
            val = var.value
        # TODO we can also add a check to skip binary variables that are not an
        # indicator_var on disjuncts.
        if fabs(val - 1) <= config.integer_tolerance:
            var_value_is_one.add(var)
        elif fabs(val) <= config.integer_tolerance:
            var_value_is_zero.add(var)
        else:
            raise ValueError(
                'Binary %s = %s is not 0 or 1' % (var.name, val))

    if not (var_value_is_one or var_value_is_zero):
        # if no remaining binary variables, then terminate algorithm.
        config.logger.info(
            'Adding integer cut to a model without binary variables. '
            'Model is now infeasible.')
        if solve_data.objective_sense == minimize:
            solve_data.LB = float('inf')
        else:
            solve_data.UB = float('-inf')
        return False

    int_cut = (sum(1 - v for v in var_value_is_one) +
               sum(v for v in var_value_is_zero)) >= 1

    if not feasible:
        config.logger.info('Adding integer cut')
        GDPopt.integer_cuts.add(expr=int_cut)
    else:
        backtracking_enabled = (
            "disabled" if GDPopt.no_backtracking.active else "allowed")
        config.logger.info(
            'Registering explored configuration. '
            'Backtracking is currently %s.' % backtracking_enabled)
        GDPopt.no_backtracking.add(expr=int_cut)
Exemplo n.º 15
0
def _process_activated_container(blk):
    """Process a container object, returning the new components found."""
    new_fixed_true_disjuncts = ComponentSet(
        disj for disj in blk.component_data_objects(Disjunct, active=True)
        if disj.indicator_var.value == 1 and disj.indicator_var.fixed)
    new_activated_disjunctions = ComponentSet(
        blk.component_data_objects(Disjunction, active=True))
    new_activated_disjuncts = ComponentSet(
        disj for disjtn in new_activated_disjunctions
        for disj in _activated_disjuncts_in_disjunction(disjtn))
    new_activated_constraints = ComponentSet(
        blk.component_data_objects(Constraint, active=True))
    return (new_activated_disjunctions, new_fixed_true_disjuncts,
            new_activated_disjuncts, new_activated_constraints)
Exemplo n.º 16
0
def filter_variables_from_solution(candidate_variables_at_relaxation_solution, tolerance=1e-6):
    """
    This function takes a set of candidate variables for OBBT and filters out 
    the variables that are at their bounds in the provided solution to the 
    relaxation. See 

    Gleixner, Ambros M., et al. "Three enhancements for
    optimization-based bound tightening." Journal of Global
    Optimization 67.4 (2017): 731-757.

    for details on why this works. The basic idea is that if x = xl is
    feasible for the relaxation that will be used for OBBT, then
    minimizing x subject to that relaxation is guaranteed to result in
    an optimal solution of x* = xl.

    This function simply loops through
    candidate_variables_at_relaxation_solution and specifies which
    variables should be minimized and which variables should be
    maximized with OBBT.

    Parameters
    ----------
    candidate_variables_at_relaxation_solution: iterable of _GeneralVarData
        This should be an iterable of the variables which are candidates 
        for OBBT. The values of the variables should be feasible for the 
        relaxation that would be used to perform OBBT on the variables.
    tolerance: float
        A float greater than or equal to zero. If the value of the variable
        is within tolerance of its lower bound, then that variable is filtered
        from the set of variables that should be minimized for OBBT. The same
        is true for upper bounds and variables that should be maximized.

    Returns
    -------
    vars_to_minimize: ComponentSet of _GeneralVarData
        variables that should be considered for minimization
    vars_to_maximize: ComponentSet of _GeneralVarData
        variables that should be considered for maximization
    """
    candidate_vars = ComponentSet(candidate_variables_at_relaxation_solution)
    vars_to_minimize = ComponentSet()
    vars_to_maximize = ComponentSet()

    for v in candidate_vars:
        if (not v.has_lb()) or (v.value - v.lb > tolerance):
            vars_to_minimize.add(v)
        if (not v.has_ub()) or (v.ub - v.value > tolerance):
            vars_to_maximize.add(v)

    return vars_to_minimize, vars_to_maximize
Exemplo n.º 17
0
 def _set_input(self, relaxation_side=RelaxationSide.BOTH, persistent_solvers=None,
                use_linear_relaxation=True, large_eval_tol=math.inf):
     self._oa_points = list()
     self._saved_oa_points = list()
     self._persistent_solvers = persistent_solvers
     if self._persistent_solvers is None:
         self._persistent_solvers = ComponentSet()
     if not isinstance(self._persistent_solvers, Iterable):
         self._persistent_solvers = ComponentSet([self._persistent_solvers])
     else:
         self._persistent_solvers = ComponentSet(self._persistent_solvers)
     self._relaxation_side = relaxation_side
     assert self._relaxation_side in RelaxationSide
     self.use_linear_relaxation = use_linear_relaxation
     self.large_eval_tol = large_eval_tol
Exemplo n.º 18
0
 def test_misc_set_ops(self):
     v1 = variable()
     cset1 = ComponentSet([v1])
     v2 = variable()
     cset2 = ComponentSet([v2])
     cset3 = ComponentSet([v1, v2])
     empty = ComponentSet([])
     self.assertEqual(cset1 | cset2, cset3)
     self.assertEqual((cset1 | cset2) - cset3, empty)
     self.assertEqual(cset1 ^ cset2, cset3)
     self.assertEqual(cset1 ^ cset3, cset2)
     self.assertEqual(cset2 ^ cset3, cset1)
     self.assertEqual(cset1 & cset2, empty)
     self.assertEqual(cset1 & cset3, cset1)
     self.assertEqual(cset2 & cset3, cset2)
    def create_hull_model(self):
        m = ConcreteModel()
        m.p = Var([1, 2], bounds=(0, 10))
        m.time1 = Disjunction(expr=[m.p[1] >= 1, m.p[1] == 0])

        m.on = Disjunct()
        m.on.above_min = Constraint(expr=m.p[2] >= 1)
        m.on.ramping = Constraint(expr=m.p[2] - m.p[1] <= 3)
        m.on.on_before = Constraint(expr=m.p[1] >= 1)

        m.startup = Disjunct()
        m.startup.startup_limit = Constraint(expr=(1, m.p[2], 2))
        m.startup.off_before = Constraint(expr=m.p[1] == 0)

        m.off = Disjunct()
        m.off.off = Constraint(expr=m.p[2] == 0)
        m.time2 = Disjunction(expr=[m.on, m.startup, m.off])

        m.obj = Objective(expr=m.p[1] + m.p[2])

        hull = TransformationFactory('gdp.hull')
        hull.apply_to(m)
        disaggregatedVars = ComponentSet([
            hull.get_disaggregated_var(m.p[1], m.time1.disjuncts[0]),
            hull.get_disaggregated_var(m.p[1], m.time1.disjuncts[1]),
            hull.get_disaggregated_var(m.p[1], m.on),
            hull.get_disaggregated_var(m.p[2], m.on),
            hull.get_disaggregated_var(m.p[1], m.startup),
            hull.get_disaggregated_var(m.p[2], m.startup),
            hull.get_disaggregated_var(m.p[1], m.off),
            hull.get_disaggregated_var(m.p[2], m.off)
        ])

        return m, disaggregatedVars
Exemplo n.º 20
0
    def _set_instance(self, model, kwds={}):
        if not isinstance(model, (Model, IBlockStorage)):
            msg = "The problem instance supplied to the {0} plugin " \
                  "'_presolve' method must be of type 'Model'".format(type(self))
            raise ValueError(msg)
        self._pyomo_model = model
        self._symbolic_solver_labels = kwds.pop('symbolic_solver_labels',
                                                self._symbolic_solver_labels)
        self._skip_trivial_constraints = kwds.pop(
            'skip_trivial_constraints', self._skip_trivial_constraints)
        self._output_fixed_variable_bounds = kwds.pop(
            'output_fixed_variable_bounds', self._output_fixed_variable_bounds)
        self._pyomo_var_to_solver_var_map = ComponentMap()
        self._pyomo_con_to_solver_con_map = ComponentMap()
        self._vars_referenced_by_con = ComponentMap()
        self._vars_referenced_by_obj = ComponentSet()
        self._referenced_variables = ComponentMap()
        self._objective_label = None
        self._objective = None

        self._symbol_map = SymbolMap()

        if self._symbolic_solver_labels:
            self._labeler = TextLabeler()
        else:
            self._labeler = NumericLabeler('x')
Exemplo n.º 21
0
    def _set_objective(self, obj):
        if self._objective is not None:
            for var in self._vars_referenced_by_obj:
                self._referenced_variables[var] -= 1
            self._vars_referenced_by_obj = ComponentSet()
            self._objective = None

        if obj.active is False:
            raise ValueError('Cannot add inactive objective to solver.')

        if obj.sense == pyomo.core.kernel.minimize:
            sense = self._gurobipy.GRB.MINIMIZE
        elif obj.sense == pyomo.core.kernel.maximize:
            sense = self._gurobipy.GRB.MAXIMIZE
        else:
            raise ValueError('Objective sense is not recognized: {0}'.format(
                obj.sense))

        gurobi_expr, referenced_vars = self._get_expr_from_pyomo_expr(
            obj.expr, self._max_obj_degree)

        for var in referenced_vars:
            self._referenced_variables[var] += 1

        self._solver_model.setObjective(gurobi_expr, sense=sense)
        self._objective = obj
        self._vars_referenced_by_obj = referenced_vars
Exemplo n.º 22
0
    def _add_sos_constraint(self, con):
        if not con.active:
            return None

        conname = self._symbol_map.getSymbol(con, self._labeler)
        level = con.level
        if level == 1:
            sos_type = self._solver_model.SOS.type.SOS1
        elif level == 2:
            sos_type = self._solver_model.SOS.type.SOS2
        else:
            raise ValueError("Solver does not support SOS "
                             "level {0} constraints".format(level))

        cplex_vars = []
        weights = []

        self._vars_referenced_by_con[con] = ComponentSet()

        if hasattr(con, 'get_items'):
            # aml sos constraint
            sos_items = list(con.get_items())
        else:
            # kernel sos constraint
            sos_items = list(con.items())

        for v, w in sos_items:
            self._vars_referenced_by_con[con].add(v)
            cplex_vars.append(self._pyomo_var_to_solver_var_map[v])
            self._referenced_variables[v] += 1
            weights.append(w)

        self._solver_model.SOS.add(type=sos_type, SOS=[cplex_vars, weights], name=conname)
        self._pyomo_con_to_solver_con_map[con] = conname
        self._solver_con_to_pyomo_con_map[conname] = con
Exemplo n.º 23
0
    def _add_sos_constraint(self, con):
        if not con.active:
            return None

        conname = self._symbol_map.getSymbol(con, self._labeler)
        level = con.level
        if level not in [1, 2]:
            raise ValueError("Solver does not support SOS "
                             "level {0} constraints".format(level))

        xpress_vars = []
        weights = []

        self._vars_referenced_by_con[con] = ComponentSet()

        if hasattr(con, 'get_items'):
            # aml sos constraint
            sos_items = list(con.get_items())
        else:
            # kernel sos constraint
            sos_items = list(con.items())

        for v, w in sos_items:
            self._vars_referenced_by_con[con].add(v)
            xpress_vars.append(self._pyomo_var_to_solver_var_map[v])
            self._referenced_variables[v] += 1
            weights.append(w)

        xpress_con = self._xpress.sos(xpress_vars, weights, level, conname)
        self._solver_model.addSOS(xpress_con)
        self._pyomo_con_to_solver_con_map[con] = xpress_con
        self._solver_con_to_pyomo_con_map[xpress_con] = con
Exemplo n.º 24
0
    def _get_expr_from_pyomo_repn(self, repn, max_degree=2):
        referenced_vars = ComponentSet()

        degree = repn.polynomial_degree()
        if (degree is None) or (degree > max_degree):
            raise DegreeError(
                'XpressDirect does not support expressions of degree {0}.'.
                format(degree))

        # NOTE: xpress's python interface only allows for expresions
        #       with native numeric types. Others, like numpy.float64,
        #       will cause an exception when constructing expressions
        if len(repn.linear_vars) > 0:
            referenced_vars.update(repn.linear_vars)
            new_expr = self._xpress.Sum(
                float(coef) * self._pyomo_var_to_solver_var_map[var]
                for coef, var in zip(repn.linear_coefs, repn.linear_vars))
        else:
            new_expr = 0.0

        for coef, (x, y) in zip(repn.quadratic_coefs, repn.quadratic_vars):
            new_expr += float(coef) * self._pyomo_var_to_solver_var_map[
                x] * self._pyomo_var_to_solver_var_map[y]
            referenced_vars.add(x)
            referenced_vars.add(y)

        new_expr += repn.constant

        return new_expr, referenced_vars
Exemplo n.º 25
0
def _build_equality_set(model):
    """Construct an equality set map.

    Maps all variables to the set of variables that are linked to them by
    equality. Mapping takes place using id(). That is, if you have x = y, then
    you would have id(x) -> ComponentSet([x, y]) and id(y) -> ComponentSet([x,
    y]) in the mapping.

    """
    # Map of variables to their equality set (ComponentSet)
    eq_var_map = ComponentMap()

    # Loop through all the active constraints in the model
    for constraint in model.component_data_objects(ctype=Constraint,
                                                   active=True,
                                                   descend_into=True):
        eq_linked_vars = _get_equality_linked_variables(constraint)
        if not eq_linked_vars:
            continue  # if we get an empty tuple, skip to next constraint.
        v1, v2 = eq_linked_vars
        set1 = eq_var_map.get(v1, ComponentSet((v1, v2)))
        set2 = eq_var_map.get(v2, (v2, ))

        # if set1 and set2 are equivalent, skip to next constraint.
        if set1 is set2:
            continue

        # add all elements of set2 to set 1
        set1.update(set2)
        # Update all elements to point to set 1
        for v in set1:
            eq_var_map[v] = set1

    return eq_var_map
Exemplo n.º 26
0
    def _get_expr_from_pyomo_repn(self, repn, max_degree=2):
        referenced_vars = ComponentSet()

        degree = repn.polynomial_degree()
        if (degree is None) or (degree > max_degree):
            raise DegreeError(
                'GurobiDirect does not support expressions of degree {0}.'.
                format(degree))

        if len(repn.linear_vars) > 0:
            referenced_vars.update(repn.linear_vars)
            new_expr = self._gurobipy.LinExpr(repn.linear_coefs, [
                self._pyomo_var_to_solver_var_map[i] for i in repn.linear_vars
            ])
        else:
            new_expr = 0.0

        for i, v in enumerate(repn.quadratic_vars):
            x, y = v
            new_expr += repn.quadratic_coefs[
                i] * self._pyomo_var_to_solver_var_map[
                    x] * self._pyomo_var_to_solver_var_map[y]
            referenced_vars.add(x)
            referenced_vars.add(y)

        new_expr += repn.constant

        return new_expr, referenced_vars
Exemplo n.º 27
0
    def _transformDisjunctionData(self, disjunction):
        # the sum of all the indicator variable values of disjuncts in the
        # disjunction
        logical_sum = sum(
            value(disj.indicator_var) for disj in disjunction.disjuncts)

        # Check that the disjunctions are not being fixed to an infeasible
        # realization.
        if disjunction.xor and not logical_sum == 1:
            # for XOR disjunctions, the sum of all disjunct values should be 1
            raise GDP_Error(
                "Disjunction %s violated. "
                "Expected 1 disjunct to be active, but %s were active." %
                (disjunction.name, logical_sum))
        elif not logical_sum >= 1:
            # for non-XOR disjunctions, the sum of all disjunct values should
            # be at least 1
            raise GDP_Error("Disjunction %s violated. "
                            "Expected at least 1 disjunct to be active, "
                            "but none were active.")
        else:
            # disjunction is in feasible realization. Deactivate it.
            disjunction.deactivate()

        # Process the disjuncts associatd with the disjunction that have not
        # already been transformed.
        for disj in ComponentSet(
                disjunction.disjuncts) - self._transformedDisjuncts:
            self._transformDisjunctData(disj)
        # Update the set of transformed disjuncts with those from this
        # disjunction
        self._transformedDisjuncts.update(disjunction.disjuncts)
Exemplo n.º 28
0
 def test_clear(self):
     cset = ComponentSet()
     self.assertEqual(len(cset), 0)
     cset.update(self._components)
     self.assertEqual(len(cset), len(self._components))
     cset.clear()
     self.assertEqual(len(cset), 0)
Exemplo n.º 29
0
    def _set_objective(self, obj):

        if self._objective is not None:
            for var in self._vars_referenced_by_obj:
                self._referenced_variables[var] -= 1
            self._vars_referenced_by_obj = ComponentSet()
            self._objective = None

        if obj.active is False:
            raise ValueError('Cannot add inactive objective to solver.')

        if obj.sense == minimize:
            self._solver_model.putobjsense(self._mosek.objsense.minimize)
        elif obj.sense == maximize:
            self._solver_model.putobjsense(self._mosek.objsense.maximize)
        else:
            raise ValueError('Objective sense is not recognized: {0}'.format(
                obj.sense))

        mosek_expr, referenced_vars = self._get_expr_from_pyomo_expr(
            obj.expr, self._max_obj_degree)

        for var in referenced_vars:
            self._referenced_variables[var] += 1

        for i, j in enumerate(mosek_expr[1]):
            self._solver_model.putcj(j, mosek_expr[0][i])

        self._solver_model.putqobj(mosek_expr[4], mosek_expr[5], mosek_expr[3])
        self._solver_model.putcfix(mosek_expr[2])
        self._objective = obj
        self._vars_referenced_by_obj = referenced_vars
Exemplo n.º 30
0
    def _get_expr_from_pyomo_repn(self, repn, max_degree=2):
        referenced_vars = ComponentSet()

        degree = repn.polynomial_degree()
        if (degree is None) or (degree > max_degree):
            raise DegreeError(
                'Mosek does not support expressions of degree {0}.'.format(
                    degree))

        # if len(repn.linear_vars) > 0:
        referenced_vars.update(repn.linear_vars)

        indexes = []
        [
            indexes.append(self._pyomo_var_to_solver_var_map[i])
            for i in repn.linear_vars
        ]

        new_expr = [list(repn.linear_coefs), indexes, repn.constant]

        qsubi = []
        qsubj = []
        qval = []
        for i, v in enumerate(repn.quadratic_vars):
            x, y = v
            qsubj.append(self._pyomo_var_to_solver_var_map[x])
            qsubi.append(self._pyomo_var_to_solver_var_map[y])
            qval.append(repn.quadratic_coefs[i] * ((qsubi == qsubj) + 1))
            referenced_vars.add(x)
            referenced_vars.add(y)
        new_expr.extend([qval, qsubi, qsubj])

        return new_expr, referenced_vars