R/135_constraints_psd.R

#####
## DO NOT EDIT THIS FILE!! EDIT THE SOURCE INSTEAD: rsrc_tree/constraints/psd.R
#####

## CVXPY SOURCE: constraints/psd.py
## PSD -- Positive Semidefinite constraint
##
##
## Holds both PSD and `SvecPSD` (CVXPY 1.9.0, PR #3268), the target of the
## PSD->SvecPSD conversion in `reductions/cone2cone/exact.R`.

#' Create a Positive Semidefinite Constraint
#'
#' Constrains a square matrix expression to be positive semidefinite (PSD):
#' \eqn{X \succeq 0}. The expression must be square.
#'
#' @param expr A CVXR expression representing a square matrix.
#' @param constr_id Optional integer constraint ID.
#' @returns A \code{PSD} constraint object.
#' @export
PSD <- new_class("PSD", parent = Cone, package = "CVXR",
  constructor = function(expr, constr_id = NULL) {
    if (FALSE) new_object(S7_object())  ## S7 static-check guard
    expr <- as_expr(expr)

    ## CVXPY SOURCE: psd.py lines 49-54
    ## Must be square matrix
    if (length(expr@shape) != 2L || expr@shape[1L] != expr@shape[2L]) {
      cli_abort("Non-square matrix in positive semidefinite constraint.")
    }

    if (is.null(constr_id)) constr_id <- next_expr_id()
    shape <- expr@shape

    args <- list(expr)
    dvars <- list(Variable(expr@shape))

    .fast_new(PSD, S7_object(),
      id    = as.integer(constr_id),
      .cache = new.env(parent = emptyenv()),
      args  = args,
      dual_variables = dvars,
      shape = shape,
      .label = ""
    )
  }
)

# -- expr_name ----------------------------------------------------
## CVXPY SOURCE: psd.py lines 57-58

method(expr_name, PSD) <- function(x) {
  sprintf("%s >> 0", expr_name(x@args[[1L]]))
}

# -- is_dcp -------------------------------------------------------
## CVXPY SOURCE: psd.py lines 60-66

method(is_dcp, PSD) <- function(x) {
  is_affine(x@args[[1L]])
}

# -- is_dgp -------------------------------------------------------
## CVXPY SOURCE: psd.py lines 68-69

method(is_dgp, PSD) <- function(x) FALSE

# -- num_cones ----------------------------------------------------

method(num_cones, PSD) <- function(x) 1L

# -- cone_sizes ---------------------------------------------------
## Returns n (the matrix dimension) -- used by solvers for PSD cone

method(cone_sizes, PSD) <- function(x) {
  x@args[[1L]]@shape[1L]
}

# -- residual -----------------------------------------------------
## CVXPY SOURCE: psd.py lines 74-86
## Returns -min(0, eigenvalues of symmetric part)

method(residual, PSD) <- function(x) {
  X_val <- value(x@args[[1L]])
  if (is.null(X_val)) return(NULL)

  ## Symmetric part
  sym <- (X_val + t(X_val)) / 2
  min_eig <- min(.eigvalsh(sym, only_values = TRUE)$values)
  max(0, -min_eig)
}

# -- dual_cone ----------------------------------------------------
## CVXPY SOURCE: psd.py lines 87-98
## PSD is self-dual

method(dual_cone, PSD) <- function(x, ...) {
  args <- list(...)
  if (length(args) == 0L) {
    PSD(x@dual_variables[[1L]])
  } else {
    PSD(args[[1L]])
  }
}

# -- SvecPSD ------------------------------------------------------
## CVXPY SOURCE: psd.py lines 125-197
##
## A PSD constraint in scaled vectorized (svec) form: the argument is a
## column of length n*(n+1)/2 holding the scaled triangle of a symmetric PSD
## matrix.  Produced by `ExactCone2Cone` for solvers that want the packed
## triangle rather than the full matrix; users do not construct it directly.
##
## `.n` is the side length of the ORIGINAL matrix, not the triangle length.

SvecPSD <- new_class("SvecPSD", parent = Cone, package = "CVXR",
  properties = list(
    .n = class_integer
  ),
  constructor = function(expr, n, constr_id = NULL) {
    if (FALSE) new_object(S7_object())  ## S7 static-check guard
    expr <- as_expr(expr)
    if (is.null(constr_id)) constr_id <- next_expr_id()

    .fast_new(SvecPSD, S7_object(),
      id    = as.integer(constr_id),
      .cache = new.env(parent = emptyenv()),
      args  = list(expr),
      dual_variables = list(Variable(expr@shape)),
      shape = expr@shape,
      .label = "",
      .n    = as.integer(n)
    )
  }
)

## CVXPY SOURCE: psd.py lines 148-150
method(get_data, SvecPSD) <- function(x) list(x@.n, x@id)

## CVXPY SOURCE: psd.py lines 152-153
method(expr_name, SvecPSD) <- function(x) {
  sprintf("svec_psd(%s, n=%d)", expr_name(x@args[[1L]]), x@.n)
}

## CVXPY SOURCE: psd.py lines 155-159
method(is_dcp, SvecPSD) <- function(x) is_affine(x@args[[1L]])

## CVXPY SOURCE: psd.py lines 161-162
method(is_dgp, SvecPSD) <- function(x) FALSE

## CVXPY SOURCE: psd.py lines 167-169
method(num_cones, SvecPSD) <- function(x) {
  tri_dim <- (x@.n * (x@.n + 1L)) %/% 2L
  as.integer(expr_size(x@args[[1L]]) %/% tri_dim)
}

## CVXPY SOURCE: psd.py lines 175-176
method(cone_sizes, SvecPSD) <- function(x) rep(x@.n, num_cones(x))

## CVXPY SOURCE: psd.py lines 178-180 -- the svec length, NOT n^2.
method(constr_size, SvecPSD) <- function(x) {
  as.integer((x@.n * (x@.n + 1L)) %/% 2L * num_cones(x))
}

## CVXPY SOURCE: psd.py lines 182-189 -- deliberately NOT implemented upstream
## either: the residual is only meaningful on the original PSD constraint,
## which is what `ExactCone2Cone` maps the solution back onto.
method(residual, SvecPSD) <- function(x) {
  if (is.null(value(x@args[[1L]]))) return(NULL)
  cli_abort(c(
    "{.fn residual} is not implemented for {.cls SvecPSD}.",
    "i" = "Check the residual on the original {.cls PSD} constraint instead."
  ))
}

## CVXPY SOURCE: psd.py lines 191-197 -- the PSD cone is self-dual.
method(dual_cone, SvecPSD) <- function(x, ...) {
  args <- list(...)
  if (length(args) == 0L) {
    SvecPSD(x@dual_variables[[1L]], n = x@.n)
  } else {
    SvecPSD(args[[1L]], n = x@.n)
  }
}

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.