R/253_zzz_R_specific_visualize_annotations.R

Defines functions .auto_developer .smith_relation .latex_sign .latex_curvature .latex_shape .latex_class_name .clean_class_name

#####
## DO NOT EDIT THIS FILE!! EDIT THE SOURCE INSTEAD: rsrc_tree/zzz_R_specific/visualize_annotations.R
#####

## R-SPECIFIC: Smith form annotations for visualize()
##
## Each atom class provides a smith_annotation() method that returns LaTeX-math
## descriptions for the Smith form pipeline stages. The null object pattern
## ensures every atom returns something -- atoms without custom annotations
## get an auto-generated stub.
##
## Architecture:
##   smith_annotation(expr) -> list(
##     latex_name,         # atom symbol: $\varphi^{|\cdot|}$
##     latex_definition,   # what it computes: $|x|$
##     conic,              # list of LaTeX conic constraints, or NULL (stub)
##     doc_topic,          # for clickable doc links
##     developer           # implementation anatomy (files, DCP props, etc.)
##   )


# ==========================================================================
# S7 generic
# ==========================================================================

#' Smith Form Annotation for an Expression Node
#'
#' Returns LaTeX-math annotation data for visualizing the canonicalization
#' pipeline. Each atom class can override this to provide a custom LaTeX name,
#' definition, and conic form. The default stub auto-generates from class
#' metadata.
#'
#' @param expr An Expression, Atom, or Leaf.
#' @param aux_var Character: the auxiliary variable name assigned to this node (e.g., "t_3").
#' @param child_vars Character vector: auxiliary variable names of the children.
#' @param ... Reserved for future use.
#' @returns A list with components: latex_name, latex_definition, conic, doc_topic, developer.
#' @keywords internal
smith_annotation <- new_generic("smith_annotation", "expr",
  function(expr, aux_var = "t", child_vars = character(0), ...) {
    S7_dispatch()
  }
)


# ==========================================================================
# Helpers
# ==========================================================================

## Clean class name: strip CVXR:: prefix
.clean_class_name <- function(expr) {
  sub("^CVXR::", "", class(expr)[[1L]])
}

## LaTeX-safe class name: strip prefix, escape underscores
.latex_class_name <- function(expr) {
  gsub("_", "\\\\_", .clean_class_name(expr))
}

## Build shape string like "(m, 1)"
.latex_shape <- function(expr) {
  paste0("(", paste(expr@shape, collapse = ", "), ")")
}

## Curvature as LaTeX-friendly string
.latex_curvature <- function(expr) {
  if (is_constant(expr)) return("constant")
  if (is_affine(expr))   return("affine")
  if (is_convex(expr))   return("convex")
  if (is_concave(expr))  return("concave")
  "unknown"
}

## Sign as LaTeX-friendly string
.latex_sign <- function(expr) {
  if (is_zero(expr))    return("zero")
  if (is_nonneg(expr))  return("\\mathbb{R}_+")
  if (is_nonpos(expr))  return("\\mathbb{R}_-")
  "?"
}

## Relation symbol for relaxed Smith form based on curvature
.smith_relation <- function(expr) {
  if (is_constant(expr) || is_affine(expr)) return("=")
  if (is_convex(expr))  return("\\geq")
  if (is_concave(expr)) return("\\leq")
  "="
}

## Auto-generate developer info from introspection
.auto_developer <- function(expr) {
  cls_name <- .clean_class_name(expr)
  parent_name <- tryCatch(
    sub("^CVXR::", "", class(S7_class(expr)@parent)[[1L]]),
    error = function(e) "unknown"
  )
  has_canon <- tryCatch(has_dcp_canon(expr), error = function(e) FALSE)
  list(
    class_file    = NULL,
    canon_file    = if (has_canon) "(registered)" else "(none \\textemdash\\ affine atom)",
    parent_class  = parent_name,
    dcp_properties = list(
      is_atom_convex  = tryCatch(is_atom_convex(expr), error = function(e) NA),
      is_atom_concave = tryCatch(is_atom_concave(expr), error = function(e) NA),
      sign            = .latex_sign(expr)
    ),
    canon_pattern = if (has_canon) "custom" else "identity (affine)",
    canon_summary = NULL
  )
}


# ==========================================================================
# Default: Atom (null object stub)
# ==========================================================================

