R/143_reductions_reduction.R

Defines functions reduction_retrieve reduction_reduce

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

## CVXPY SOURCE: reductions/reduction.py
## Reduction -- abstract base class for reductions
## Also: InverseData -- stores data for solution retrieval


# -- InverseData -----------------------------------------------------
## CVXPY SOURCE: reductions/inverse_data.py
## Stores variable/constraint ID mappings for inverting a reduction.

InverseData <- new_class("InverseData", package = "CVXR",
  properties = list(
    id_map       = class_list,    # var.id -> (offset, size)
    var_offsets  = class_list,    # var.id -> offset
    x_length     = class_integer, # total variable length
    var_shapes   = class_list,    # var.id -> shape
    param_shapes = class_list,    # param.id -> shape
    param_to_size = class_list,   # param.id -> size (includes CONSTANT_ID -> 1)
    param_id_map = class_list,    # param.id -> column offset in tensor
    cons_id_map  = class_environment, # orig constraint id -> canon constraint id
    .extra       = class_environment  # mutable store for ConeMatrixStuffing etc.
  ),
  constructor = function(problem) {
    if (FALSE) new_object(S7_object())  ## S7 static-check guard
    vars_ <- variables(problem)

    ## CVXPY SOURCE: inverse_data.py lines 45-56
    id_map <- list()
    var_offsets <- list()
    var_shapes <- list()
    vert_offset <- 0L
    for (v in vars_) {
      vid <- as.character(.id(v))
      sz <- expr_size(v)
      var_shapes[[vid]] <- .shape(v)
      var_offsets[[vid]] <- vert_offset
      id_map[[vid]] <- c(vert_offset, sz)
      vert_offset <- vert_offset + sz
    }

    ## CVXPY SOURCE: inverse_data.py lines 31-43
    ## Build parameter mappings for DPP tensor construction.
    ## param_to_size always starts with CONSTANT_ID -> 1.
    ## param_id_map: parameter columns are first, CONSTANT_ID is last.
    param_shapes <- list()
    param_to_size <- list()
    param_id_map <- list()
    param_to_size[[as.character(LINOP_CONSTANT_ID)]] <- 1L
    offset <- 0L
    for (p in parameters(problem)) {
      pid <- as.character(.id(p))
      param_shapes[[pid]] <- .shape(p)
      param_to_size[[pid]] <- expr_size(p)
      param_id_map[[pid]] <- offset
      offset <- offset + expr_size(p)
    }
    param_id_map[[as.character(LINOP_CONSTANT_ID)]] <- offset

    .fast_new(InverseData, S7_object(),
      id_map        = id_map,
      var_offsets   = var_offsets,
      x_length      = as.integer(vert_offset),
      var_shapes    = var_shapes,
      param_shapes  = param_shapes,
      param_to_size = param_to_size,
      param_id_map  = param_id_map,
      cons_id_map   = new.env(hash = TRUE, parent = emptyenv()),
      .extra        = new.env(hash = TRUE, parent = emptyenv())
    )
  }
)

# -- Reduction base class --------------------------------------------
## CVXPY SOURCE: reductions/reduction.py lines 20-253

Reduction <- new_class("Reduction", package = "CVXR",
  properties = list(
    .cache = class_environment
  ),
  constructor = function() {
    if (FALSE) new_object(S7_object())  ## S7 static-check guard
    .fast_new(Reduction, S7_object(),
      .cache = new.env(parent = emptyenv())
    )
  }
)

## accepts: subclasses must override
method(reduction_accepts, Reduction) <- function(x, problem, ...) {
  cli_abort("Class {.cls {class(x)[[1L]]}} must implement {.fn reduction_accepts}.")
}

## apply: subclasses must override
method(reduction_apply, Reduction) <- function(x, problem, ...) {
  cli_abort("Class {.cls {class(x)[[1L]]}} must implement {.fn reduction_apply}.")
}

## invert: subclasses must override
method(reduction_invert, Reduction) <- function(x, solution, inverse_data, ...) {
  cli_abort("Class {.cls {class(x)[[1L]]}} must implement {.fn reduction_invert}.")
}

## update_parameters: default no-op (overridden by Dgp2Dcp for DGP+DPP)
## CVXPY SOURCE: reduction.py -- base class does nothing
method(update_parameters, Reduction) <- function(x, problem, ...) {
  invisible(NULL)
}

