Nothing
#####
## DO NOT EDIT THIS FILE!! EDIT THE SOURCE INSTEAD: rsrc_tree/reductions/dgp2dcp/dgp2dcp.R
#####
## CVXPY SOURCE: reductions/dgp2dcp/dgp2dcp.py
## Dgp2Dcp -- reduces DGP problems to DCP problems via log-space transformation
##
## Key design:
## - Inherits from Canonicalization but uses its own tree walk (G1)
## - Per-problem DGP methods closure with variable/parameter caches (G2)
## - Stores dgp_methods on instance .cache for DPP reuse (G8)
## - Stores original problem in inverse_data@.extra (G3)
## dgp_canonicalize generic is defined in dgp_canonicalizers.R (loads before this file)
Dgp2Dcp <- new_class("Dgp2Dcp", parent = Canonicalization, package = "CVXR",
constructor = function() {
new_object(S7_object(),
.cache = new.env(parent = emptyenv())
)
}
)
## -- reduction_accepts ---------------------------------------------
## CVXPY SOURCE: dgp2dcp.py line 62-65
method(reduction_accepts, Dgp2Dcp) <- function(x, problem, ...) {
is_dgp(problem)
}
## -- reduction_apply -----------------------------------------------
## CVXPY SOURCE: dgp2dcp.py lines 119-128
## Creates per-problem DGP methods, then walks tree via own tree walk.
method(reduction_apply, Dgp2Dcp) <- function(x, problem, ...) {
if (!reduction_accepts(x, problem)) {
cli_abort("The supplied problem is not DGP.")
}
## Create per-problem DGP methods (G2)
dgp_methods <- .make_dgp_methods()
## Store on instance for DPP reuse (G8)
x@.cache$dgp_methods <- dgp_methods
## Create inverse data
inverse_data <- InverseData(problem)
## Store original problem (G3)
inverse_data@.extra$problem <- problem
## Canonicalize objective via OWN tree walk (G1)
obj_result <- .dgp2dcp_tree(dgp_methods, problem@objective)
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 <- .dgp2dcp_tree(dgp_methods, con)
all_chunks[[i + 1L]] <- c(con_result[[2L]], list(con_result[[1L]]))
## Store constraint ID mapping
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)
}
## -- reduction_invert ----------------------------------------------
## CVXPY SOURCE: dgp2dcp.py lines 152-160
## Transform solution back from log-space: exp(value) for all primals.
method(reduction_invert, Dgp2Dcp) <- function(x, solution, inverse_data, ...) {
## First apply parent invert (handles cons_id_map remapping)
## S7: call Canonicalization method directly (no callNextMethod in S7)
solution <- method(reduction_invert, Canonicalization)(x, solution, inverse_data, ...)
if (solution@status == SOLVER_ERROR) return(solution)
## Transform primal vars back: exp(log_x) = x
for (vid in names(solution@primal_vars)) {
solution@primal_vars[[vid]] <- exp(solution@primal_vars[[vid]])
}
## Transform objective: f(x) = exp(F(u))
solution@opt_val <- exp(solution@opt_val)
solution
}
## -- update_parameters ----------------------------------------------
## CVXPY SOURCE: dgp2dcp.py lines 67-78
## Called in DPP fast path: transforms original parameter values to log-space.
method(update_parameters, Dgp2Dcp) <- function(x, problem, ...) {
dgp_methods <- x@.cache$dgp_methods
if (is.null(dgp_methods)) return(invisible(NULL))
params_cache <- dgp_methods$params_cache
for (param in parameters(problem)) {
pid <- as.character(param@id)
if (exists(pid, envir = params_cache, inherits = FALSE)) {
log_param <- get(pid, envir = params_cache, inherits = FALSE)
value(log_param) <- log(value(param))
}
}
invisible(NULL)
}
## ==================================================================
## Own tree walk functions (G1)
## ==================================================================
## .dgp2dcp_tree: recursive bottom-up walk (same structure as .canonicalize_tree
## but calls .dgp2dcp_expr which does NOT skip constants)
.dgp2dcp_tree <- function(dgp_methods, expr) {
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 <- .dgp2dcp_tree(dgp_methods, expr@args[[i]])
canon_args[[i]] <- arg_result[[1L]]
constr_chunks[[i]] <- arg_result[[2L]]
}
node_result <- .dgp2dcp_expr(dgp_methods, expr, canon_args)
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)
}
## .dgp2dcp_expr: canonicalize a single node
## NO constant-skipping (G1). Variable/Parameter dispatch via dgp_methods (G2).
.dgp2dcp_expr <- function(dgp_methods, expr, args) {
## Variable -> stateful variable_canon
if (S7_inherits(expr, Variable)) {
return(dgp_methods$variable_canon(expr, args))
}
## Parameter -> stateful parameter_canon
if (S7_inherits(expr, Parameter)) {
return(dgp_methods$parameter_canon(expr, args))
}
## S7 dispatch via dgp_canonicalize -- NULL means no method registered
result <- dgp_canonicalize(expr, args)
if (!is.null(result)) return(result)
## Default: copy with canonicalized args
list(expr_copy(expr, args), list())
}
## ==================================================================
## Per-problem DGP methods closure (G2)
## ==================================================================
.make_dgp_methods <- function() {
## Per-problem caches for dedup
vars_cache <- new.env(hash = TRUE, parent = emptyenv())
params_cache <- new.env(hash = TRUE, parent = emptyenv())
variable_canon <- function(variable, args) {
vid <- as.character(variable@id)
if (exists(vid, envir = vars_cache, inherits = FALSE)) {
return(list(get(vid, envir = vars_cache, inherits = FALSE), list()))
}
## Create unconstrained log-space variable (same shape, preserves ID)
log_var <- Variable(variable@shape, var_id = variable@id)
assign(vid, log_var, envir = vars_cache)
list(log_var, list())
}
parameter_canon <- function(parameter, args) {
pid <- as.character(parameter@id)
if (exists(pid, envir = params_cache, inherits = FALSE)) {
return(list(get(pid, envir = params_cache, inherits = FALSE), list()))
}
## Create log-space parameter (DPP: may not have value yet)
log_param <- Parameter(parameter@shape, name = expr_name(parameter))
if (!is.null(value(parameter))) {
value(log_param) <- log(value(parameter))
}
assign(pid, log_param, envir = params_cache)
list(log_param, list())
}
list(
variable_canon = variable_canon,
parameter_canon = parameter_canon,
vars_cache = vars_cache,
params_cache = params_cache
)
}
## DEFERRED: Derivative chain-rule overrides (dgp2dcp.py lines 80-112)
## Dgp2Dcp transforms parameters via log, so the chain rule for derivatives
## requires log/exp Jacobian correction:
##
## param_backward(x, param, dparams) -- multiply gradient by param.value (exp(log_param))
## param_forward(x, param, delta) -- divide delta by param.value
## var_backward(x, var, value) -- multiply by exp(log_var.value)
## var_forward(x, var, value) -- divide by exp(log_var.value)
##
## Deferred: derivative API depends on diffcp. See notes/derivative_api_deferred.md.
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.