method(smith_annotation, Atom) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  cls_latex <- .latex_class_name(expr)
  args_str <- paste(child_vars, collapse = ", ")
  list(
    latex_name       = sprintf("\\varphi^{\\texttt{%s}}", cls_latex),
    latex_definition = sprintf("\\texttt{%s}(%s)", cls_latex, args_str),
    smith            = sprintf("$%s = \\varphi^{\\texttt{%s}}(%s)$", aux_var, cls_latex, args_str),
    relaxed          = sprintf("$%s %s \\varphi^{\\texttt{%s}}(%s)$",
                               aux_var, .smith_relation(expr), cls_latex, args_str),
    conic            = NULL,
    doc_topic        = class(expr)[[1L]],
    developer        = .auto_developer(expr)
  )
}


# ==========================================================================
# Leaves: Variable, Constant, Parameter
# ==========================================================================

method(smith_annotation, Variable) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  vname <- expr_name(expr)
  shape_str <- .latex_shape(expr)
  list(
    latex_name       = sprintf("\\mathbf{%s}", vname),
    latex_definition = sprintf("\\mathbf{%s} \\in \\mathbb{R}^{%s}",
                               vname, paste(expr@shape, collapse = " \\times ")),
    smith            = sprintf("$\\mathbf{%s}$", vname),
    relaxed          = sprintf("$\\mathbf{%s}$", vname),
    conic            = NULL,
    doc_topic        = "Variable",
    developer        = list(
      class_file   = "expressions/variable.R",
      canon_file   = "(terminal \\textemdash\\ no canonicalizer)",
      parent_class = "Leaf",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = TRUE, sign = "?"),
      canon_pattern  = "terminal",
      canon_summary  = "Variables are leaf nodes; no canonicalization needed"
    )
  )
}

method(smith_annotation, Constant) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  ## Try to show small constants literally
  val <- tryCatch(value(expr), error = function(e) NULL)
  if (!is.null(val) && length(val) == 1L) {
    val_str <- format(val, digits = 4)
  } else {
    val_str <- sprintf("C_{%s}", .latex_shape(expr))
  }
  list(
    latex_name       = val_str,
    latex_definition = val_str,
    smith            = sprintf("$%s$", val_str),
    relaxed          = sprintf("$%s$", val_str),
    conic            = NULL,
    doc_topic        = "Constant",
    developer        = list(
      class_file   = "expressions/constants/constant.R",
      canon_file   = "(terminal \\textemdash\\ constant)",
      parent_class = "Leaf",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = TRUE, sign = .latex_sign(expr)),
      canon_pattern  = "terminal",
      canon_summary  = "Constants are leaf nodes; folded into affine data"
    )
  )
}

method(smith_annotation, Parameter) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  pname <- expr_name(expr)
  list(
    latex_name       = sprintf("\\theta_{\\text{%s}}", pname),
    latex_definition = sprintf("\\theta_{\\text{%s}} \\in \\mathbb{R}^{%s}",
                               pname, paste(expr@shape, collapse = " \\times ")),
    smith            = sprintf("$\\theta_{\\text{%s}}$", pname),
    relaxed          = sprintf("$\\theta_{\\text{%s}}$", pname),
    conic            = NULL,
    doc_topic        = "Parameter",
    developer        = list(
      class_file   = "expressions/constants/parameter.R",
      canon_file   = "(terminal \\textemdash\\ parameter)",
      parent_class = "Leaf",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = TRUE, sign = .latex_sign(expr)),
      canon_pattern  = "terminal",
      canon_summary  = "Parameters are leaf nodes; values substituted at solve time"
    )
  )
}


# ==========================================================================
# P0 Affine Atoms
# ==========================================================================

