R/243_reductions_solvers_nlp_solvers_diff_engine_helpers.R

Defines functions .de_build_param_dict .de_build_var_dict .de_chain_add .de_to_dense .de_shape

#####
## DO NOT EDIT THIS FILE!! EDIT THE SOURCE INSTEAD: rsrc_tree/reductions/solvers/nlp_solvers/diff_engine/helpers.R
#####

## CVXPY SOURCE: reductions/solvers/nlp_solvers/diff_engine/helpers.py
## Shared helpers for converting CVXR expressions to sparsediff (C diff engine)
## expression handles.
##
## NOTE on shapes: CVXPY's normalize_shape pads 1D (n,) -> (1, n). CVXR shapes
## are ALWAYS 2D (r, c) (vectors are (n, 1)), and the sparsediff R package uses
## the same (n, 1) column convention as CVXR (see its README:
## sd_variable(d1 = n, d2 = 1L, ...)). So here `.de_shape` is essentially the
## identity -- we pass CVXR's native (r, c) straight through to d1/d2, with no
## CVXPY-style 1D normalization.

## Normalize a CVXR shape to an integer (d1, d2) pair for the engine.
.de_shape <- function(shape) {
  shape <- as.integer(shape)
  if (length(shape) == 1L) c(1L, shape) else shape[1:2]
}

## Convert a value to a dense double matrix (sparse -> dense).
.de_to_dense <- function(value) {
  if (inherits(value, "Matrix")) value <- as.matrix(value)
  storage.mode(value) <- "double"
  value
}

## Combine children with a balanced binary tree of adds (depth ceil(log2 N)
## instead of N-1, keeping the AD graph shallow when summing many terms).
.de_chain_add <- function(children) {
  n <- length(children)
  if (n == 1L) return(children[[1L]])
  mid <- n %/% 2L
  sparsediff::sd_add(.de_chain_add(children[seq_len(mid)]),
                     .de_chain_add(children[(mid + 1L):n]))
}

## Build a {var_id -> sparsediff variable handle} environment from InverseData.
## id_map[[vid]] = c(offset, size); var_id passed to sd_variable IS the 0-based
## offset (InverseData offsets start at 0).
.de_build_var_dict <- function(inverse_data) {
  n_vars <- as.integer(inverse_data@x_length)
  var_dict <- new.env(parent = emptyenv())
  for (vid in names(inverse_data@id_map)) {
    offset <- as.integer(inverse_data@id_map[[vid]][[1L]])
    shp <- .de_shape(inverse_data@var_shapes[[vid]])
    assign(vid,
           sparsediff::sd_variable(d1 = shp[1L], d2 = shp[2L],
                                   var_id = offset, n_vars = n_vars),
           envir = var_dict)
  }
  list(var_dict = var_dict, n_vars = n_vars)
}

## Build a {param_id -> sparsediff parameter handle} environment from
## InverseData. The CONSTANT_ID entry in param_id_map has no shape -> skipped.
## Parameter values are flattened column-major (order='F' == R as.numeric).
.de_build_param_dict <- function(problem, inverse_data) {
  n_vars <- as.integer(inverse_data@x_length)
  param_dict <- new.env(parent = emptyenv())
  params <- parameters(problem)
  for (pid in names(inverse_data@param_id_map)) {
    shp <- inverse_data@param_shapes[[pid]]
    if (is.null(shp)) next   # CONSTANT_ID has no shape
    shp <- .de_shape(shp)
    offset <- as.integer(inverse_data@param_id_map[[pid]])
    p <- Find(function(pp) as.character(pp@id) == pid, params)
    pv <- .de_to_dense(value(p))
    assign(pid,
           sparsediff::sd_parameter(d1 = shp[1L], d2 = shp[2L],
                                    param_id = offset, n_vars = n_vars,
                                    values = as.numeric(pv)),
           envir = param_dict)
  }
  list(param_dict = param_dict, n_vars = n_vars)
}

Try the CVXR package in your browser

Any scripts or data that you put into this service are public.

CVXR documentation built on Aug. 24, 2026, 9:10 a.m.