Exemplo n.º 1
0
    def test_npv_abs(self):
        m = ConcreteModel()
        m.p1 = Param(mutable=True)
        m.p2 = Param(mutable=True)
        m.x = Var()

        e1 = abs(m.p1)
        e2 = replace_expressions(e1, {id(m.p1): m.p2})
        e3 = replace_expressions(e1, {id(m.p1): m.x})

        self.assertTrue(compare_expressions(e2, abs(m.p2)))
        self.assertTrue(compare_expressions(e3, abs(m.x)))
Exemplo n.º 2
0
    def test_npv_div(self):
        m = ConcreteModel()
        m.p1 = Param(mutable=True)
        m.p2 = Param(mutable=True)
        m.x = Var()

        e1 = m.p1/3
        e2 = replace_expressions(e1, {id(m.p1): m.p2})
        e3 = replace_expressions(e1, {id(m.p1): m.x})

        self.assertTrue(compare_expressions(e2, m.p2/3))
        self.assertTrue(compare_expressions(e3, DivisionExpression([m.x, 3])))
Exemplo n.º 3
0
    def test_npv_unary(self):
        m = ConcreteModel()
        m.p1 = Param(mutable=True)
        m.p2 = Param(mutable=True)
        m.x = Var(initialize=0)

        e1 = sin(m.p1)
        e2 = replace_expressions(e1, {id(m.p1): m.p2})
        e3 = replace_expressions(e1, {id(m.p1): m.x})

        self.assertTrue(compare_expressions(e2, sin(m.p2)))
        self.assertTrue(compare_expressions(e3, sin(m.x)))
Exemplo n.º 4
0
    def test_npv_product(self):
        m = ConcreteModel()
        m.p1 = Param(mutable=True)
        m.p2 = Param(mutable=True)
        m.x = Var()

        e1 = m.p1*3
        e2 = replace_expressions(e1, {id(m.p1): m.p2})
        e3 = replace_expressions(e1, {id(m.p1): m.x})

        self.assertTrue(compare_expressions(e2, m.p2*3))
        self.assertTrue(compare_expressions(e3, ProductExpression([m.x, 3])))
Exemplo n.º 5
0
    def test_npv_negation(self):
        m = ConcreteModel()
        m.p1 = Param(mutable=True)
        m.p2 = Param(mutable=True)
        m.x = Var()

        e1 = -m.p1
        e2 = replace_expressions(e1, {id(m.p1): m.p2})
        e3 = replace_expressions(e1, {id(m.p1): m.x})

        self.assertTrue(compare_expressions(e2, -m.p2))
        self.assertTrue(compare_expressions(e3, NegationExpression([m.x])))
Exemplo n.º 6
0
    def test_npv_sum(self):
        m = ConcreteModel()
        m.p1 = Param(mutable=True)
        m.p2 = Param(mutable=True)
        m.x = Var()

        e1 = m.p1 + 2
        e2 = replace_expressions(e1, {id(m.p1): m.p2})
        e3 = replace_expressions(e1, {id(m.p1): m.x})

        self.assertTrue(compare_expressions(e2, m.p2 + 2))
        self.assertTrue(compare_expressions(e3, m.x + 2))
Exemplo n.º 7
0
 def test_replace_expressions_with_monomial_term(self):
     M = ConcreteModel()
     M.x = Var()
     e = 2.0 * M.x
     substitution_map = {id(M.x): 3.0 * M.x}
     new_e = replace_expressions(e, substitution_map=substitution_map)
     self.assertEqual('6.0*x', str(new_e))
Exemplo n.º 8
0
 def test_replace_expressions_with_monomial_term(self):
     M = ConcreteModel()
     M.x = Var()
     e = 2.0*M.x
     substitution_map = {id(M.x): 3.0*M.x}
     new_e = replace_expressions(e, substitution_map=substitution_map)
     self.assertEqual('6.0*x', str(new_e))
Exemplo n.º 9
0
    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
Exemplo n.º 10
0
    def _apply_to(self, instance):
        cons = self.get_uncertain_components(instance)
        objs = self.get_uncertain_components(instance, component=Objective)

        smap = {}

        for c in cons:
            param = collect_uncparam(c)
            for i in param:
                smap[id(param[i])] = param[i].nominal
            body_nominal = replace_expressions(c.body, smap)
            c.set_value((c.lower, body_nominal, c.upper))

        for o in objs:
            param = collect_uncparam(o)
            for i in param:
                smap[id(param[i])] = param[i].nominal
            expr_nominal = replace_expressions(o.expr, smap)
            o.expr = expr_nominal
Exemplo n.º 11
0
def substitute_ssv_in_dr_constraints(model, constraint):
    '''
    Generate the standard_repn for the dr constraints. Generate new expression with replace_expression to ignore
    the ssv component.
    Then, replace_expression with substitution_map between ssv and the new expression.
    Deactivate or del_component the original dr equation.
    Then, return modified model and do coefficient matching as normal.
    :param model: the working_model
    :param constraint: an equality constraint from the working model identified to be of the form h(x,z,q) = 0.
    :return:
    '''
    dr_eqns = model.util.decision_rule_eqns
    fsv = ComponentSet(model.util.first_stage_variables)
    if not hasattr(model, "dr_substituted_constraints"):
        model.dr_substituted_constraints = ConstraintList()
    for eqn in dr_eqns:
        repn = generate_standard_repn(eqn.body, compute_values=False)
        new_expression = 0
        map_linear_coeff_to_var = [x for x in zip(repn.linear_coefs, repn.linear_vars) if x[1] in ComponentSet(fsv)]
        map_quad_coeff_to_var = [x for x in zip(repn.quadratic_coefs, repn.quadratic_vars) if x[1] in ComponentSet(fsv)]
        if repn.linear_coefs:
            for coeff, var in map_linear_coeff_to_var:
                new_expression += coeff * var
        if repn.quadratic_coefs:
            for coeff, var in map_quad_coeff_to_var:
                new_expression += coeff * var[0] * var[1] # var here is a 2-tuple

        model.no_ssv_dr_expr = Expression(expr=new_expression)
        substitution_map = {}
        substitution_map[id(repn.linear_vars[-1])] = model.no_ssv_dr_expr.expr

    model.dr_substituted_constraints.add(
            replace_expressions(expr=constraint.lower,
                                     substitution_map=substitution_map) ==
            replace_expressions(expr=constraint.body,
                                     substitution_map=substitution_map))

    # === Delete the original constraint
    model.del_component(constraint.name)
    model.del_component("no_ssv_dr_expr")

    return model.dr_substituted_constraints[max(model.dr_substituted_constraints.keys())]