# -- Derivative chain-rule generics (dict-in / dict-out) ----------
## CVXPY SOURCE: reduction.py:139-218 (PR #3147 part A).
## V19-PARTIAL: dict diff-chain API adopted (#3147 part A); shared-id
## removal (part B) still owed -- ADR D_19.5.
##
## The four hooks are dict-in / dict-out, batched over ALL leaves at once
## (the per-leaf signature they replace could not express a 1->many split,
## e.g. Complex2Real's complex -> real+imag):
##   * var_backward / var_forward     : {var-id   -> array}  (gradients / deltas)
##   * param_backward / param_forward : {param-id -> array}
## Dict keys are `as.character(leaf@id)`. Dict VALUES are shaped arrays carrying
## the leaf's `dim` and MUST stay dimensioned end-to-end -- ADR D_19.5 addendum 2:
## two dimensioned arrays error on a shape mismatch, but the instant one operand
## decays to a bare vector R silently recycles. Base methods are pure identity
## pass-throughs (no element-wise ops -> no recycling possible here).
##
## Direction (chain order, preserved by Problem$backward()/derivative()):
##   backward:  var_backward  outer->inner ;  param_backward inner->outer
##   forward :  param_forward outer->inner ;  var_forward    inner->outer
## A reduction that does not transform leaves uses the identity default, so its
## dict passes straight through; overriding reductions pop the keys they consume.

#' Reduction chain-rule hooks (dict-in / dict-out)
#'
#' Walk the solving chain during `Problem$backward()` / `Problem$derivative()`.
#' Each hook takes and returns a named list keyed by `as.character(leaf@id)`
#' whose values are shaped arrays (the leaf's `dim`). The base `Reduction`
#' methods are identity pass-throughs.
#'
#' @param x A `Reduction`.
#' @param del_vars Named list `var-id -> gradient array` (outer representation).
#' @returns For `var_backward`: the same map in the inner (reduced) representation.
#' @name reduction-chain-rule
#' @export
var_backward <- new_generic("var_backward", "x",
  function(x, del_vars) S7_dispatch())
method(var_backward, Reduction) <- function(x, del_vars) del_vars

#' @rdname reduction-chain-rule
#' @param dvars Named list `var-id -> delta array` (inner representation).
#' @returns For `var_forward`: the same map in the outer (original) representation.
#' @export
var_forward <- new_generic("var_forward", "x",
  function(x, dvars) S7_dispatch())
method(var_forward, Reduction) <- function(x, dvars) dvars

#' @rdname reduction-chain-rule
#' @param dparams Named list `param-id -> gradient array` (inner representation).
#' @returns For `param_backward`: the same map in the outer (original) representation.
#' @export
param_backward <- new_generic("param_backward", "x",
  function(x, dparams) S7_dispatch())
method(param_backward, Reduction) <- function(x, dparams) dparams

#' @rdname reduction-chain-rule
#' @param param_deltas Named list `param-id -> delta array` (outer representation).
#' @returns For `param_forward`: the same map in the inner (transformed) representation.
#' @export
param_forward <- new_generic("param_forward", "x",
  function(x, param_deltas) S7_dispatch())
method(param_forward, Reduction) <- function(x, param_deltas) param_deltas

# -- Reduction leaf-id maps (for chain composition) ---------------
## CVXPY SOURCE: reduction.py:87-138 (var_id_map / param_id_map properties).
## Map original-leaf-id -> list of reduced-leaf-id(s). LIST-valued to support
## 1->many (Complex2Real: complex var -> [real-id, imag-id]). A reduction that
## replaces leaves overrides these; the default is the empty map (no remap).
## Composed across the chain by Chain$compose_var_id_map()/compose_param_id_map().
## Keys/values are `as.character(id)`.

#' Reduction leaf-id maps
#'
#' @param x A `Reduction`.
#' @returns A named list `orig-id -> character vector of reduced-id(s)`; empty
#'   by default (the reduction replaces no leaves).
#' @name reduction-id-map
#' @export
var_id_map <- new_generic("var_id_map", "x", function(x) S7_dispatch())
method(var_id_map, Reduction) <- function(x) list()

#' @rdname reduction-id-map
#' @export
param_id_map <- new_generic("param_id_map", "x", function(x) S7_dispatch())
method(param_id_map, Reduction) <- function(x) list()

## reduce: convenience -- apply and cache result
## CVXPY SOURCE: reduction.py lines 169-192
reduction_reduce <- function(x, problem) {
  if (!is.null(x@.cache$emitted_problem)) {
    return(x@.cache$emitted_problem)
  }
  result <- reduction_apply(x, problem)
  x@.cache$emitted_problem <- result[[1L]]
  x@.cache$retrieval_data <- result[[2L]]
  result[[1L]]
}

## retrieve: convenience -- invert cached result
## CVXPY SOURCE: reduction.py lines 194-215
reduction_retrieve <- function(x, solution) {
  if (is.null(x@.cache$retrieval_data)) {
    cli_abort("{.fn reduction_reduce} must be called before {.fn reduction_retrieve}.")
  }
  reduction_invert(x, solution, x@.cache$retrieval_data)
}

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.