method(smith_annotation, AddExpression) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  ## Smart joining: if a term starts with "- " or "-\\", use subtraction
  if (length(child_vars) == 0L) {
    args_str <- ""
  } else {
    parts <- child_vars[[1L]]
    for (j in seq_along(child_vars)[-1L]) {
      cv <- child_vars[[j]]
      if (grepl("^-\\s", cv) || grepl("^-\\\\", cv)) {
        ## Already negated: use " - rest" instead of " + -rest"
        parts <- paste0(parts, " ", cv)
      } else {
        parts <- paste0(parts, " + ", cv)
      }
    }
    args_str <- parts
  }
  list(
    latex_name       = "\\varphi^{+}",
    latex_definition = sprintf("%s", args_str),
    smith            = sprintf("$%s = %s$", aux_var, args_str),
    relaxed          = sprintf("$%s = %s$", aux_var, args_str),
    conic            = list(sprintf("$%s = %s$", aux_var, args_str)),
    doc_topic        = "AddExpression",
    developer        = list(
      class_file    = "atoms/affine/add_expr.R",
      canon_file    = "(none \\textemdash\\ affine atom)",
      parent_class  = "AffAtom",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = TRUE, sign = "?"),
      canon_pattern  = "identity (affine)",
      canon_summary  = "Affine: passed through to LinOp tree as \\texttt{LINOP\\_SUM}"
    )
  )
}

method(smith_annotation, NegExpression) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "x"
  list(
    latex_name       = "\\varphi^{-}",
    latex_definition = sprintf("-%s", x),
    smith            = sprintf("$%s = -%s$", aux_var, x),
    relaxed          = sprintf("$%s = -%s$", aux_var, x),
    conic            = list(sprintf("$%s = -%s$", aux_var, x)),
    doc_topic        = "NegExpression",
    developer        = list(
      class_file    = "atoms/affine/unary_operators.R",
      canon_file    = "(none \\textemdash\\ affine atom)",
      parent_class  = "AffAtom",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = TRUE, sign = "?"),
      canon_pattern  = "identity (affine)",
      canon_summary  = "Affine: passed through to LinOp tree as \\texttt{LINOP\\_NEG}"
    )
  )
}

method(smith_annotation, MulExpression) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  lhs <- if (length(child_vars) >= 1L) child_vars[[1L]] else "A"
  rhs <- if (length(child_vars) >= 2L) child_vars[[2L]] else "\\mathbf{x}"
  list(
    latex_name       = "\\varphi^{\\times}",
    latex_definition = sprintf("%s %s", lhs, rhs),
    smith            = sprintf("$%s = %s %s$", aux_var, lhs, rhs),
    relaxed          = sprintf("$%s = %s %s$", aux_var, lhs, rhs),
    conic            = list(sprintf("$%s = %s %s$", aux_var, lhs, rhs)),
    doc_topic        = "MulExpression",
    developer        = list(
      class_file    = "atoms/affine/binary_operators.R",
      canon_file    = "(none \\textemdash\\ affine atom)",
      parent_class  = "AffAtom",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = TRUE, sign = "?"),
      canon_pattern  = "identity (affine)",
      canon_summary  = "Affine: passed through to LinOp tree as \\texttt{LINOP\\_MUL}"
    )
  )
}

method(smith_annotation, Multiply) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  lhs <- if (length(child_vars) >= 1L) child_vars[[1L]] else "a"
  rhs <- if (length(child_vars) >= 2L) child_vars[[2L]] else "x"
  list(
    latex_name       = "\\varphi^{\\odot}",
    latex_definition = sprintf("%s \\odot %s", lhs, rhs),
    smith            = sprintf("$%s = %s \\odot %s$", aux_var, lhs, rhs),
    relaxed          = sprintf("$%s = %s \\odot %s$", aux_var, lhs, rhs),
    conic            = list(sprintf("$%s = %s \\odot %s$", aux_var, lhs, rhs)),
    doc_topic        = "Multiply",
    developer        = list(
      class_file    = "atoms/affine/binary_operators.R",
      canon_file    = "(none \\textemdash\\ affine atom)",
      parent_class  = "AffAtom",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = TRUE, sign = "?"),
      canon_pattern  = "identity (affine)",
      canon_summary  = "Elementwise multiply: affine when one arg is constant"
    )
  )
}

method(smith_annotation, DivExpression) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  lhs <- if (length(child_vars) >= 1L) child_vars[[1L]] else "x"
  rhs <- if (length(child_vars) >= 2L) child_vars[[2L]] else "a"
  list(
    latex_name       = "\\varphi^{\\div}",
    latex_definition = sprintf("%s / %s", lhs, rhs),
    smith            = sprintf("$%s = %s / %s$", aux_var, lhs, rhs),
    relaxed          = sprintf("$%s = %s / %s$", aux_var, lhs, rhs),
    conic            = list(sprintf("$%s = %s / %s$", aux_var, lhs, rhs)),
    doc_topic        = "DivExpression",
    developer        = list(
      class_file    = "atoms/affine/binary_operators.R",
      canon_file    = "(none \\textemdash\\ affine atom)",
      parent_class  = "AffAtom",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = TRUE, sign = "?"),
      canon_pattern  = "identity (affine)",
      canon_summary  = "Division by positive constant: affine"
    )
  )
}