Exemplo n.º 12
0
    def _apply_to(self, instance):
        cons = self.get_uncertain_components(instance)
        objs = self.get_uncertain_components(instance, component=Objective)

        smap = {}

        for c in cons:
            param = collect_uncparam(c)
            for i in param:
                if param[i].nominal is None:
                    raise RuntimeError("Uncertain parameter '{}' does not have"
                                       " a nominal value.".format(
                                           param[i].name))
                smap[id(param[i])] = param[i].nominal
            body_nominal = replace_expressions(c.body, smap)
            c.set_value((c.lower, body_nominal, c.upper))

        for o in objs:
            param = collect_uncparam(o)
            for i in param:
                smap[id(param[i])] = param[i].nominal
            expr_nominal = replace_expressions(o.expr, smap)
            o.expr = expr_nominal
Exemplo n.º 13
0
    def _apply_to(self, instance):
        for c in chain(
                self.get_adjustable_components(instance),
                self.get_adjustable_components(instance, component=Objective)):
            # Collect adjustable var
            adjvar = collect_adjustable(c)
            # Get regular var
            if adjvar.name not in self._adjvar_dict:
                var = Var(adjvar.index_set(), bounds=adjvar._bounds_init_value)
                setattr(instance, adjvar.name + '_nominal', var)
                self._adjvar_dict[adjvar.name] = var
                for i in adjvar:
                    var[i].fixed = adjvar[i].fixed
                    var[i].setlb(adjvar[i].lb)
                    var[i].setub(adjvar[i].ub)
                    var[i].value = adjvar[i].value
            else:
                var = self._adjvar_dict[adjvar.name]
            # Construct substitution map
            sub_map = {id(adjvar[i]): var[i] for i in adjvar}
            # Replace AdjustableVar with Var
            if c.ctype is Objective:
                e_new = replace_expressions(c.expr, substitution_map=sub_map)
                c_new = Objective(expr=e_new, sense=c.sense)
            else:
                e_new = replace_expressions(c.body, substitution_map=sub_map)
                if c.equality:
                    c_new = Constraint(expr=e_new == c.upper)
                else:
                    c_new = Constraint(
                        expr=inequality(c.lower, e_new, c.upper))
            setattr(instance, c.name + '_nominal', c_new)

            self._cons_dict[c.name] = (c, c_new)

            c.deactivate()
Exemplo n.º 14
0
def construct_master_feasibility_problem(model_data, config):
    """
    Construct a slack-variable based master feasibility model.
    Initialize all model variables appropriately, and scale slack variables
    as well.

    Parameters
    ----------
    model_data : MasterProblemData
        Master problem data.
    config : ConfigDict
        PyROS solver config.

    Returns
    -------
    model : ConcreteModel
        Slack variable model.
    """

    model = model_data.master_model.clone()
    for obj in model.component_data_objects(Objective):
        obj.deactivate()
    iteration = model_data.iteration

    # first stage vars are already initialized appropriately.
    # initialize second-stage DOF variables using DR equation expressions
    if model.scenarios[iteration, 0].util.second_stage_variables:
        for blk in model.scenarios[iteration, :]:
            for eq in blk.util.decision_rule_eqns:
                vars_in_dr_eq = ComponentSet(identify_variables(eq.body))
                ssv_set = ComponentSet(blk.util.second_stage_variables)

                # get second-stage var in DR eqn. should only be one var
                ssv_in_dr_eq = [
                    var for var in vars_in_dr_eq if var in ssv_set
                ][0]

                # update var value for initialization
                # fine since DR eqns are f(d) - z == 0 (not z - f(d) == 0)
                ssv_in_dr_eq.set_value(0)
                ssv_in_dr_eq.set_value(eq.body)

    # initialize state vars to previous master solution values
    if iteration != 0:
        stvar_map = get_state_vars(model, [iteration, iteration - 1])
        for current, prev in zip(stvar_map[iteration],
                                 stvar_map[iteration - 1]):
            current.set_value(prev)

    # constraints to which slacks should be added
    # (all the constraints for the current iteration, except the DR eqns)
    targets = []
    for blk in model.scenarios[iteration, :]:
        if blk.util.second_stage_variables:
            dr_eqs = blk.util.decision_rule_eqns
        else:
            dr_eqs = list()

        targets.extend([
            con for con in blk.component_data_objects(
                Constraint, active=True, descend_into=True)
            if con not in dr_eqs
        ])

    # retain original constraint exprs (for slack initialization and scaling)
    pre_slack_con_exprs = ComponentMap([(con, con.body) for con in targets])

    # add slack variables and objective
    # inequalities g(v) <= b become g(v) -- s^-<= b
    # equalities h(v) == b become h(v) -- s^- + s^+ == b
    TransformationFactory("core.add_slack_variables").apply_to(model,
                                                               targets=targets)
    slack_vars = ComponentSet(
        model._core_add_slack_variables.component_data_objects(
            Var, descend_into=True))

    # initialize and scale slack variables
    for con in pre_slack_con_exprs:
        # obtain slack vars in updated constraints
        # and their coefficients (+/-1) in the constraint expression
        repn = generate_standard_repn(con.body)
        slack_var_coef_map = ComponentMap()
        for idx in range(len(repn.linear_vars)):
            var = repn.linear_vars[idx]
            if var in slack_vars:
                slack_var_coef_map[var] = repn.linear_coefs[idx]

        slack_substitution_map = dict()

        for slack_var in slack_var_coef_map:
            # coefficient determines whether the slack is a +ve or -ve slack
            if slack_var_coef_map[slack_var] == -1:
                con_slack = max(0, value(pre_slack_con_exprs[con]))
            else:
                con_slack = max(0, -value(pre_slack_con_exprs[con]))

            # initialize slack var, evaluate scaling coefficient
            scaling_coeff = 1
            slack_var.set_value(con_slack)

            # update expression replacement map
            slack_substitution_map[id(slack_var)] = (scaling_coeff * slack_var)

        # finally, scale slack(s)
        con.set_value((
            replace_expressions(con.lower, slack_substitution_map),
            replace_expressions(con.body, slack_substitution_map),
            replace_expressions(con.upper, slack_substitution_map),
        ))

    return model
