R/026_utilities_canonical.R

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

## CVXPY SOURCE: utilities/canonical.py
## Canonical -- base class for all canonicalizable objects


Canonical <- new_class("Canonical", package = "CVXR",
  properties = list(
    id     = new_property(class = class_integer),
    .cache = new_property(class = class_any,
                          default = quote(new.env(parent = emptyenv()))),
    args   = new_property(class = class_list, default = list())
  ),
  constructor = function(id = NULL, args = list()) {
    if (FALSE) new_object(S7_object())  ## S7 static-check guard
    if (is.null(id)) id <- next_expr_id()
    .fast_new(Canonical, S7_object(), id = as.integer(id),
               .cache = new.env(parent = emptyenv()), args = args)
  }
)

# -- Default methods on Canonical --------------------------------------

## variables(): recurse over args, dedup by id
## CVXPY SOURCE: canonical.py line 58-62
method(variables, Canonical) <- function(x) {
  unique_list(unlist(lapply(.args(x), variables), recursive = FALSE))
}

## parameters(): recurse over args, dedup by id
## CVXPY SOURCE: canonical.py line 64-68
method(parameters, Canonical) <- function(x) {
  unique_list(unlist(lapply(.args(x), parameters), recursive = FALSE))
}

## constants(): recurse over args, dedup by id
## CVXPY SOURCE: canonical.py line 70-74
method(constants, Canonical) <- function(x) {
  unique_list(unlist(lapply(.args(x), constants), recursive = FALSE))
}

## atoms(): recurse over args, dedup by id
## CVXPY SOURCE: canonical.py line 163-171
method(atoms, Canonical) <- function(x) {
  unique_list(unlist(lapply(.args(x), atoms), recursive = FALSE))
}

## get_data(): default returns NULL
## CVXPY SOURCE: canonical.py line 154-161
method(get_data, Canonical) <- function(x) NULL

## canonicalize(): default errors -- subclasses must implement
method(canonicalize, Canonical) <- function(x) {
  cli_abort("Class {.cls {class(x)[[1L]]}} must implement {.fn canonicalize}.")
}

## canonical_form: lazily-cached wrapper around canonicalize()
## CVXPY SOURCE: canonical.py lines 47-54 (@lazyprop)
method(canonical_form, Canonical) <- function(x) {
  cached <- cache_get(x, "canonical_form")
  if (!cache_miss(cached)) return(cached)
  result <- canonicalize(x)
  cache_set(x, "canonical_form", result)
  result
}

## copy(): shallow copy of a node (reconstruct from args + get_data)
## CVXPY SOURCE: canonical.py lines 86-112
method(expr_copy, Canonical) <- function(x, args = NULL, id_objects = NULL) {
  ## When `id_objects` is NULL the memo CANNOT hit: the environment would be
  ## allocated one line earlier. Every call on the solve path is such a call
  ## (dcp2cone.R:32, eval_params.R:45); `tree_copy` (cvx_attr2constr.R:503,509)
  ## is the only caller that supplies a real map, and that path is unchanged.
  ## Allocating the env, building a character key, probing it and assigning
  ## into it cost as much as the copy itself -- 4.1 -> 2.1 us/node (2.02x) over
  ## 1023 nodes/solve. Note CVXPY's `copy()` only ever READS `id_objects`
  ## (canonical.py:86-112); the store here is a CVXR addition.
  ## See notes/string_key_hashing_sweep_2026-08-13.md.
  if (!is.null(id_objects)) {
    key <- as.character(.id(x))
    if (exists(key, envir = id_objects, inherits = FALSE)) {
      return(get(key, envir = id_objects, inherits = FALSE))
    }
  }
  ## For Leaf nodes (no args), return self -- they are immutable
  if (length(.args(x)) == 0L && (is.null(args) || length(args) == 0L)) return(x)
  if (is.null(args)) args <- .args(x)
  data <- get_data(x)
  ## Reconstruct via the class constructor
  cls <- S7_class(x)
  if (!is.null(data)) {
    result <- do.call(cls, c(args, data))
  } else {
    result <- do.call(cls, args)
  }
  if (!is.null(id_objects)) assign(key, result, envir = id_objects)
  result
}

## tree_copy(): deep copy of the entire expression tree
## CVXPY SOURCE: canonical.py lines 76-84
method(tree_copy, Canonical) <- function(x, id_objects = NULL) {
  if (is.null(id_objects)) id_objects <- new.env(hash = TRUE, parent = emptyenv())
  new_args <- lapply(.args(x), function(arg) {
    if (is.list(arg)) {
      lapply(arg, function(elem) tree_copy(elem, id_objects))
    } else {
      tree_copy(arg, id_objects)
    }
  })
  expr_copy(x, args = new_args, id_objects = id_objects)
}

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.