method(smith_annotation, Transpose) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "X"
  list(
    latex_name       = "\\varphi^{\\top}",
    latex_definition = sprintf("%s^\\top", x),
    smith            = sprintf("$%s = %s^\\top$", aux_var, x),
    relaxed          = sprintf("$%s = %s^\\top$", aux_var, x),
    conic            = list(sprintf("$%s = %s^\\top$", aux_var, x)),
    doc_topic        = "Transpose",
    developer        = list(
      class_file    = "atoms/affine/transpose.R",
      canon_file    = "(none \\textemdash\\ affine atom)",
      parent_class  = "AffAtom",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = TRUE, sign = "?"),
      canon_pattern  = "identity (affine)",
      canon_summary  = "Affine: passed through to LinOp tree as \\texttt{LINOP\\_TRANSPOSE}"
    )
  )
}

method(smith_annotation, Reshape) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "X"
  list(
    latex_name       = "\\varphi^{\\text{vec}}",
    latex_definition = sprintf("\\operatorname{vec}(%s)", x),
    smith            = sprintf("$%s = \\operatorname{vec}(%s)$", aux_var, x),
    relaxed          = sprintf("$%s = \\operatorname{vec}(%s)$", aux_var, x),
    conic            = list(sprintf("$%s = \\operatorname{vec}(%s)$", aux_var, x)),
    doc_topic        = "Reshape",
    developer        = list(
      class_file    = "atoms/affine/reshape.R",
      canon_file    = "(none \\textemdash\\ affine atom)",
      parent_class  = "AffAtom",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = TRUE, sign = "?"),
      canon_pattern  = "identity (affine)",
      canon_summary  = "Affine: passed through to LinOp tree as \\texttt{LINOP\\_RESHAPE}"
    )
  )
}

method(smith_annotation, SumEntries) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "\\mathbf{x}"
  list(
    latex_name       = "\\varphi^{\\Sigma}",
    latex_definition = sprintf("\\mathbf{1}^\\top %s", x),
    smith            = sprintf("$%s = \\varphi^{\\Sigma}(%s)$", aux_var, x),
    relaxed          = sprintf("$%s = \\mathbf{1}^\\top %s$", aux_var, x),
    conic            = list(sprintf("$%s = \\mathbf{1}^\\top %s$", aux_var, x)),
    doc_topic        = "SumEntries",
    developer        = list(
      class_file    = "atoms/affine/sum.R",
      canon_file    = "(none \\textemdash\\ affine atom)",
      parent_class  = "AxisAffAtom",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = TRUE, sign = "?"),
      canon_pattern  = "identity (affine)",
      canon_summary  = "Affine sum: $\\mathbf{1}^\\top \\mathbf{x}$"
    )
  )
}


# ==========================================================================
# P0 Convex Atoms (with full conic annotations)
# ==========================================================================

## -- Abs ------------------------------------------------------------------

method(smith_annotation, Abs) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "x"
  list(
    latex_name       = "\\varphi^{|\\cdot|}",
    latex_definition = sprintf("|%s|", x),
    smith            = sprintf("$%s = \\varphi^{|\\cdot|}(%s)$", aux_var, x),
    relaxed          = sprintf("$%s \\geq \\varphi^{|\\cdot|}(%s)$", aux_var, x),
    conic            = list(sprintf("$(%s, %s) \\in \\mathcal{Q}^2$", aux_var, x)),
    doc_topic        = "Abs",
    developer        = list(
      class_file    = "atoms/elementwise/abs.R",
      canon_file    = "reductions/dcp2cone/canonicalizers/abs_canon.R",
      parent_class  = "Elementwise",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = FALSE,
                            sign = "\\mathbb{R}_+"),
      canon_pattern  = "new_variable_plus_constraints",
      canon_summary  = "Introduces $t$ with $t \\geq x$ and $t \\geq -x$"
    )
  )
}


## -- Pnorm (p=2 special case, general p) ---------------------------------