Exemplo n.º 15
0
    def generate_structured_model(self):
        """
        Using the community map and the original model used to create this community map, we will create
        structured_model, which will be based on the original model but will place variables, constraints, and
        objectives into or outside of various blocks (communities) based on the community map.

        Returns
        -------
        structured_model: Block
            a Pyomo model that reflects the nature of the community map
        """

        # Initialize a new model (structured_model) which will contain variables and constraints in blocks based on
        # their respective communities within the CommunityMap
        structured_model = ConcreteModel()

        # Create N blocks (where N is the number of communities found within the model)
        structured_model.b = Block([0, len(self.community_map) - 1,
                                    1])  # values given for (start, stop, step)

        # Initialize a ComponentMap that will map a variable from the model (for example, old_model.x1) used to
        # create the CommunityMap to a list of variables in various blocks that were created based on this
        # variable (for example, [structured_model.b[0].x1, structured_model.b[3].x1])
        blocked_variable_map = ComponentMap()
        # Example key-value pair -> {original_model.x1 : [structured_model.b[0].x1, structured_model.b[3].x1]}

        # TODO - Consider changing structure of the next two for loops to be more efficient (maybe loop through
        #  constraints and add variables as you go) (but note that disconnected variables would be
        #  missed with this strategy)

        # First loop through community_map to add all the variables to structured_model before we add constraints
        # that use those variables
        for community_key, community in self.community_map.items():
            _, variables_in_community = community

            # Loop through all of the variables (from the original model) in the given community
            for stored_variable in variables_in_community:
                # Construct a new_variable whose attributes are determined by querying the variable from the
                # original model
                new_variable = Var(domain=stored_variable.domain,
                                   bounds=stored_variable.bounds)

                # Add this new_variable to its block/community and name it using the string of the variable from the
                # original model
                structured_model.b[community_key].add_component(
                    str(stored_variable), new_variable)

                # Since there could be multiple variables 'x1' (such as
                # structured_model.b[0].x1, structured_model.b[3].x1, etc), we need to create equality constraints
                # for all of the variables 'x1' within structured_model (this is the purpose of blocked_variable_map)

                # Here we update blocked_variable_map to keep track of what equality constraints need to be made
                variable_in_new_model = structured_model.find_component(
                    new_variable)
                blocked_variable_map[
                    stored_variable] = blocked_variable_map.get(
                        stored_variable, []) + [variable_in_new_model]

        # Now that we have all of our variables within the model, we will initialize a dictionary that used to
        # replace variables within constraints to other variables (in our case, this will convert variables from the
        # original model into variables from the new model (structured_model))
        replace_variables_in_expression_map = dict()

        # Loop through community_map again, this time to add constraints (with replaced variables)
        for community_key, community in self.community_map.items():
            constraints_in_community, _ = community

            # Loop through all of the constraints (from the original model) in the given community
            for stored_constraint in constraints_in_community:

                # Now, loop through all of the variables within the given constraint expression
                for variable_in_stored_constraint in identify_variables(
                        stored_constraint.expr):

                    # Loop through each of the "blocked" variables that a variable is mapped to and update
                    # replace_variables_in_expression_map if a variable has a "blocked" form in the given community

                    # What this means is that if we are looping through constraints in community 0, then it would be
                    # best to change a variable x1 into b[0].x1 as opposed to b[2].x1 or b[5].x1 (assuming all of these
                    # blocked versions of the variable x1 exist (which depends on the community map))

                    variable_in_current_block = False
                    for blocked_variable in blocked_variable_map[
                            variable_in_stored_constraint]:
                        if 'b[%d]' % community_key in str(blocked_variable):
                            # Update replace_variables_in_expression_map accordingly
                            replace_variables_in_expression_map[
                                id(variable_in_stored_constraint
                                   )] = blocked_variable
                            variable_in_current_block = True

                    if not variable_in_current_block:
                        # Create a version of the given variable outside of blocks then add it to
                        # replace_variables_in_expression_map

                        new_variable = Var(
                            domain=variable_in_stored_constraint.domain,
                            bounds=variable_in_stored_constraint.bounds)

                        # Add the new variable just as we did above (but now it is not in any blocks)
                        structured_model.add_component(
                            str(variable_in_stored_constraint), new_variable)

                        # Update blocked_variable_map to keep track of what equality constraints need to be made
                        variable_in_new_model = structured_model.find_component(
                            new_variable)
                        blocked_variable_map[
                            variable_in_stored_constraint] = blocked_variable_map.get(
                                variable_in_stored_constraint,
                                []) + [variable_in_new_model]

                        # Update replace_variables_in_expression_map accordingly
                        replace_variables_in_expression_map[
                            id(variable_in_stored_constraint
                               )] = variable_in_new_model

                # TODO - Is there a better way to check whether something is actually an objective? (as done below)
                # Check to see whether 'stored_constraint' is actually an objective (since constraints and objectives
                # grouped together)
                if self.with_objective and isinstance(
                        stored_constraint, (_GeneralObjectiveData, Objective)):
                    # If the constraint is actually an objective, we add it to the block as an objective
                    new_objective = Objective(expr=replace_expressions(
                        stored_constraint.expr,
                        replace_variables_in_expression_map))
                    structured_model.b[community_key].add_component(
                        str(stored_constraint), new_objective)

                else:
                    # Construct a constraint based on the expression within stored_constraint and the dict we have
                    # created for the purpose of replacing the variables within the constraint expression
                    new_constraint = Constraint(expr=replace_expressions(
                        stored_constraint.expr,
                        replace_variables_in_expression_map))

                    # Add this new constraint to the corresponding community/block with its name as the string of the
                    # constraint from the original model
                    structured_model.b[community_key].add_component(
                        str(stored_constraint), new_constraint)

        # If with_objective was set to False, that means we might have missed an objective function within the
        # original model
        if not self.with_objective:
            # Construct a new dictionary for replacing the variables (replace_variables_in_objective_map) which will
            # be specific to the variables in the objective function, since there is the possibility that the
            # objective contains variables we have not yet seen (and thus not yet added to our new model)
            for objective_function in self.model.component_data_objects(
                    ctype=Objective,
                    active=self.use_only_active_components,
                    descend_into=True):

                for variable_in_objective in identify_variables(
                        objective_function):
                    # Add all of the variables in the objective function (not within any blocks)

                    # Check to make sure a form of the variable has not already been made outside of the blocks
                    if structured_model.find_component(
                            str(variable_in_objective)) is None:

                        new_variable = Var(domain=variable_in_objective.domain,
                                           bounds=variable_in_objective.bounds)
                        structured_model.add_component(
                            str(variable_in_objective), new_variable)

                        # Again we update blocked_variable_map to keep track of what
                        # equality constraints need to be made
                        variable_in_new_model = structured_model.find_component(
                            new_variable)
                        blocked_variable_map[
                            variable_in_objective] = blocked_variable_map.get(
                                variable_in_objective,
                                []) + [variable_in_new_model]

                        # Update the dictionary that we will use to replace the variables
                        replace_variables_in_expression_map[id(
                            variable_in_objective)] = variable_in_new_model

                    else:
                        for version_of_variable in blocked_variable_map[
                                variable_in_objective]:
                            if 'b[' not in str(version_of_variable):
                                replace_variables_in_expression_map[
                                    id(variable_in_objective
                                       )] = version_of_variable

                # Now we will construct a new objective function based on the one from the original model and then
                # add it to the new model just as we have done before
                new_objective = Objective(expr=replace_expressions(
                    objective_function.expr,
                    replace_variables_in_expression_map))
                structured_model.add_component(str(objective_function),
                                               new_objective)

        # Now, we need to create equality constraints for all of the different "versions" of a variable (such
        # as x1, b[0].x1, b[2].x2, etc.)

        # Create a constraint list for the equality constraints
        structured_model.equality_constraint_list = ConstraintList(
            doc="Equality Constraints for the different "
            "forms of a given variable")

        # Loop through blocked_variable_map and create constraints accordingly
        for variable, duplicate_variables in blocked_variable_map.items():
            # variable -> variable from the original model
            # duplicate_variables -> list of variables in the new model

            # Create a list of all the possible equality constraints that need to be made
            equalities_to_make = combinations(duplicate_variables, 2)

            # Loop through the list of two-variable tuples and create an equality constraint for those two variables
            for variable_1, variable_2 in equalities_to_make:
                structured_model.equality_constraint_list.add(
                    expr=variable_1 == variable_2)

        # Return 'structured_model', which is essentially identical to the original model but now has all of the
        # variables, constraints, and objectives placed into blocks based on the nature of the CommunityMap

        return structured_model
