R/102_atoms_norm.R

Defines functions .cvxr_norm_entrywise cvxr_norm

Documented in cvxr_norm

#####
## DO NOT EDIT THIS FILE!! EDIT THE SOURCE INSTEAD: rsrc_tree/atoms/norm.R
#####

## CVXPY SOURCE: atoms/norm.py
## Norm factory function (matching old CVXR API)
## Dispatches to Norm1, NormInf, or Pnorm based on p

#' Compute a norm of an expression
#'
#' With \code{axis = NULL}, \code{p = 1}, \code{2} and \code{Inf} are the
#' matrix norms, as in CVXPY and base R \code{norm()}: the maximum absolute
#' column sum, the largest singular value (\code{\link{sigma_max}}) and the
#' maximum absolute row sum. A row vector is a 1 x n matrix, so its 1-norm is
#' its largest absolute entry and its Inf-norm the sum of absolute entries.
#' For a column vector these equal the entrywise norms. Any other \code{p} is
#' an error for a matrix or row vector, as in CVXPY ("Unsupported matrix
#' norm."); use \code{\link{p_norm}} for the entrywise p-norm. On a column
#' vector, and with an \code{axis}, it is the entrywise norm. The masking
#' \code{norm()} gives the same results under base R's type names.
#'
#' @param x An Expression
#' @param p Norm type: 1, 2, Inf, "fro" (Frobenius), or "nuc" (nuclear norm
#'   of a matrix). "fro" and "nuc" are whole-matrix norms and take no
#'   \code{axis}.
#' @param axis NULL (all), 0 (columns), or 1 (rows)
#' @param keepdims Logical
#' @param max_denom Integer max denominator
#' @returns A norm atom
#' @export
cvxr_norm <- function(x, p = 2, axis = NULL, keepdims = FALSE, max_denom = 1024L) {
  if (is.character(p)) {
    p_lc <- tolower(p)
    ## CVXPY v1.9.3 fix: #3501 -- 'fro' and 'nuc' are matrix norms, defined
    ## over the whole matrix; an axis used to be dropped silently for 'fro'.
    ## CVXPY SOURCE: norm.py:69-78.
    if (!is.null(axis) && p_lc %in% c("fro", "f", "nuc")) {
      cli_abort(c(
        "The axis parameter is not supported for the {.val {p}} norm, which is defined over a whole matrix.",
        "i" = "Use {.code cvxr_norm(x, 2, axis = ...)} for the 2-norm along an axis."
      ))
    }
    ## CVXPY SOURCE: norm.py:62-63 (matrix) and :86-87 (non-matrix). An R
    ## vector is a matrix with one dimension equal to 1 -- CVXPY's 1-D case.
    if (p_lc == "nuc") {
      x <- as_expr(x)
      if (any(.shape(x) == 1L)) {
        cli_abort("Unsupported norm option nuc for non-matrix.")
      }
      return(norm_nuc(x))
    }
    if (p_lc %in% c("fro", "f")) p <- "fro"
  }
  if (identical(p, "fro")) {
    ## Frobenius norm = p_norm(vec(x), 2)
    return(p_norm(x, 2, axis = NULL, keepdims = FALSE, max_denom = max_denom))
  }
  ## 1.9.3 plan defect 0.4 (decision D3): matrix norms take precedence, as in
  ## CVXPY norm.py:54-67 and base R norm(). Every CVXR expression is 2-D, so
  ## a row vector is a 1 x n matrix, as in base R and CVXPY 2-D: its 1-norm
  ## is the max |entry| and its Inf-norm the sum (user, 2026-10-14). A column
  ## vector or scalar keeps the entrywise atoms, whose values are the matrix
  ## norms there. p = 2 is sigma_max only when both dimensions are > 1
  ## (norm.py:58: one nontrivial dimension is the Frobenius norm). Any other
  ## p on a matrix or row vector raises, as in norm.py:66-67: its induced
  ## p-norm has no tractable form (a row's is the dual q-norm), and the
  ## entrywise p-norm returned here before was silent (user, 2026-10-14). It
  ## stays available as p_norm(x, p). A column vector or scalar is CVXPY's
  ## 1-D case (norm.py:89) and keeps the entrywise p-norm.
  ## A numeric string ("1", "2", "Inf", as base R norm() types are written)
  ## is the same p as the number.
  pn <- suppressWarnings(as.numeric(p))
  if (is.character(p) && is.na(pn)) {
    ## CVXPY SOURCE: norm.py:67 (2-D, no axis) and norm.py:87 (otherwise). A
    ## string that is neither a matrix-norm name nor a number used to fall
    ## through to .cvxr_norm_entrywise and raise R's "missing value where
    ## TRUE/FALSE needed" behind a coercion warning (sweep item 4, pass D3).
    if (is.null(axis) && .shape(as_expr(x))[2L] > 1L) {
      cli_abort("Unsupported matrix norm.")
    }
    cli_abort("Unsupported norm option {p} for non-matrix.")
  }
  if (is.null(axis) && !is.na(pn)) {
    x <- as_expr(x)
    shp <- .shape(x)
    if (shp[2L] > 1L) {
      if (pn == 1) return(max_entries(norm1(x, axis = 2L)))      # max column sum
      if (pn == Inf) return(max_entries(norm1(x, axis = 1L)))    # max row sum
      if (pn != 2) {
        cli_abort(c(
          "Unsupported matrix norm.",
          "i" = "For the entrywise {pn}-norm, use {.code p_norm(x, {pn})}."
        ))
      }
      if (shp[1L] > 1L) return(sigma_max(x))                     # largest singular value
    }
  }
  .cvxr_norm_entrywise(x, p, axis, keepdims, max_denom)
}

## The entrywise (vector) norms: cvxr_norm()'s numeric-p behavior before the
## 1.9.3 matrix-norm change.
.cvxr_norm_entrywise <- function(x, p, axis = NULL, keepdims = FALSE, max_denom = 1024L) {
  p <- as.numeric(p)
  if (p == 1) return(norm1(x, axis, keepdims))
  if (p == Inf) return(norm_inf(x, axis, keepdims))
  p_norm(x, p, axis, keepdims, max_denom)
}

Try the CVXR package in your browser

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

CVXR documentation built on Oct. 19, 2026, 5:07 p.m.