method(smith_annotation, Pnorm) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "\\mathbf{x}"
  p <- expr@p

  if (p == 2) {
    list(
      latex_name       = "\\varphi^{\\lVert\\cdot\\rVert_2}",
      latex_definition = sprintf("\\lVert %s \\rVert_2", x),
      smith            = sprintf("$%s = \\varphi^{\\lVert\\cdot\\rVert_2}(%s)$", aux_var, x),
      relaxed          = sprintf("$%s \\geq \\varphi^{\\lVert\\cdot\\rVert_2}(%s)$", aux_var, x),
      conic            = list(sprintf("$(%s, %s) \\in \\mathcal{Q}^{n+1}$", aux_var, x)),
      doc_topic        = "Pnorm",
      developer        = list(
        class_file    = "atoms/pnorm.R",
        canon_file    = "reductions/dcp2cone/canonicalizers/pnorm_canon.R",
        parent_class  = "AxisAtom",
        dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = FALSE,
                              sign = "\\mathbb{R}_+"),
        canon_pattern  = "new_variable_plus_SOC",
        canon_summary  = sprintf("SOC constraint: $(%s, %s) \\in \\mathcal{Q}^{n+1}$", aux_var, x)
      )
    )
  } else {
    p_str <- format(p, digits = 4)
    rel <- if (p >= 1) "\\geq" else "\\leq"
    list(
      latex_name       = sprintf("\\varphi^{\\lVert\\cdot\\rVert_{%s}}", p_str),
      latex_definition = sprintf("\\lVert %s \\rVert_{%s}", x, p_str),
      smith            = sprintf("$%s = \\varphi^{\\lVert\\cdot\\rVert_{%s}}(%s)$", aux_var, p_str, x),
      relaxed          = sprintf("$%s %s \\varphi^{\\lVert\\cdot\\rVert_{%s}}(%s)$",
                                 aux_var, rel, p_str, x),
      conic            = list(sprintf("$\\text{PowCone3D constraints for } p = %s$", p_str)),
      doc_topic        = "Pnorm",
      developer        = list(
        class_file    = "atoms/pnorm.R",
        canon_file    = "reductions/dcp2cone/canonicalizers/pnorm_canon.R",
        parent_class  = "AxisAtom",
        dcp_properties = list(is_atom_convex = (p >= 1), is_atom_concave = (p < 1),
                              sign = "\\mathbb{R}_+"),
        canon_pattern  = "new_variable_plus_PowCone",
        canon_summary  = sprintf("Power cone decomposition for $p = %s$", p_str)
      )
    )
  }
}


## -- Power (covers square as p=2 special case) ---------------------------

method(smith_annotation, Power) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "x"
  p <- expr@p_used

  if (!is.null(p) && p == 2) {
    ## Square: x^2, convex
    list(
      latex_name       = "\\varphi^{(\\cdot)^2}",
      latex_definition = sprintf("%s^2", x),
      smith            = sprintf("$%s = \\varphi^{(\\cdot)^2}(%s)$", aux_var, x),
      relaxed          = sprintf("$%s \\geq \\varphi^{(\\cdot)^2}(%s)$", aux_var, x),
      conic            = list(
        sprintf("$\\left(\\frac{1+%s}{2},\\; \\frac{1-%s}{2},\\; %s\\right) \\in \\mathcal{Q}^3$",
                aux_var, aux_var, x)
      ),
      doc_topic        = "Power",
      developer        = list(
        class_file    = "atoms/elementwise/power.R",
        canon_file    = "reductions/dcp2cone/canonicalizers/power_canon.R",
        parent_class  = "Elementwise",
        dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = FALSE,
                              sign = "\\mathbb{R}_+"),
        canon_pattern  = "delegating",
        canon_summary  = "$x^2$ canonicalized via $\\varphi^{\\text{qol}}(x, 1)$ (quad\\_over\\_lin)"
      )
    )
  } else {
    ## General power
    p_str <- if (!is.null(p)) format(p, digits = 4) else "p"
    curv <- .latex_curvature(expr)
    rel <- .smith_relation(expr)
    list(
      latex_name       = sprintf("\\varphi^{(\\cdot)^{%s}}", p_str),
      latex_definition = sprintf("%s^{%s}", x, p_str),
      smith            = sprintf("$%s = \\varphi^{(\\cdot)^{%s}}(%s)$", aux_var, p_str, x),
      relaxed          = sprintf("$%s %s \\varphi^{(\\cdot)^{%s}}(%s)$", aux_var, rel, p_str, x),
      conic            = NULL,
      doc_topic        = "Power",
      developer        = list(
        class_file    = "atoms/elementwise/power.R",
        canon_file    = "reductions/dcp2cone/canonicalizers/power_canon.R",
        parent_class  = "Elementwise",
        dcp_properties = list(is_atom_convex = is_atom_convex(expr),
                              is_atom_concave = is_atom_concave(expr),
                              sign = .latex_sign(expr)),
        canon_pattern  = "power_cone",
        canon_summary  = sprintf("Power $p = %s$: %s, uses power cone decomposition", p_str, curv)
      )
    )
  }
}