Exemplo n.º 16
0
    def _apply_to(self, instance):
        for c in chain(
                self.get_adjustable_components(instance),
                self.get_adjustable_components(instance, component=Objective)):
            # Collect adjustable vars and uncparams
            adjvar = collect_adjustable(c)
            if id(adjvar) not in self._adjvars:
                self._adjvars[id(adjvar)] = adjvar
            # Create variables for LDR coefficients
            for i in adjvar:
                for u in adjvar[i].uncparams:
                    parent = u.parent_component()
                    if (adjvar.name, parent.name) not in self._coef_dict:
                        coef = Var(adjvar.index_set(), parent.index_set())
                        coef_name = adjvar.name + '_' + parent.name + '_coef'
                        setattr(instance, coef_name, coef)
                        self._coef_dict[adjvar.name, parent.name] = coef

            # Create substitution map
            def coef(u):
                return self._coef_dict[adjvar.name, u.parent_component().name]

            def gen_index(u):
                if hasattr(u, 'index'):
                    yield u.index()
                else:
                    for i in u:
                        yield i

            sub_map = {
                id(adjvar[i]): sum(u.parent_component()[j] * coef(u)[i, j]
                                   for u in adjvar[i].uncparams
                                   for j in gen_index(u))
                for i in adjvar
            }
            self._expr_dict[adjvar.name] = sub_map
            # Replace AdjustableVar by LDR
            # Objectives
            if c.ctype is Objective:
                e_new = replace_expressions(c.expr, substitution_map=sub_map)
                c_new = Objective(expr=e_new, sense=c.sense)
                setattr(instance, c.name + '_ldr', c_new)
            # Constraints
            elif c.ctype is Constraint:
                e_new = replace_expressions(c.body, substitution_map=sub_map)

                if c.equality:
                    repn = self.generate_repn_param(instance, e_new)

                    c_new = ConstraintList()
                    setattr(instance, c.name + '_ldr', c_new)
                    # Check if repn.constant is an expression
                    cons = repn.constant
                    if cons.__class__ in nonpyomo_leaf_types:
                        if cons != 0:
                            raise ValueError("Can't reformulate constraint {} "
                                             "with numeric constant "
                                             "{}".format(c.name, cons))
                    elif cons.is_potentially_variable():
                        c_new.add(cons == 0)
                    else:
                        raise ValueError("Can't reformulate constraint {} with"
                                         " constant "
                                         "{}".format(c.name, cons))
                    # Add constraints for each uncparam
                    for coef in repn.linear_coefs:
                        c_new.add(coef == 0)
                    for coef in repn.quadratic_coefs:
                        c_new.add(coef == 0)
                else:

                    def c_rule(x):
                        return (c.lower, e_new, c.upper)

                    c_new = Constraint(rule=c_rule)
                    setattr(instance, c.name + '_ldr', c_new)

            c.deactivate()

        # Add constraints for bounds on AdjustableVar
        for name, sub_map in self._expr_dict.items():
            adjvar = instance.find_component(name)
            cl = ConstraintList()
            setattr(instance, adjvar.name + '_bounds', cl)
            for i in adjvar:
                if adjvar[i].has_lb():
                    cl.add(adjvar[i].lb <= sub_map[id(adjvar[i])])
                if adjvar[i].has_ub():
                    cl.add(adjvar[i].ub >= sub_map[id(adjvar[i])])
