def prox_sum_quantile(expr): arg = None if (expr.expression_type == Expression.SUM and expr.arg[0].expression_type == Expression.MAX_ELEMENTWISE and len(expr.arg[0].arg) == 2): alpha, x = get_quantile_arg(expr.arg[0].arg[0]) beta, y = get_quantile_arg(expr.arg[0].arg[1]) if (x is not None and y is not None and x == y): if (alpha.sign.sign_type == Sign.NEGATIVE and beta.sign.sign_type == Sign.POSITIVE): alpha, beta = beta, expression.negate(alpha) arg = x elif (alpha.sign.sign_type == Sign.POSITIVE and beta.sign.sign_type == Sign.NEGATIVE): beta = expression.negate(beta) arg = x if not arg: return MatchResult(False) alpha = linear.transform_expr(alpha) beta = linear.transform_expr(beta) diagonal_arg, constrs = convert_diagonal(arg) return MatchResult( True, expression.prox_function( create_prox( prox_function_type=ProxFunction.SUM_QUANTILE, arg_size=[Size(dim=dims(arg))], scaled_zone_params=ProxFunction.ScaledZoneParams( alpha_expr=alpha.proto_with_args, beta_expr=beta.proto_with_args)), diagonal_arg), constrs)
def epigraph(expr): f_expr, t_expr = get_epigraph(expr) if f_expr: for rule in BASE_RULES: result = rule(f_expr) if result.match: epi_function = result.prox_expr.prox_function epi_function.epigraph = True epi_function.arg_size.add().CopyFrom(Size(dim=dims(t_expr))) linear_t_expr = linear.transform_expr(t_expr) if linear_t_expr.affine_props.scalar: constrs = [] else: linear_t_expr, constrs = epi_transform( linear_t_expr, "scalar") return MatchResult( True, expression.prox_function( epi_function, *(result.prox_expr.arg + [linear_t_expr])), result.raw_exprs + constrs) # No epigraph transform found, do conic transformation obj, constrs = conic.transform_expr(f_expr) return MatchResult( True, None, [expression.leq_constraint(obj, t_expr)] + constrs) # Not in epigraph form return MatchResult(False)
def convert_scalar(expr): if not expr.dcp_props.affine: return epi_transform(expr, "affine") linear_expr = linear.transform_expr(expr) if linear_expr.affine_props.scalar: return linear_expr, [] return epi_transform(linear_expr, "scalar")
def convert_diagonal(expr): if not expr.dcp_props.affine: return epi_transform(expr, "affine") linear_expr = linear.transform_expr(expr) if linear_expr.affine_props.diagonal: return linear_expr, [] return epi_transform(linear_expr, "diagonal")
def prox_constant(expr): if expr.dcp_props.constant: return MatchResult( True, expression.prox_function( create_prox(prox_function_type=ProxFunction.CONSTANT), linear.transform_expr(expr))) else: return MatchResult(False)
def prox_affine(expr): if expr.dcp_props.affine: return MatchResult( True, expression.prox_function( create_prox(prox_function_type=ProxFunction.AFFINE), linear.transform_expr(expr))) else: return MatchResult(False)
def prox_sum_quantile(expr): arg = None if (expr.expression_type == Expression.SUM and expr.arg[0].expression_type == Expression.MAX_ELEMENTWISE and len(expr.arg[0].arg) == 2): alpha, x = get_quantile_arg(expr.arg[0].arg[0]) beta, y = get_quantile_arg(expr.arg[0].arg[1]) if (x is not None and y is not None and x == y): if (alpha.sign.sign_type == Sign.NEGATIVE and beta.sign.sign_type == Sign.POSITIVE): alpha, beta = beta, expression.negate(alpha) arg = x elif (alpha.sign.sign_type == Sign.POSITIVE and beta.sign.sign_type == Sign.NEGATIVE): beta = expression.negate(beta) arg = x if not arg: return MatchResult(False) alpha = linear.transform_expr(alpha) beta = linear.transform_expr(beta) data = alpha.expression_data() data.update(beta.expression_data()) diagonal_arg, constrs = convert_diagonal(arg) return MatchResult( True, expression.prox_function( create_prox( prox_function_type=ProxFunction.SUM_QUANTILE, arg_size=[Size(dim=dims(arg))], scaled_zone_params=ProxFunction.ScaledZoneParams( alpha_expr=alpha.proto_with_args, beta_expr=beta.proto_with_args)), diagonal_arg, data=data), constrs)
def transform_linear_map(expr, constrs): if expr.arg[0].expression_type == Expression.LINEAR_MAP: # TODO(mwytock): need more complete logic here, i.e. need to do type # inference across the entire chain using something like affine_props A = affine.LinearMapType(expr.linear_map) B = affine.LinearMapType(expr.arg[0].linear_map) if ((A.kronecker_product or B.kronecker_product) and not (A*B).kronecker_product): t, epi_constrs = epi_transform(expr.arg[0], "split_linear_map") constrs += [linear.transform_expr(x) for x in epi_constrs] return expression.from_proto(expr.proto, [t], expr.data) return expr
def add_variable_copy(f, var, graph): m, n = dims(var.expr) old_var_id = var.expr.variable.variable_id new_var_id = "separate:%s:%s" % (old_var_id, f.node_id) new_var = graph.add_node( expression.variable(m, n, new_var_id), VARIABLE, new_var_id) f.expr = replace_var(f.expr, old_var_id, new_var.expr) graph.remove_edge(f, var) graph.add_edge(f, new_var) eq_constr = graph.add_node(linear.transform_expr( expression.eq_constraint(new_var.expr, var.expr)), CONSTRAINT) graph.add_edge(eq_constr, new_var) graph.add_edge(eq_constr, var)
def add_variable_copy(f, var, graph): m, n = dims(var.expr) old_var_id = var.expr.variable.variable_id new_var_id = "separate:%s:%s" % (old_var_id, f.node_id) new_var = graph.add_node(expression.variable(m, n, new_var_id), VARIABLE, new_var_id) f.expr = replace_var(f.expr, old_var_id, new_var.expr) graph.remove_edge(f, var) graph.add_edge(f, new_var) eq_constr = graph.add_node( linear.transform_expr(expression.eq_constraint(new_var.expr, var.expr)), CONSTRAINT) graph.add_edge(eq_constr, new_var) graph.add_edge(eq_constr, var)
def convert_affine(expr): if not expr.dcp_props.affine: return epi_transform(expr, "affine") return linear.transform_expr(expr), []