## -- QuadOverLin ---------------------------------------------------------

method(smith_annotation, QuadOverLin) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "\\mathbf{x}"
  y <- if (length(child_vars) >= 2L) child_vars[[2L]] else "y"
  list(
    latex_name       = "\\varphi^{\\text{qol}}",
    latex_definition = sprintf("\\frac{\\lVert %s \\rVert_2^2}{%s}", x, y),
    smith            = sprintf("$%s = \\varphi^{\\text{qol}}(%s, %s)$", aux_var, x, y),
    relaxed          = sprintf("$%s \\geq \\varphi^{\\text{qol}}(%s, %s)$", aux_var, x, y),
    conic            = list(
      sprintf("$\\left(\\frac{%s+%s}{2},\\; \\frac{%s-%s}{2},\\; %s\\right) \\in \\mathcal{Q}^{n+2}$",
              y, aux_var, y, aux_var, x),
      sprintf("$%s \\in \\mathcal{Q}^1$", y)
    ),
    doc_topic        = "QuadOverLin",
    developer        = list(
      class_file    = "atoms/quad_over_lin.R",
      canon_file    = "reductions/dcp2cone/canonicalizers/quad_over_lin_canon.R",
      parent_class  = "AxisAtom",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = FALSE,
                            sign = "\\mathbb{R}_+"),
      canon_pattern  = "new_variable_plus_SOC",
      canon_summary  = "Rotated SOC: $\\lVert\\mathbf{x}\\rVert_2^2 \\leq t \\cdot y$"
    )
  )
}


## -- QuadForm ------------------------------------------------------------

method(smith_annotation, QuadForm) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "\\mathbf{x}"
  P <- if (length(child_vars) >= 2L) child_vars[[2L]] else "P"
  curv <- .latex_curvature(expr)
  rel <- .smith_relation(expr)
  list(
    latex_name       = "\\varphi^{\\text{quad}}",
    latex_definition = sprintf("%s^\\top %s %s", x, P, x),
    smith            = sprintf("$%s = \\varphi^{\\text{quad}}(%s, %s)$", aux_var, x, P),
    relaxed          = sprintf("$%s %s \\varphi^{\\text{quad}}(%s, %s)$", aux_var, rel, x, P),
    conic            = list(
      sprintf("$%s %s \\lVert P^{1/2} %s \\rVert_2^2 \\text{ via eigendecomposition} \\to \\text{SOC}$",
              aux_var, rel, x)
    ),
    doc_topic        = "QuadForm",
    developer        = list(
      class_file    = "atoms/quad_form.R",
      canon_file    = "reductions/dcp2cone/canonicalizers/quad_form_canon.R",
      parent_class  = "Atom",
      dcp_properties = list(is_atom_convex = is_atom_convex(expr),
                            is_atom_concave = is_atom_concave(expr),
                            sign = .latex_sign(expr)),
      canon_pattern  = "delegating",
      canon_summary  = "Eigendecomposes $P$, then delegates to $\\varphi^{\\text{qol}}$"
    )
  )
}


## -- MaxEntries ----------------------------------------------------------

method(smith_annotation, MaxEntries) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "\\mathbf{x}"
  list(
    latex_name       = "\\varphi^{\\max}",
    latex_definition = sprintf("\\max\\{%s_1, \\ldots, %s_n\\}", x, x),
    smith            = sprintf("$%s = \\varphi^{\\max}(%s)$", aux_var, x),
    relaxed          = sprintf("$%s \\geq \\varphi^{\\max}(%s)$", aux_var, x),
    conic            = list(
      sprintf("$%s - %s_i \\in \\mathcal{Q}^1,\\; i = 1, \\ldots, n$", aux_var, x)
    ),
    doc_topic        = "MaxEntries",
    developer        = list(
      class_file    = "atoms/max.R",
      canon_file    = "reductions/dcp2cone/canonicalizers/max_canon.R",
      parent_class  = "AxisAtom",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = FALSE, sign = "?"),
      canon_pattern  = "new_variable_plus_constraints",
      canon_summary  = "$n$ nonneg constraints: $t \\geq x_i$ for all $i$"
    )
  )
}