Exemplo n.º 17
0
    def _reformulate(self,
                     c,
                     param,
                     uncset,
                     counterpart,
                     initialize_wolfe=False):
        """
        Reformulate an uncertain constraint or objective

            c: Constraint or Objective
            param: UncParam
            uncset: UncSet
            counterpart: Block

        """

        # Only proceed if uncertainty set is WarpedGPSet
        from rogp.util.numpy import _pyomo_to_np, _to_np_obj_array

        repn = self.generate_repn_param(c)

        assert repn.is_linear(), ("Only constraints which are linear in "
                                  "the uncertain parameters are valid for "
                                  "uncertainty set WarpedGPSet.")

        # Constraint may contain subset of elements of UncParam
        index_set = [p.index() for p in repn.linear_vars]
        x = [[xi] for xi in repn.linear_coefs]
        x = _to_np_obj_array(x)

        # Set up Block for Wolfe duality constraints
        b = counterpart
        # Set up extra variables
        b.y = Var(param.index_set())
        # Set bounds for extra variables based on UncParam bounds
        for i in param:
            lb, ub = param[i].lb, param[i].ub
            b.y[i].setlb(lb)
            b.y[i].setub(ub)
            if ((lb is not None and lb is not float('-inf'))
                    and (ub is not None and ub is not float('inf'))):
                b.y[i].value = (ub + lb) / 2
        y = _pyomo_to_np(b.y, ind=index_set)
        # Setup dual vars based on sense
        if c.ctype is Constraint:
            if c.has_ub() and not c.has_lb():
                b.u = Var(within=NonPositiveReals)
            elif not c.has_ub() and c.has_lb():
                b.u = Var(within=NonNegativeReals)
            else:
                raise RuntimeError(
                    "Uncertain constraints with 'WarpedGPSet' "
                    "currently only support either an upper or a "
                    "lower bound, not both.")
        elif c.ctype is Objective:
            if c.sense is maximize:
                b.u = Var(within=NonPositiveReals)
            else:
                b.u = Var(within=NonNegativeReals)
        u = _pyomo_to_np(b.u)

        # Primal constraint
        sub_map = {id(param[i]): b.y[i] for i in param}
        if c.ctype is Constraint:
            e_new = replace_expressions(c.body, substitution_map=sub_map)
            b.primal = Constraint(rule=lambda x: (c.lower, e_new, c.upper))
        else:
            e_new = replace_expressions(c.expr, substitution_map=sub_map)
            b.primal = Objective(expr=e_new, sense=c.sense)

        # Calculate matrices
        gp = uncset.gp
        var = uncset.var
        if type(var) is dict:
            z = [var[i] for i in index_set]
            z = _to_np_obj_array(z)
        else:
            var = var[0]
            assert var.index_set() == param.index_set(), (
                "Index set of `UncParam` and `var` in `WarpedGPSet` "
                "should be the same. Alternatively use "
                "var = {index: [list of vars]}")
            z = _pyomo_to_np(var, ind=index_set)

        Sig = gp.predict_cov_latent(z)
        mu = gp.predict_mu_latent(z)
        dHinv = 1 / gp.warp_deriv(y)
        dHinv = np.diag(dHinv[:, 0])
        hz = gp.warp(y)

        LHS = np.matmul(Sig, dHinv)
        LHS = np.matmul(LHS, x)
        RHS = LHS
        LHS = LHS + 2 * u * (hz - mu)
        # Add stationarity condition
        b.stationarity = ConstraintList()
        for lhs in np.nditer(LHS, ['refs_ok']):
            b.stationarity.add(lhs.item() == 0)
        RHS = np.matmul(dHinv, RHS)
        rhs = np.matmul(x.T, RHS)[0, 0]
        lhs = 4 * u[0, 0]**2 * uncset.F
        # Set consistent initial value for u (helps convergence)
        if initialize_wolfe:
            u0 = np.sqrt(rhs() / 4 / uncset.F)
            if u[0, 0].ub == 0:
                u[0, 0].value = -u0
            elif u[0, 0].lb == 0:
                u[0, 0].value = u0
        # Dual variable constraint
        b.dual = Constraint(expr=lhs == rhs)
Exemplo n.º 18
0
    def SDM(self, model_name):
        '''  Algorithm 2 in Boland et al. (with small tweaks)
        '''
        mip = self.local_subproblems[model_name]
        qp = self.local_QP_subproblems[model_name]

        # Set the QP dual weights to the correct values If we are bundling, we
        # initialize the QP dual weights to be the dual weights associated with
        # the first scenario in the bundle (this should be okay, because each
        # scenario in the bundle has the same dual weights, analytically--maybe
        # a numerical problem).
        arb_scen_mip = self.local_scenarios[mip.scen_list[0]] \
                       if self.bundling else mip
        for (node_name, ix) in arb_scen_mip._nonant_indexes:
            qp._Ws[node_name, ix]._value = \
                arb_scen_mip._Ws[node_name, ix].value

        alpha = self.FW_options['FW_weight']
        # Algorithm 3 line 6
        xt = {
            ndn_i: (1 - alpha) * pyo.value(arb_scen_mip._xbars[ndn_i]) +
            alpha * pyo.value(xvar)
            for ndn_i, xvar in arb_scen_mip._nonant_indexes.items()
        }

        for itr in range(self.FW_options['FW_iter_limit']):
            # Algorithm 2 line 4
            mip_source = mip.scen_list if self.bundling else [model_name]
            for scenario_name in mip_source:
                scen_mip = self.local_scenarios[scenario_name]
                for ndn_i, nonant in scen_mip._nonant_indexes.items():
                    x_source = xt[ndn_i] if itr==0 \
                               else nonant._value
                    scen_mip._Ws[ndn_i]._value = (
                        qp._Ws[ndn_i]._value + scen_mip._PHrho[ndn_i]._value *
                        (x_source - scen_mip._xbars[ndn_i]._value))

            # Algorithm 2 line 5
            if (sputils.is_persistent(mip._solver_plugin)):
                mip_obj = find_active_objective(mip, True)
                mip._solver_plugin.set_objective(mip_obj)
            mip_results = mip._solver_plugin.solve(mip)
            self._check_solve(mip_results, model_name + ' (MIP)')

            # Algorithm 2 lines 6--8
            obj = find_active_objective(mip, True)
            if (itr == 0):
                dual_bound = pyo.value(obj)

            # Algorithm 2 line 9 (compute \Gamma^t)
            val0 = pyo.value(obj)
            new = replace_expressions(obj.expr, mip.mip_to_qp)
            val1 = pyo.value(new)
            obj.expr = replace_expressions(new, qp.qp_to_mip)
            if abs(val0) > 1e-9:
                stop_check = (val1 - val0) / abs(
                    val0)  # \Gamma^t in Boland, but normalized
            else:
                stop_check = val1 - val0  # \Gamma^t in Boland
            stop_check_tol = self.FW_options["stop_check_tol"]\
                             if "stop_check_tol" in self.FW_options else 1e-4
            if (self.is_minimizing and stop_check < -stop_check_tol):
                print(
                    'Warning (fwph): convergence quantity Gamma^t = '
                    '{sc:.2e} (should be non-negative)'.format(sc=stop_check))
                print('Try decreasing the MIP gap tolerance and re-solving')
            elif (not self.is_minimizing and stop_check > stop_check_tol):
                print(
                    'Warning (fwph): convergence quantity Gamma^t = '
                    '{sc:.2e} (should be non-positive)'.format(sc=stop_check))
                print('Try decreasing the MIP gap tolerance and re-solving')

            self._add_QP_column(model_name)
            if (sputils.is_persistent(qp._QP_solver_plugin)):
                qp_obj = find_active_objective(qp, True)
                qp._QP_solver_plugin.set_objective(qp_obj)
            qp_results = qp._QP_solver_plugin.solve(qp)
            self._check_solve(qp_results, model_name + ' (QP)')

            if (stop_check < self.FW_options['FW_conv_thresh']):
                break

        # Re-set the mip._Ws so that the QP objective
        # is correct in the next major iteration
        mip_source = mip.scen_list if self.bundling else [model_name]
        for scenario_name in mip_source:
            scen_mip = self.local_scenarios[scenario_name]
            for (node_name, ix) in scen_mip._nonant_indexes:
                scen_mip._Ws[node_name, ix]._value = \
                    qp._Ws[node_name, ix]._value

        return dual_bound
