R/115_atoms_perspective.R

Defines functions perspective

Documented in perspective

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

## CVXPY SOURCE: atoms/perspective.py
## Perspective -- perspective transform of convex/concave scalar expression
##
## Given a scalar expression f and nonneg variable s, the perspective is
## the function t >= s*f(x/s). This exploits the cone representation of
## f's epigraph to build a lifted conic program.


# -- Perspective class --------------------------------------------
## CVXPY SOURCE: perspective.py lines 30-153

Perspective <- new_class("Perspective", parent = Atom, package = "CVXR",
  properties = list(
    .f           = class_any,  # The scalar expression being transformed
    .f_recession = class_any   # Optional recession function for s=0
  ),
  constructor = function(f, s, f_recession = NULL) {
    id <- next_expr_id()
    f <- as_expr(f)
    args <- c(list(s), variables(f))
    shape <- f@shape

    obj <- new_object(S7_object(),
      id     = as.integer(id),
      .cache = new.env(parent = emptyenv()),
      args   = args,
      shape  = shape,
      .f           = f,
      .f_recession = f_recession
    )
    validate_arguments(obj)
    obj
  }
)

# -- validate_arguments -------------------------------------------
## CVXPY SOURCE: perspective.py lines 56-61
method(validate_arguments, Perspective) <- function(x) {
  if (expr_size(x@.f) != 1L) {
    cli_abort("Perspective requires a scalar function f, got size {expr_size(x@.f)}.")
  }
  s <- x@args[[1L]]
  if (expr_size(s) != 1L) {
    cli_abort("Perspective requires scalar s, got size {expr_size(s)}.")
  }
  if (!S7_inherits(s, Variable)) {
    cli_abort("s must be a {.cls Variable}.")
  }
  if (!is_nonneg(s)) {
    cli_abort("s must be a nonnegative variable.")
  }
}

# -- sign ---------------------------------------------------------
## CVXPY SOURCE: perspective.py lines 97-107
method(sign_from_args, Perspective) <- function(x) {
  f_pos <- is_nonneg(x@.f)
  f_neg <- is_nonpos(x@.f)
  s_pos <- is_nonneg(x@args[[1L]])
  list(is_nonneg = f_pos && s_pos,
       is_nonpos = f_neg && s_pos)
}

# -- curvature ----------------------------------------------------
## CVXPY SOURCE: perspective.py lines 109-123
## With DPP scope: non-param-free f -> not convex/concave (forces EvalParams)
method(is_atom_convex, Perspective) <- function(x) {
  if (dpp_scope_active() && !is_param_free(x@.f)) return(FALSE)
  is_convex(x@.f) && is_nonneg(x@args[[1L]])
}

method(is_atom_concave, Perspective) <- function(x) {
  if (dpp_scope_active() && !is_param_free(x@.f)) return(FALSE)
  is_concave(x@.f) && is_nonneg(x@args[[1L]])
}

# -- monotonicity -------------------------------------------------
## CVXPY SOURCE: perspective.py lines 125-133
method(is_incr, Perspective) <- function(x, idx, ...) FALSE
method(is_decr, Perspective) <- function(x, idx, ...) FALSE

# -- shape --------------------------------------------------------
## CVXPY SOURCE: perspective.py lines 135-138
method(shape_from_args, Perspective) <- function(x) x@.f@shape

# -- numeric ------------------------------------------------------
## CVXPY SOURCE: perspective.py lines 63-95
## Compute s * f(x / s) numerically.
method(numeric_value, Perspective) <- function(x, values, ...) {
  s_val <- as.numeric(values[[1L]])
  if (s_val < 0) cli_abort("s must be nonneg, got {s_val}.")

  f <- x@.f
  if (abs(s_val) < .Machine$double.eps) {
    ## Handle s = 0 with recession function
    if (is.null(x@.f_recession)) {
      cli_abort("To handle s = 0, pass in a recession function f_recession.")
    }
    f <- x@.f_recession
    s_val <- 1.0
  }

  ## Save old variable values, set x/s, compute, restore
  f_vars <- variables(f)
  old_vals <- lapply(f_vars, function(v) value(v))

  for (i in seq_along(f_vars)) {
    value(f_vars[[i]]) <- values[[i + 1L]] / s_val
  }

  ret <- value(f) * s_val

  ## Restore
  for (i in seq_along(f_vars)) {
    value(f_vars[[i]]) <- old_vals[[i]]
  }

  as.numeric(ret)
}

# -- graph_implementation stub ------------------------------------
method(graph_implementation, Perspective) <- function(x, arg_objs, shape, data = NULL, ...) {
  cli_abort("graph_implementation for {.cls Perspective} not implemented; use Dcp2Cone.")
}

# -- User-facing constructor --------------------------------------
#' Perspective Transform
#'
#' Creates the perspective transform of a scalar convex or concave expression.
#' Given a scalar expression \code{f(x)} and a nonneg variable \code{s},
#' the perspective is \code{s * f(x/s)}.
#'
#' @param f A scalar convex or concave Expression.
#' @param s A nonneg Variable (scalar).
#' @param f_recession Optional recession function for handling \code{s = 0}.
#' @returns A \code{Perspective} expression.
#' @export
perspective <- function(f, s, f_recession = NULL) {
  Perspective(f, s, f_recession)
}

Try the CVXR package in your browser

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

CVXR documentation built on March 6, 2026, 9:10 a.m.