## -- MinEntries ----------------------------------------------------------

method(smith_annotation, MinEntries) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "\\mathbf{x}"
  list(
    latex_name       = "\\varphi^{\\min}",
    latex_definition = sprintf("\\min\\{%s_1, \\ldots, %s_n\\}", x, x),
    smith            = sprintf("$%s = \\varphi^{\\min}(%s)$", aux_var, x),
    relaxed          = sprintf("$%s \\leq \\varphi^{\\min}(%s)$", aux_var, x),
    conic            = list(
      sprintf("$%s_i - %s \\in \\mathcal{Q}^1,\\; i = 1, \\ldots, n$", x, aux_var)
    ),
    doc_topic        = "MinEntries",
    developer        = list(
      class_file    = "atoms/min.R",
      canon_file    = "reductions/dcp2cone/canonicalizers/min_canon.R",
      parent_class  = "AxisAtom",
      dcp_properties = list(is_atom_convex = FALSE, is_atom_concave = TRUE, sign = "?"),
      canon_pattern  = "new_variable_plus_constraints",
      canon_summary  = "$n$ nonneg constraints: $x_i \\geq t$ for all $i$"
    )
  )
}


## -- Maximum (elementwise max of two expressions) ------------------------

method(smith_annotation, Maximum) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "x"
  y <- if (length(child_vars) >= 2L) child_vars[[2L]] else "y"
  list(
    latex_name       = "\\varphi^{\\max}",
    latex_definition = sprintf("\\max(%s, %s)", x, y),
    smith            = sprintf("$%s = \\varphi^{\\max}(%s, %s)$", aux_var, x, y),
    relaxed          = sprintf("$%s \\geq \\varphi^{\\max}(%s, %s)$", aux_var, x, y),
    conic            = list(
      sprintf("$%s \\geq %s,\\; %s \\geq %s$", aux_var, x, aux_var, y)
    ),
    doc_topic        = "Maximum",
    developer        = list(
      class_file    = "atoms/elementwise/maximum.R",
      canon_file    = "reductions/dcp2cone/canonicalizers/max_elemwise_canon.R",
      parent_class  = "Elementwise",
      dcp_properties = list(is_atom_convex = TRUE, is_atom_concave = FALSE, sign = "?"),
      canon_pattern  = "new_variable_plus_constraints",
      canon_summary  = "Introduces $t$ with $t \\geq x$ and $t \\geq y$"
    )
  )
}


## -- Minimum (elementwise min of two expressions) ------------------------

method(smith_annotation, Minimum) <- function(expr, aux_var = "t", child_vars = character(0), ...) {
  x <- if (length(child_vars) >= 1L) child_vars[[1L]] else "x"
  y <- if (length(child_vars) >= 2L) child_vars[[2L]] else "y"
  list(
    latex_name       = "\\varphi^{\\min}",
    latex_definition = sprintf("\\min(%s, %s)", x, y),
    smith            = sprintf("$%s = \\varphi^{\\min}(%s, %s)$", aux_var, x, y),
    relaxed          = sprintf("$%s \\leq \\varphi^{\\min}(%s, %s)$", aux_var, x, y),
    conic            = list(
      sprintf("$%s \\leq %s,\\; %s \\leq %s$", aux_var, x, aux_var, y)
    ),
    doc_topic        = "Minimum",
    developer        = list(
      class_file    = "atoms/elementwise/minimum.R",
      canon_file    = "reductions/dcp2cone/canonicalizers/min_elemwise_canon.R",
      parent_class  = "Elementwise",
      dcp_properties = list(is_atom_convex = FALSE, is_atom_concave = TRUE, sign = "?"),
      canon_pattern  = "new_variable_plus_constraints",
      canon_summary  = "Introduces $t$ with $t \\leq x$ and $t \\leq y$"
    )
  )
}

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.