Exemplo n.º 19
0
def coefficient_matching(model, constraint, uncertain_params, config):
    '''
    :param model: master problem model
    :param constraint: the constraint from the master problem model
    :param uncertain_params: the list of uncertain parameters
    :param first_stage_variables: the list of effective first-stage variables (includes ssv if decision_rule_order = 0)
    :return: True if the coefficient matching was successful, False if its proven robust_infeasible due to
             constraints of the form 1 == 0
    '''
    # === Returned flags
    successful_matching = True
    robust_infeasible = False

    # === Efficiency for q_LB = q_UB
    actual_uncertain_params = []

    for i in range(len(uncertain_params)):
        if not is_certain_parameter(uncertain_param_index=i, config=config):
            actual_uncertain_params.append(uncertain_params[i])

    # === Add coefficient matching constraint list
    if not hasattr(model, "coefficient_matching_constraints"):
        model.coefficient_matching_constraints = ConstraintList()
    if not hasattr(model, "swapped_constraints"):
        model.swapped_constraints = ConstraintList()

    variables_in_constraint = ComponentSet(identify_variables(constraint.expr))
    params_in_constraint = ComponentSet(identify_mutable_parameters(constraint.expr))
    first_stage_variables = model.util.first_stage_variables
    second_stage_variables = model.util.second_stage_variables

    # === Determine if we need to do DR expression/ssv substitution to
    #     make h(x,z,q) == 0 into h(x,d,q) == 0 (which is just h(x,q) == 0)
    if all(v in ComponentSet(first_stage_variables) for v in variables_in_constraint) and \
            any(q in ComponentSet(actual_uncertain_params) for q in params_in_constraint):
        # h(x, q) == 0
        pass
    elif all(v in ComponentSet(first_stage_variables + second_stage_variables) for v in variables_in_constraint) and \
            any(q in ComponentSet(actual_uncertain_params) for q in params_in_constraint):
        constraint = substitute_ssv_in_dr_constraints(model=model, constraint=constraint)
        variables_in_constraint = ComponentSet(identify_variables(constraint.expr))
        params_in_constraint = ComponentSet(identify_mutable_parameters(constraint.expr))
    else:
        pass

    if all(v in ComponentSet(first_stage_variables) for v in variables_in_constraint) and \
            any(q in ComponentSet(actual_uncertain_params) for q in params_in_constraint):

        # Swap param objects for variable objects in this constraint
        model.param_set = []
        for i in range(len(list(variables_in_constraint))):
            # Initialize Params to non-zero value due to standard_repn bug
            model.add_component("p_%s" % i, Param(initialize=1, mutable=True))
            model.param_set.append(getattr(model, "p_%s" % i))

        model.variable_set = []
        for i in range(len(list(actual_uncertain_params))):
            model.add_component("x_%s" % i, Var(initialize=1))
            model.variable_set.append(getattr(model, "x_%s" % i))

        original_var_to_param_map = list(zip(list(variables_in_constraint), model.param_set))
        original_param_to_vap_map = list(zip(list(actual_uncertain_params), model.variable_set))

        var_to_param_substitution_map_forward = {}
        # Separation problem initialized to nominal uncertain parameter values
        for var, param in original_var_to_param_map:
            var_to_param_substitution_map_forward[id(var)] = param

        param_to_var_substitution_map_forward = {}
        # Separation problem initialized to nominal uncertain parameter values
        for param, var in original_param_to_vap_map:
            param_to_var_substitution_map_forward[id(param)] = var

        var_to_param_substitution_map_reverse = {}
        # Separation problem initialized to nominal uncertain parameter values
        for var, param in original_var_to_param_map:
            var_to_param_substitution_map_reverse[id(param)] = var

        param_to_var_substitution_map_reverse = {}
        # Separation problem initialized to nominal uncertain parameter values
        for param, var in original_param_to_vap_map:
            param_to_var_substitution_map_reverse[id(var)] = param

        model.swapped_constraints.add(
            replace_expressions(
                expr=replace_expressions(expr=constraint.lower,
                                         substitution_map=param_to_var_substitution_map_forward),
                substitution_map=var_to_param_substitution_map_forward) ==
            replace_expressions(
                expr=replace_expressions(expr=constraint.body,
                                         substitution_map=param_to_var_substitution_map_forward),
                substitution_map=var_to_param_substitution_map_forward))

        swapped = model.swapped_constraints[max(model.swapped_constraints.keys())]

        val = generate_standard_repn(swapped.body, compute_values=False)

        if val.constant is not None:
            if type(val.constant) not in native_types:
                temp_expr = replace_expressions(val.constant, substitution_map=var_to_param_substitution_map_reverse)
                if temp_expr.is_potentially_variable():
                    model.coefficient_matching_constraints.add(expr=temp_expr == 0)
                elif math.isclose(value(temp_expr), 0, rel_tol=COEFF_MATCH_REL_TOL, abs_tol=COEFF_MATCH_ABS_TOL):
                    pass
                else:
                    successful_matching = False
                    robust_infeasible = True
            elif math.isclose(value(val.constant), 0, rel_tol=COEFF_MATCH_REL_TOL, abs_tol=COEFF_MATCH_ABS_TOL):
                pass
            else:
                successful_matching = False
                robust_infeasible = True
        if val.linear_coefs is not None:
            for coeff in val.linear_coefs:
                if type(coeff) not in native_types:
                    temp_expr = replace_expressions(coeff, substitution_map=var_to_param_substitution_map_reverse)
                    if temp_expr.is_potentially_variable():
                        model.coefficient_matching_constraints.add(expr=temp_expr == 0)
                    elif math.isclose(value(temp_expr), 0, rel_tol=COEFF_MATCH_REL_TOL, abs_tol=COEFF_MATCH_ABS_TOL):
                        pass
                    else:
                        successful_matching = False
                        robust_infeasible = True
                elif math.isclose(value(coeff), 0, rel_tol=COEFF_MATCH_REL_TOL, abs_tol=COEFF_MATCH_ABS_TOL):
                    pass
                else:
                    successful_matching = False
                    robust_infeasible = True
        if val.quadratic_coefs:
            for coeff in val.quadratic_coefs:
                if type(coeff) not in native_types:
                    temp_expr = replace_expressions(coeff, substitution_map=var_to_param_substitution_map_reverse)
                    if temp_expr.is_potentially_variable():
                        model.coefficient_matching_constraints.add(expr=temp_expr == 0)
                    elif math.isclose(value(temp_expr), 0, rel_tol=COEFF_MATCH_REL_TOL, abs_tol=COEFF_MATCH_ABS_TOL):
                        pass
                    else:
                        successful_matching = False
                        robust_infeasible = True
                elif math.isclose(value(coeff), 0, rel_tol=COEFF_MATCH_REL_TOL, abs_tol=COEFF_MATCH_ABS_TOL):
                    pass
                else:
                    successful_matching = False
                    robust_infeasible = True
        if val.nonlinear_expr is not None:
            successful_matching = False
            robust_infeasible = False

        if successful_matching:
            model.util.h_x_q_constraints.add(constraint)

    for i in range(len(list(variables_in_constraint))):
        model.del_component("p_%s" % i)

    for i in range(len(list(params_in_constraint))):
        model.del_component("x_%s" % i)

    model.del_component("swapped_constraints")
    model.del_component("swapped_constraints_index")

    return successful_matching, robust_infeasible
