def relax_variable(self, problem, variable): return Variable( variable.name, problem.lower_bound(variable), problem.upper_bound(variable), Domain.REAL, )
def relax_objective(self, problem, objective): self._objective_count += 1 if self._objective_count > 1: raise ValueError( 'Apply LinearRelaxation to multiobjective problem') new_variable = Variable('_objvar', None, None, Domain.REAL) new_objective_expr = LinearExpression([new_variable], [1.0], 0.0) new_objective = Objective( objective.name, new_objective_expr, objective.original_sense, ) under_result = self.relax_expression(problem, objective.root_expr) new_cons_expr = SumExpression([ under_result.expression, LinearExpression([new_variable], [-1.0], 0.0), ]) new_cons = Constraint('_obj_{}'.format(objective.name), new_cons_expr, None, 0.0) under_result.constraints.append(new_cons) return RelaxationResult(new_objective, under_result.constraints)
def variables(draw): domain = draw( st.sampled_from( Domain.REALS, Domain.INTEGERS, Domain.BINARY, )) lower_bound = draw(st.one_of(st.none(), st.floats())) upper_bound = draw(st.one_of(st.none(), st.floats(min_value=lower_bound))) return Variable(lower_bound, upper_bound, domain)
def _insert_variable(self, expr, problem, relaxed_problem, use_problem_bounds=False): assert expr.expression_type == ExpressionType.Variable if use_problem_bounds and expr.graph is not None: lower_bound = problem.lower_bound(expr) upper_bound = problem.upper_bound(expr) domain = problem.domain(expr) else: lower_bound = expr.lower_bound upper_bound = expr.upper_bound domain = expr.domain new_var = Variable(expr.name, lower_bound, upper_bound, domain) new_var.reference = expr.reference new_var = relaxed_problem.add_variable(new_var) self._problem_expr[expr.uid] = new_var return new_var
def _underestimate_bilinear_term(self, problem, term, ctx): bilinear_aux_vars = ctx.metadata[BILINEAR_AUX_VAR_META] x_expr = term.var1 y_expr = term.var2 xy_tuple = self._bilinear_tuple(x_expr, y_expr) w = self._get_bilinear_aux_var(problem, ctx, xy_tuple) if w is None: x_l = problem.lower_bound(x_expr) x_u = problem.upper_bound(x_expr) y_l = problem.lower_bound(y_expr) y_u = problem.upper_bound(y_expr) if term.var1 == term.var2: assert np.isclose(x_l, y_l) and np.isclose(x_u, y_u) w_bounds = Interval(x_l, x_u) ** 2 else: w_bounds = Interval(x_l, x_u) * Interval(y_l, y_u) w = Variable( self._format_aux_name(term.var1, term.var2), w_bounds.lower_bound, w_bounds.upper_bound, Domain.REAL, ) reference = BilinearTermReference(x_expr, y_expr) w.reference = reference bilinear_aux_vars[xy_tuple] = w constraints = self._generate_envelope_constraints(problem, term, w) else: constraints = [] new_expr = LinearExpression([w], [term.coefficient], 0.0) return new_expr, constraints
def test_constraints_must_be_constraints(self): v = Variable('v', None, None, None) c = Constraint('foo', v, None, None) with pytest.raises(ValueError): ExpressionRelaxationResult(v, [c, Foo()])
def relax(self, problem, expr, ctx, **kwargs): assert expr.expression_type == ExpressionType.Quadratic side = kwargs.pop('side') term_graph = nx.Graph() term_graph.add_nodes_from(ch.idx for ch in expr.children) term_graph.add_edges_from( (t.var1.idx, t.var2.idx, {'coefficient': t.coefficient}) for t in expr.terms ) # Check convexity of each connected subgraph convex_exprs = [] nonconvex_exprs = [] for connected_component in nx.connected_components(term_graph): connected_graph = term_graph.subgraph(connected_component) vars1 = [] vars2 = [] coefs = [] for (idx1, idx2) in connected_graph.edges: coef = connected_graph.edges[idx1, idx2]['coefficient'] v1 = problem.variable(idx1) v2 = problem.variable(idx2) vars1.append(v1) vars2.append(v2) coefs.append(coef) quadratic_expr = QuadraticExpression(vars1, vars2, coefs) cvx = self._quadratic_rule.apply( quadratic_expr, ctx.convexity, ctx.monotonicity, ctx.bounds ) if cvx.is_convex() and side == RelaxationSide.UNDER: convex_exprs.append(quadratic_expr) elif cvx.is_convex() and side == RelaxationSide.BOTH: convex_exprs.append(quadratic_expr) elif cvx.is_concave() and side == RelaxationSide.OVER: convex_exprs.append(quadratic_expr) else: nonconvex_exprs.append(quadratic_expr) aux_vars = [] aux_coefs = [] constraints = [] if DISAGGREGATE_VAR_AUX_META not in ctx.metadata: ctx.metadata[DISAGGREGATE_VAR_AUX_META] = dict() bilinear_aux = ctx.metadata[DISAGGREGATE_VAR_AUX_META] for quadratic_expr in convex_exprs: if len(quadratic_expr.terms) == 1: term = quadratic_expr.terms[0] xy_idx = (term.var1.idx, term.var2.idx) aux_w = bilinear_aux.get(xy_idx, None) if aux_w is not None: aux_vars.append(aux_w) aux_coefs.append(term.coefficient) continue quadratic_expr_bounds = \ self._quadratic_bound_propagation_rule.apply( quadratic_expr, ctx.bounds ) aux_w = Variable( '_aux_{}'.format(self._call_count), quadratic_expr_bounds.lower_bound, quadratic_expr_bounds.upper_bound, Domain.REAL, ) if len(quadratic_expr.terms) == 1: term = quadratic_expr.terms[0] xy_idx = (term.var1.idx, term.var2.idx) bilinear_aux[xy_idx] = aux_w aux_w.reference = ExpressionReference(quadratic_expr) aux_vars.append(aux_w) aux_coefs.append(1.0) if side == RelaxationSide.UNDER: lower_bound = None upper_bound = 0.0 elif side == RelaxationSide.OVER: lower_bound = 0.0 upper_bound = None else: lower_bound = upper_bound = 0.0 lower_bound = upper_bound = 0.0 constraint = Constraint( '_disaggregate_aux_{}'.format(self._call_count), SumExpression([ LinearExpression([aux_w], [-1.0], 0.0), quadratic_expr, ]), lower_bound, upper_bound, ) constraint.metadata['original_side'] = side constraints.append(constraint) self._call_count += 1 nonconvex_quadratic_expr = QuadraticExpression(nonconvex_exprs) nonconvex_quadratic_under = \ self._bilinear_underestimator.relax( problem, nonconvex_quadratic_expr, ctx, **kwargs ) assert nonconvex_quadratic_under is not None aux_vars_expr = LinearExpression( aux_vars, np.ones_like(aux_vars), 0.0, ) new_expr = LinearExpression( [aux_vars_expr, nonconvex_quadratic_under.expression] ) constraints.extend(nonconvex_quadratic_under.constraints) return ExpressionRelaxationResult(new_expr, constraints)
def relax_constraint(self, problem, constraint): if constraint.name != 'cons2': return RelaxationResult(constraint) w = Variable('aux', None, None, None) return RelaxationResult(Constraint('aux_cons2', w, None, None))
def _make_variable(i): return Variable('u_%d' % i, None, None, Domain.REAL)