R/261_reductions_solvers_utilities.R

Defines functions get_dual_values expcone_permutor extract_dual_value .status_from_map

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

## CVXPY SOURCE: reductions/solvers/utilities.py
## Utility functions for solver dual value extraction


# -- .status_from_map ----------------------------------------------
## R-SPECIFIC hardening of a pattern CVXPY does not need.
##
## Upstream indexes its STATUS_MAPs directly (`self.STATUS_MAP[model.Status]`,
## 14 sites) and would raise KeyError on anything unmapped. CVXR is already
## better here -- every lookup is followed by `if (is.null(status)) status <-
## SOLVER_ERROR` -- but that guard is incomplete in R, because the FAILURE HAPPENS
## BEFORE IT: `map[[key]]` with `key` NULL or `character(0)` raises "subscript out
## of bounds" rather than returning NULL, so a solver that hands back no status at
## all produced a raw R error instead of the intended SOLVER_ERROR. `NA` is the
## same trap. Ten sites repeated the fragile idiom; this is the one place that
## has to be right.
##
## Integer-keyed maps (SCIP, XPRESS, CPLEX) are looked up through as.character()
## by every caller; folding the coercion in here keeps that from being a per-site
## thing to remember.
.status_from_map <- function(map, key, default = SOLVER_ERROR) {
  if (is.null(key) || length(key) != 1L || is.na(key)) return(default)
  status <- map[[as.character(key)]]
  if (is.null(status)) default else status
}


# -- extract_dual_value --------------------------------------------
## CVXPY SOURCE: utilities.py lines 53-58
## Extracts a slice of the dual result vector for a constraint.

extract_dual_value <- function(result_vec, offset, constraint) {
  sz <- constr_size(constraint)
  value <- result_vec[(offset + 1L):(offset + sz)]
  if (sz == 1L) value <- as.numeric(value)
  new_offset <- offset + sz
  list(value = value, new_offset = new_offset)
}

# -- get_dual_values ----------------------------------------------
## CVXPY SOURCE: utilities.py lines 61-87
## Iterates over constraints, calling parse_func to extract dual values.

# -- expcone_permutor ---------------------------------------------
## CVXPY SOURCE: utilities.py expcone_permutor()
## Creates a permutation vector for reordering ExpCone dual variables.
## ECOS expects ExpCone args in order (x, z, y) but CVXR standard is (x, y, z).

expcone_permutor <- function(n_cones, exp_cone_order) {
  order <- rep(exp_cone_order, n_cones)
  offsets <- rep(seq(0L, by = 3L, length.out = n_cones), each = 3L)
  order + offsets + 1L  # +1 for R 1-based indexing
}

# -- get_dual_values ----------------------------------------------
## CVXPY SOURCE: utilities.py lines 61-87
## Iterates over constraints, calling parse_func to extract dual values.

get_dual_values <- function(result_vec, parse_func, constraints) {
  ## CVXPY's `dual_vars[constr.id] = ...` is an O(1) dict insert; the R
  ## transliteration `dual_vars[[chr]] <- ...` reallocates the list and its
  ## names on every constraint, so the loop was O(n^2) -- 12.6 ms at n = 1000,
  ## against 7.3 ms here (1.7x). Preallocate, then name once.
  ## See notes/string_key_hashing_sweep_2026-08-13.md.
  n <- length(constraints)
  vals <- vector("list", n)
  nms <- character(n)
  offset <- 0L
  for (i in seq_len(n)) {
    constr <- constraints[[i]]
    result <- parse_func(result_vec, offset, constr)
    ## `vals[[i]] <- NULL` would DELETE element i; `vals[i] <- list(v)` stores.
    vals[i] <- list(result$value)
    nms[i] <- as.character(constr@id)
    offset <- result$new_offset
  }
  names(vals) <- nms
  ## `[[<-` with NULL deletes rather than stores, so the loop this replaces
  ## could never yield a NULL entry; drop any so the result is identical.
  keep <- !vapply(vals, is.null, logical(1))
  if (!all(keep)) vals <- vals[keep]
  vals
}

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.