Exemplo n.º 20
0
    def _parse_sol(self):
        solve_vars = self._writer.get_ordered_vars()
        solve_cons = self._writer.get_ordered_cons()
        results = Results()

        f = open(self._filename + '.sol', 'r')
        all_lines = list(f.readlines())
        f.close()

        termination_line = all_lines[1]
        if 'Optimal Solution Found' in termination_line:
            results.termination_condition = TerminationCondition.optimal
        elif 'Problem may be infeasible' in termination_line:
            results.termination_condition = TerminationCondition.infeasible
        elif 'problem might be unbounded' in termination_line:
            results.termination_condition = TerminationCondition.unbounded
        elif 'Maximum Number of Iterations Exceeded' in termination_line:
            results.termination_condition = TerminationCondition.maxIterations
        elif 'Maximum CPU Time Exceeded' in termination_line:
            results.termination_condition = TerminationCondition.maxTimeLimit
        else:
            results.termination_condition = TerminationCondition.unknown

        n_cons = len(solve_cons)
        n_vars = len(solve_vars)
        dual_lines = all_lines[12:12 + n_cons]
        primal_lines = all_lines[12 + n_cons:12 + n_cons + n_vars]

        rc_upper_info_line = all_lines[12 + n_cons + n_vars + 1]
        assert rc_upper_info_line.startswith('suffix')
        n_rc_upper = int(rc_upper_info_line.split()[2])
        assert 'ipopt_zU_out' in all_lines[12 + n_cons + n_vars + 2]
        upper_rc_lines = all_lines[12 + n_cons + n_vars + 3:12 + n_cons +
                                   n_vars + 3 + n_rc_upper]

        rc_lower_info_line = all_lines[12 + n_cons + n_vars + 3 + n_rc_upper]
        assert rc_lower_info_line.startswith('suffix')
        n_rc_lower = int(rc_lower_info_line.split()[2])
        assert 'ipopt_zL_out' in all_lines[12 + n_cons + n_vars + 3 +
                                           n_rc_upper + 1]
        lower_rc_lines = all_lines[12 + n_cons + n_vars + 3 + n_rc_upper +
                                   2:12 + n_cons + n_vars + 3 + n_rc_upper +
                                   2 + n_rc_lower]

        self._dual_sol = dict()
        self._primal_sol = ComponentMap()
        self._reduced_costs = ComponentMap()

        for ndx, dual in enumerate(dual_lines):
            dual = float(dual)
            con = solve_cons[ndx]
            self._dual_sol[con] = dual

        for ndx, primal in enumerate(primal_lines):
            primal = float(primal)
            var = solve_vars[ndx]
            self._primal_sol[var] = primal

        for rcu_line in upper_rc_lines:
            split_line = rcu_line.split()
            var_ndx = int(split_line[0])
            rcu = float(split_line[1])
            var = solve_vars[var_ndx]
            self._reduced_costs[var] = rcu

        for rcl_line in lower_rc_lines:
            split_line = rcl_line.split()
            var_ndx = int(split_line[0])
            rcl = float(split_line[1])
            var = solve_vars[var_ndx]
            if var in self._reduced_costs:
                if abs(rcl) > abs(self._reduced_costs[var]):
                    self._reduced_costs[var] = rcl
            else:
                self._reduced_costs[var] = rcl

        for var in solve_vars:
            if var not in self._reduced_costs:
                self._reduced_costs[var] = 0

        if results.termination_condition == TerminationCondition.optimal and self.config.load_solution:
            for v, val in self._primal_sol.items():
                v.set_value(val, skip_validation=True)

            if self._writer.get_active_objective() is None:
                results.best_feasible_objective = None
            else:
                results.best_feasible_objective = value(
                    self._writer.get_active_objective().expr)
        elif results.termination_condition == TerminationCondition.optimal:
            if self._writer.get_active_objective() is None:
                results.best_feasible_objective = None
            else:
                obj_expr_evaluated = replace_expressions(
                    self._writer.get_active_objective().expr,
                    substitution_map={
                        id(v): val
                        for v, val in self._primal_sol.items()
                    },
                    descend_into_named_expressions=True,
                    remove_named_expressions=True)
                results.best_feasible_objective = value(obj_expr_evaluated)
        elif self.config.load_solution:
            raise RuntimeError(
                'A feasible solution was not found, so no solution can be loaded.'
                'Please set opt.config.load_solution=False and check '
                'results.termination_condition and '
                'resutls.best_feasible_objective before loading a solution.')

        results.solution_loader = PersistentSolutionLoader(solver=self)

        return results
