Nothing
#####
## DO NOT EDIT THIS FILE!! EDIT THE SOURCE INSTEAD: rsrc_tree/reductions/dcp2cone/dcp2cone.R
#####
## CVXPY SOURCE: reductions/dcp2cone/dcp2cone.py
## Dcp2Cone -- reduce DCP problems to conic form
##
## Uses S7 generic dispatch (dcp_canonicalize, quad_canonicalize, has_dcp_canon)
## for expression-level canonicalization. When quad_obj=TRUE, also tries
## quad_canonicalize for quadratic atoms in the objective's affine subtree (QP path).
## This is NOT the same as graph_implementation (which is LinOp-level).
## -- S7 generics for canonicalization dispatch ----------------------
## These replace the old environment-based CANON_METHODS / QUAD_CANON_METHODS
## registries with proper S7 method dispatch.
##
## INHERITANCE SAFETY INVARIANT: Every atom subclass of a canon atom MUST have
## its own explicit method() registration. Without it, S7 would silently dispatch
## to the parent's canonicalizer -- potentially producing incorrect results.
## Verified safe pairs: Log/Log1p, Pnorm/PnormApprox, Power/PowerApprox,
## GeoMean/GeoMeanApprox -- all have explicit separate registrations.
#' DCP cone canonicalization dispatch
#'
#' Replaces CANON_METHODS environment lookup. Default: identity copy.
#' Each DCP atom registers its own method returning list(canon_expr, constraints).
#' @noRd
dcp_canonicalize <- new_generic("dcp_canonicalize", "expr",
function(expr, args, ...) {
S7_dispatch()
}
)
method(dcp_canonicalize, S7_object) <- function(expr, args, ...) {
list(expr_copy(expr, args), list())
}
#' Predicate: does this expression class have a DCP cone canonicalizer?
#'
#' Used by .dcp2cone_tree() for affine_above tracking. Returns TRUE only for
#' atoms with a registered dcp_canonicalize method (NOT FiniteSet).
#' @noRd
has_dcp_canon <- new_generic("has_dcp_canon", "expr",
function(expr) {
S7_dispatch()
}
)
method(has_dcp_canon, S7_object) <- function(expr) FALSE
#' Quadratic canonicalization dispatch
#'
#' Replaces QUAD_CANON_METHODS environment lookup. Default: NULL sentinel
#' (meaning "no quad canon for this class" -- fall through to dcp_canonicalize).
#' Guard logic (.quadratic_power(), is_qpwa()) is inside each method.
#' @noRd
quad_canonicalize <- new_generic("quad_canonicalize", "expr",
function(expr, args, ...) {
S7_dispatch()
}
)
method(quad_canonicalize, S7_object) <- function(expr, args, ...) NULL
# -- Helper: vectorize an expression (column-major) -----------------
## Equivalent to CVXPY vec(x, order='F')
.cvxr_vec <- function(x) {
Reshape(x, c(expr_size(x), 1L), order = "F")
}
# -- Dcp2Cone class -------------------------------------------------
## CVXPY SOURCE: dcp2cone.py lines 31-145
Dcp2Cone <- new_class("Dcp2Cone", parent = Canonicalization,
package = "CVXR",
properties = list(
quad_obj = class_logical
),
constructor = function(quad_obj = FALSE) {
new_object(S7_object(),
.cache = new.env(parent = emptyenv()),
quad_obj = quad_obj
)
}
)
## accepts: problem must be Minimize and DCP
## CVXPY SOURCE: dcp2cone.py lines 47-50
method(reduction_accepts, Dcp2Cone) <- function(x, problem, ...) {
S7_inherits(problem@objective, Minimize) && is_dcp(problem)
}
## apply: override to pass affine_above flag
## CVXPY SOURCE: dcp2cone.py lines 52-76
method(reduction_apply, Dcp2Cone) <- function(x, problem, ...) {
if (!reduction_accepts(x, problem)) {
cli_abort("Cannot reduce problem to cone program: must be a minimization DCP problem.")
}
inverse_data <- InverseData(problem)
## Canonicalize objective (affine_above = TRUE for objective)
obj_result <- .dcp2cone_tree(x@quad_obj, problem@objective, TRUE)
canon_objective <- obj_result[[1L]]
## Canonicalize each constraint -- collect chunks, flatten once
n_cons <- length(problem@constraints)
all_chunks <- vector("list", n_cons + 1L)
all_chunks[[1L]] <- obj_result[[2L]]
for (i in seq_len(n_cons)) {
con <- problem@constraints[[i]]
con_result <- .dcp2cone_tree(x@quad_obj, con, FALSE)
all_chunks[[i + 1L]] <- c(con_result[[2L]], list(con_result[[1L]]))
assign(as.character(con@id), con_result[[1L]]@id,
envir = inverse_data@cons_id_map)
}
canon_constraints <- unlist(all_chunks, recursive = FALSE)
if (is.null(canon_constraints)) canon_constraints <- list()
new_problem <- Problem(canon_objective, canon_constraints)
list(new_problem, inverse_data)
}
# -- Dcp2Cone-specific tree walk ------------------------------------
## CVXPY SOURCE: dcp2cone.py lines 78-107
## The key difference from base Canonicalization is the affine_above tracking:
## if the path from root to current node is all affine, we may skip cone canon.
.dcp2cone_tree <- function(quad_obj, expr, affine_above) {
## Determine if this atom is affine (no DCP cone canonicalizer registered)
affine_atom <- !has_dcp_canon(expr)
## Recurse into each argument -- pre-allocate, flatten once
n_args <- length(expr@args)
canon_args <- vector("list", n_args)
constr_chunks <- vector("list", n_args + 1L)
for (i in seq_len(n_args)) {
arg_result <- .dcp2cone_tree(quad_obj,
expr@args[[i]],
affine_atom && affine_above)
canon_args[[i]] <- arg_result[[1L]]
constr_chunks[[i]] <- arg_result[[2L]]
}
## Canonicalize this node
## CVXPY passes affine_above (NOT affine_atom && affine_above) here.
## affine_above means "is the path ABOVE this node all affine?"
## This allows QuadForm (non-affine) at the top of an affine path
## to be dispatched to quad canon methods.
node_result <- .dcp2cone_expr(quad_obj, expr, canon_args,
affine_above = affine_above)
constr_chunks[[n_args + 1L]] <- node_result[[2L]]
constrs <- unlist(constr_chunks, recursive = FALSE)
if (is.null(constrs)) constrs <- list()
list(node_result[[1L]], constrs)
}
## .dcp2cone_expr: canonicalize a single node (Dcp2Cone version)
## CVXPY SOURCE: dcp2cone.py lines 109-145
.dcp2cone_expr <- function(quad_obj, expr, args, affine_above = FALSE) {
## Skip constants with no parameters
if (S7_inherits(expr, Expression) &&
is_constant(expr) && length(parameters(expr)) == 0L) {
return(list(expr, list()))
}
## QP path: try quad_canonicalize first when quad_obj=TRUE
## and the path above is all affine.
## Guard logic (.quadratic_power, is_qpwa) is inside each quad method.
## NULL return = guard failed or no quad method -> fall through to DCP.
## CVXPY SOURCE: dcp2cone.py lines 128-139
if (quad_obj && affine_above) {
quad_result <- quad_canonicalize(expr, args)
if (!is.null(quad_result)) return(quad_result)
}
## DCP cone canonicalization (S7 dispatch -- default returns identity copy)
dcp_canonicalize(expr, args)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.