Exemplo n.º 21
0
def copy_relaxation_with_local_data(rel, old_var_to_new_var_map):
    """
    This function copies a relaxation object with new variables.
    Note that only what can be set through the set_input and build
    methods are copied. For example, piecewise partitioning points
    are not copied.

    Parameters
    ----------
    rel: coramin.relaxations.relaxations_base.BaseRelaxationData
        The relaxation to be copied
    old_var_to_new_var_map: dict
        Map from the original variable id to the new variable

    Returns
    -------
    rel: coramin.relaxations.relaxations_base.BaseRelaxationData
        The copy of rel with new variables
    """
    if isinstance(rel, PWXSquaredRelaxationData):
        new_x = old_var_to_new_var_map[id(rel.get_rhs_vars()[0])]
        new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())]
        new_rel = PWXSquaredRelaxation(concrete=True)
        new_rel.set_input(x=new_x,
                          aux_var=new_aux_var,
                          pw_repn=rel._pw_repn,
                          use_linear_relaxation=rel.use_linear_relaxation,
                          relaxation_side=rel.relaxation_side)
    elif isinstance(rel, PWArctanRelaxationData):
        new_x = old_var_to_new_var_map[id(rel.get_rhs_vars()[0])]
        new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())]
        new_rel = PWArctanRelaxation(concrete=True)
        new_rel.set_input(x=new_x,
                          aux_var=new_aux_var,
                          pw_repn=rel._pw_repn,
                          relaxation_side=rel.relaxation_side,
                          use_linear_relaxation=rel.use_linear_relaxation)
    elif isinstance(rel, PWSinRelaxationData):
        new_x = old_var_to_new_var_map[id(rel.get_rhs_vars()[0])]
        new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())]
        new_rel = PWSinRelaxation(concrete=True)
        new_rel.set_input(x=new_x,
                          aux_var=new_aux_var,
                          pw_repn=rel._pw_repn,
                          relaxation_side=rel.relaxation_side,
                          use_linear_relaxation=rel.use_linear_relaxation)
    elif isinstance(rel, PWCosRelaxationData):
        new_x = old_var_to_new_var_map[id(rel.get_rhs_vars()[0])]
        new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())]
        new_rel = PWCosRelaxation(concrete=True)
        new_rel.set_input(x=new_x,
                          aux_var=new_aux_var,
                          pw_repn=rel._pw_repn,
                          relaxation_side=rel.relaxation_side,
                          use_linear_relaxation=rel.use_linear_relaxation)
    elif isinstance(rel, PWUnivariateRelaxationData):
        new_x = old_var_to_new_var_map[id(rel.get_rhs_vars()[0])]
        new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())]
        new_f_x_expr = replace_expressions(
            rel.get_rhs_expr(),
            substitution_map=old_var_to_new_var_map,
            remove_named_expressions=True)
        new_rel = PWUnivariateRelaxation(concrete=True)
        if rel.is_rhs_convex():
            shape = FunctionShape.CONVEX
        elif rel.is_rhs_concave():
            shape = FunctionShape.CONCAVE
        else:
            shape = FunctionShape.UNKNOWN
        new_rel.set_input(x=new_x,
                          aux_var=new_aux_var,
                          shape=shape,
                          f_x_expr=new_f_x_expr,
                          pw_repn=rel._pw_repn,
                          relaxation_side=rel.relaxation_side,
                          use_linear_relaxation=rel.use_linear_relaxation)
    elif isinstance(rel, PWMcCormickRelaxationData):
        rhs_vars = rel.get_rhs_vars()
        old_x1 = rhs_vars[0]
        old_x2 = rhs_vars[1]
        new_x1 = old_var_to_new_var_map[id(old_x1)]
        new_x2 = old_var_to_new_var_map[id(old_x2)]
        new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())]
        new_rel = PWMcCormickRelaxation(concrete=True)
        new_rel.set_input(x1=new_x1,
                          x2=new_x2,
                          aux_var=new_aux_var,
                          relaxation_side=rel.relaxation_side)
    elif isinstance(rel, MultivariateRelaxationData):
        new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())]
        if rel.is_rhs_convex():
            shape = FunctionShape.CONVEX
        elif rel.is_rhs_concave():
            shape = FunctionShape.CONCAVE
        else:
            shape = FunctionShape.UNKNOWN
        new_f_x_expr = replace_expressions(
            rel.get_rhs_expr(),
            substitution_map=old_var_to_new_var_map,
            remove_named_expressions=True)
        new_rel = MultivariateRelaxation(concrete=True)
        new_rel.set_input(aux_var=new_aux_var,
                          shape=shape,
                          f_x_expr=new_f_x_expr,
                          use_linear_relaxation=rel.use_linear_relaxation)
    else:
        raise ValueError('Unrecognized relaxation: {0}'.format(str(type(rel))))

    return new_rel