Nothing
#####
## DO NOT EDIT THIS FILE!! EDIT THE SOURCE INSTEAD: rsrc_tree/zzz_R_specific/to_latex.R
#####
## R-SPECIFIC: LaTeX export for CVXR expressions, constraints, and problems
##
## Renders Problem/Expression/Constraint objects as publication-quality LaTeX.
## Output uses macros from dcp.sty (shipped in inst/sty/dcp.sty).
##
## Architecture:
## to_latex(Problem) -> optidef mini*/maxi* environment
## to_latex(Expression) -> math-mode LaTeX string
## to_latex(Constraint) -> constraint relation string
## to_latex(Objective) -> objective expression
##
## Internal workhorse: .to_latex_prec(x, names_map) returns list(latex, prec)
## for parenthesization control.
##
## Design: ZERO changes to core files. Generic defined here (like smith_annotation
## in visualize_annotations.R). All method() registrations are in this file.
# ==========================================================================
# S7 generic
# ==========================================================================
#' Convert CVXR Object to LaTeX
#'
#' Renders a CVXR \code{Problem}, \code{Expression}, or \code{Constraint}
#' as a LaTeX string. Problem-level output uses the \code{optidef} package
#' (\code{mini*}/\code{maxi*} environments) and atom macros from
#' \code{dcp.sty} (shipped as \code{system.file("sty", "dcp.sty", package = "CVXR")}).
#'
#' @param x A \code{Problem}, \code{Expression}, \code{Constraint}, or
#' \code{Objective}.
#' @param ... Reserved for future options.
#' @returns A character string containing LaTeX code.
#'
#' @examples
#' x <- Variable(3, name = "x")
#' cat(to_latex(p_norm(x, 2)))
#' # \cvxnorm{x}_2
#'
#' @export
to_latex <- new_generic("to_latex", "x",
function(x, ...) S7_dispatch()
)
# ==========================================================================
# Precedence constants
# ==========================================================================
.LATEX_PREC <- list(
COMPARE = 0L,
ADD = 10L,
MUL = 20L,
UNARY = 30L,
POW = 40L,
FUNC = 50L,
ATOM = 60L
)
# ==========================================================================
# Greek letter mapping
# ==========================================================================
.GREEK_LETTERS <- c(
"alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta",
"iota", "kappa", "lambda", "mu", "nu", "xi", "pi", "rho",
"sigma", "tau", "upsilon", "phi", "chi", "psi", "omega",
## Uppercase
"Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma",
"Upsilon", "Phi", "Psi", "Omega"
)
# ==========================================================================
# Name conversion helpers
# ==========================================================================
#' Convert a variable/parameter name to LaTeX
#' @param nm Character string name
#' @returns LaTeX representation
#' @noRd
.name_to_latex <- function(nm) {
if (nchar(nm) == 0L) return("")
## Single letter: pass through
if (nchar(nm) == 1L) return(nm)
## Greek letter with trailing digits -> \greek_{digits}
for (gl in .GREEK_LETTERS) {
if (startsWith(nm, gl)) {
suffix <- substring(nm, nchar(gl) + 1L)
if (nchar(suffix) == 0L) {
return(paste0("\\", gl))
}
if (grepl("^[0-9]+$", suffix)) {
return(paste0("\\", gl, "_{", suffix, "}"))
}
}
}
## Recognized suffix patterns: x_hat -> \hat{x}, x_bar -> \bar{x}, x_tilde -> \tilde{x}
hat_match <- regmatches(nm, regexec("^(.)_hat$", nm))[[1L]]
if (length(hat_match) == 2L) {
return(paste0("\\hat{", hat_match[2L], "}"))
}
bar_match <- regmatches(nm, regexec("^(.)_bar$", nm))[[1L]]
if (length(bar_match) == 2L) {
return(paste0("\\bar{", bar_match[2L], "}"))
}
tilde_match <- regmatches(nm, regexec("^(.)_tilde$", nm))[[1L]]
if (length(tilde_match) == 2L) {
return(paste0("\\tilde{", tilde_match[2L], "}"))
}
## Multi-character: wrap in \mathit
paste0("\\mathit{", nm, "}")
}
# ==========================================================================
# Problem-level name resolution (collision-safe)
# ==========================================================================
#' Build collision-safe name table for a Problem
#' @param problem A Problem object
#' @returns An environment mapping id (as character) -> LaTeX name
#' @noRd
.build_latex_names <- function(problem) {
vars <- variables(problem)
params <- parameters(problem)
all_leaves <- c(vars, params)
if (length(all_leaves) == 0L) {
return(new.env(hash = TRUE, parent = emptyenv()))
}
## Map: id -> latex_name
names_map <- new.env(hash = TRUE, parent = emptyenv())
## Pass 1: assign base names
for (leaf in all_leaves) {
id_key <- as.character(leaf@id)
if (nzchar(leaf@.latex_name)) {
## User-provided .latex_name takes absolute priority
assign(id_key, leaf@.latex_name, envir = names_map)
} else {
nm <- expr_name(leaf)
assign(id_key, .name_to_latex(nm), envir = names_map)
}
}
## Pass 2: detect collisions, disambiguate with subscript ID
latex_vals <- as.list(names_map)
seen <- new.env(hash = TRUE, parent = emptyenv())
colliders <- character(0)
for (id_key in names(latex_vals)) {
lname <- latex_vals[[id_key]]
if (exists(lname, envir = seen, inherits = FALSE)) {
colliders <- c(colliders, lname)
}
assign(lname, TRUE, envir = seen)
}
if (length(colliders) > 0L) {
colliders <- unique(colliders)
for (id_key in names(latex_vals)) {
if (latex_vals[[id_key]] %in% colliders) {
old <- latex_vals[[id_key]]
latex_vals[[id_key]] <- paste0(old, "_{", id_key, "}")
cli_warn("Multiple variables/parameters map to LaTeX name {.val {old}}; disambiguating with ID subscripts.")
}
}
## Rebuild env
names_map <- list2env(latex_vals, hash = TRUE, parent = emptyenv())
}
names_map
}
# ==========================================================================
# Shape / zero-detection helpers
# ==========================================================================
## Peel through Promote wrappers to get the "real" expression
.peel_promote <- function(expr) {
while (S7_inherits(expr, Promote)) {
expr <- expr@args[[1L]]
}
expr
}
## Is the expression effectively scalar (possibly wrapped in Promote)?
.is_effectively_scalar <- function(expr) {
prod(.peel_promote(expr)@shape) == 1L
}
## Is the expression a zero constant (possibly promoted)?
.is_zero_constant <- function(expr) {
inner <- .peel_promote(expr)
if (!S7_inherits(inner, Constant)) return(FALSE)
v <- inner@.value
isTRUE(all(as.numeric(v) == 0))
}
# ==========================================================================
# Parenthesization helper
# ==========================================================================
#' Wrap LaTeX in parens if inner precedence is lower than outer
#' @noRd
.paren_if <- function(inner_latex, inner_prec, outer_prec) {
if (inner_prec < outer_prec) {
paste0("\\left(", inner_latex, "\\right)")
} else {
inner_latex
}
}
# ==========================================================================
# Internal workhorse: .to_latex_prec()
# ==========================================================================
## Returns list(latex = character(1), prec = integer(1))
## names_map: environment (id -> LaTeX name) or NULL for standalone
.to_latex_prec <- new_generic(".to_latex_prec", "x",
function(x, names_map = NULL, ...) S7_dispatch()
)
# ==========================================================================
# Numeric formatting
# ==========================================================================
.format_numeric <- function(val, digits = 4L) {
if (length(val) == 1L) {
if (is.na(val)) return("\\mathrm{NA}")
if (is.infinite(val)) {
return(if (val > 0) "\\infty" else "-\\infty")
}
## Integer-valued floats: drop decimals
if (is.numeric(val) && val == round(val) && abs(val) < 1e12) {
return(as.character(as.integer(val)))
}
return(format(val, digits = digits))
}
## Matrix/vector: shouldn't reach here normally
as.character(val)
}
# ==========================================================================
# Leaf methods
# ==========================================================================
## -- Variable --
method(.to_latex_prec, Variable) <- function(x, names_map = NULL, ...) {
if (!is.null(names_map)) {
id_key <- as.character(x@id)
if (exists(id_key, envir = names_map, inherits = FALSE)) {
return(list(latex = get(id_key, envir = names_map), prec = .LATEX_PREC$ATOM))
}
}
## Standalone: use .latex_name if set, else .name_to_latex(expr_name)
if (nzchar(x@.latex_name)) {
return(list(latex = x@.latex_name, prec = .LATEX_PREC$ATOM))
}
list(latex = .name_to_latex(expr_name(x)), prec = .LATEX_PREC$ATOM)
}
## -- Parameter --
method(.to_latex_prec, Parameter) <- function(x, names_map = NULL, ...) {
if (!is.null(names_map)) {
id_key <- as.character(x@id)
if (exists(id_key, envir = names_map, inherits = FALSE)) {
return(list(latex = get(id_key, envir = names_map), prec = .LATEX_PREC$ATOM))
}
}
if (nzchar(x@.latex_name)) {
return(list(latex = x@.latex_name, prec = .LATEX_PREC$ATOM))
}
list(latex = .name_to_latex(expr_name(x)), prec = .LATEX_PREC$ATOM)
}
## -- Constant --
method(.to_latex_prec, Constant) <- function(x, names_map = NULL, ...) {
v <- x@.value
shape <- x@shape
if (prod(shape) == 1L) {
## Scalar constant
val <- as.numeric(v[1L])
ltx <- .format_numeric(val)
prec <- if (val < 0) .LATEX_PREC$ADD else .LATEX_PREC$ATOM
return(list(latex = ltx, prec = prec))
}
## Named constant
if (nchar(x@.name) > 0L) {
return(list(latex = .name_to_latex(x@.name), prec = .LATEX_PREC$ATOM))
}
## Matrix/vector: abbreviated
list(
latex = sprintf("\\mathit{[%dx%d]}", shape[1L], shape[2L]),
prec = .LATEX_PREC$ATOM
)
}
# ==========================================================================
# Atom fallback (default for any atom without a specialized method)
# ==========================================================================
method(.to_latex_prec, Atom) <- function(x, names_map = NULL, ...) {
cls <- sub("^CVXR::", "", class(x)[[1L]])
args_latex <- vapply(x@args, function(a) {
.to_latex_prec(a, names_map)$latex
}, character(1))
list(
latex = sprintf("\\operatorname{%s}\\left(%s\\right)", cls,
paste(args_latex, collapse = ", ")),
prec = .LATEX_PREC$FUNC
)
}
# ==========================================================================
# Affine operators
# ==========================================================================
## -- AddExpression --
method(.to_latex_prec, AddExpression) <- function(x, names_map = NULL, ...) {
n <- length(x@args)
if (n == 0L) return(list(latex = "0", prec = .LATEX_PREC$ATOM))
parts <- character(n)
for (i in seq_len(n)) {
res <- .to_latex_prec(x@args[[i]], names_map)
if (i == 1L) {
parts[i] <- res$latex
} else {
## Check if this arg is a negation: render as " - ..." instead of " + -..."
## After broadcast_args, NegExpression may be wrapped in Promote
ai <- x@args[[i]]
neg_inner <- if (S7_inherits(ai, NegExpression)) ai@args[[1L]]
else if (S7_inherits(ai, Promote) && S7_inherits(ai@args[[1L]], NegExpression)) ai@args[[1L]]@args[[1L]]
else NULL
if (!is.null(neg_inner)) {
inner <- .to_latex_prec(neg_inner, names_map)
parts[i] <- paste0(" - ", .paren_if(inner$latex, inner$prec, .LATEX_PREC$ADD))
} else if (startsWith(res$latex, "-")) {
parts[i] <- paste0(" - ", substring(res$latex, 2L))
} else {
parts[i] <- paste0(" + ", res$latex)
}
}
}
list(latex = paste0(parts, collapse = ""), prec = .LATEX_PREC$ADD)
}
## -- NegExpression --
method(.to_latex_prec, NegExpression) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("-", .paren_if(inner$latex, inner$prec, .LATEX_PREC$UNARY)),
prec = .LATEX_PREC$UNARY
)
}
## -- MulExpression (matrix multiply %*%) --
method(.to_latex_prec, MulExpression) <- function(x, names_map = NULL, ...) {
lhs <- .to_latex_prec(x@args[[1L]], names_map)
rhs <- .to_latex_prec(x@args[[2L]], names_map)
## For matrix multiply, juxtaposition (no operator) is standard;
## use explicit space for readability
l <- .paren_if(lhs$latex, lhs$prec, .LATEX_PREC$MUL)
r <- .paren_if(rhs$latex, rhs$prec, .LATEX_PREC$MUL)
list(latex = paste0(l, " ", r), prec = .LATEX_PREC$MUL)
}
## -- Multiply (elementwise *) --
method(.to_latex_prec, Multiply) <- function(x, names_map = NULL, ...) {
lhs <- .to_latex_prec(x@args[[1L]], names_map)
rhs <- .to_latex_prec(x@args[[2L]], names_map)
l <- .paren_if(lhs$latex, lhs$prec, .LATEX_PREC$MUL)
r <- .paren_if(rhs$latex, rhs$prec, .LATEX_PREC$MUL)
## Use \circ for Hadamard product only when BOTH sides are genuinely
## non-scalar. Promote wraps a scalar to match the other side's shape,
## so peel through it before checking.
if (!.is_effectively_scalar(x@args[[1L]]) && !.is_effectively_scalar(x@args[[2L]])) {
list(latex = paste0(l, " \\circ ", r), prec = .LATEX_PREC$MUL)
} else {
list(latex = paste0(l, " ", r), prec = .LATEX_PREC$MUL)
}
}
## -- DivExpression --
method(.to_latex_prec, DivExpression) <- function(x, names_map = NULL, ...) {
num <- .to_latex_prec(x@args[[1L]], names_map)
den <- .to_latex_prec(x@args[[2L]], names_map)
list(
latex = paste0("\\frac{", num$latex, "}{", den$latex, "}"),
prec = .LATEX_PREC$MUL
)
}
## -- Transpose --
method(.to_latex_prec, Transpose) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
## Use braces to scope the ^T
ltx <- if (inner$prec >= .LATEX_PREC$ATOM) {
paste0(inner$latex, "\\T")
} else {
paste0("\\left(", inner$latex, "\\right)\\T")
}
list(latex = ltx, prec = .LATEX_PREC$ATOM)
}
## -- Index --
method(.to_latex_prec, Index) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
orig_shape <- x@args[[1L]]@shape
## Guard against double subscripts and ambiguous subscript scope:
## If the inner expression is compound (not ATOM-level), wrapping _{...}
## would only subscript the last token. Wrap in parens first.
base <- if (inner$prec < .LATEX_PREC$ATOM) {
paste0("\\left(", inner$latex, "\\right)")
} else {
inner$latex
}
## Format row index
row_idx <- x@key[[1L]]
col_idx <- x@key[[2L]]
row_all <- identical(row_idx, seq_len(orig_shape[1L]))
col_all <- identical(col_idx, seq_len(orig_shape[2L]))
.fmt_idx <- function(idx, dim_len) {
if (identical(idx, seq_len(dim_len))) return(":")
if (length(idx) == 1L) return(as.character(idx))
## Contiguous range
if (length(idx) > 1L && all(diff(idx) == 1L)) {
return(paste0(idx[1L], ":", idx[length(idx)]))
}
paste(idx, collapse = ",")
}
row_str <- .fmt_idx(row_idx, orig_shape[1L])
col_str <- .fmt_idx(col_idx, orig_shape[2L])
## Single element: x_{ij}
if (length(row_idx) == 1L && length(col_idx) == 1L) {
sub <- paste0(row_str, col_str)
if (nchar(sub) <= 2L) {
ltx <- paste0(base, "_{", sub, "}")
} else {
ltx <- paste0(base, "_{", row_str, ",", col_str, "}")
}
return(list(latex = ltx, prec = .LATEX_PREC$ATOM))
}
## Column vector: x_{i:j} or x_{:}
if (orig_shape[2L] == 1L) {
ltx <- paste0(base, "_{", row_str, "}")
return(list(latex = ltx, prec = .LATEX_PREC$ATOM))
}
## General slice
ltx <- paste0(base, "_{", row_str, ",", col_str, "}")
list(latex = ltx, prec = .LATEX_PREC$ATOM)
}
## -- SumEntries --
method(.to_latex_prec, SumEntries) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
axis <- x@axis
if (is.null(axis)) {
## Sum of all entries — adapt to argument shape
arg_shape <- x@args[[1L]]@shape
i <- .paren_if(inner$latex, inner$prec, .LATEX_PREC$MUL)
if (arg_shape[2L] == 1L) {
## Column vector: 1^T x suffices
ltx <- paste0("\\ones\\T ", i)
} else if (arg_shape[1L] == 1L) {
## Row vector: x 1 suffices
ltx <- paste0(i, " \\ones")
} else {
## General matrix: 1^T X 1
ltx <- paste0("\\ones\\T ", i, " \\ones")
}
list(latex = ltx, prec = .LATEX_PREC$MUL)
} else {
## Axis-wise sum: use \sum notation
list(
latex = paste0("\\operatorname{sum}\\left(", inner$latex,
", \\text{axis}=", axis, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
}
## -- Trace --
method(.to_latex_prec, Trace) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\tr\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Kron (Kronecker product) --
method(.to_latex_prec, Kron) <- function(x, names_map = NULL, ...) {
lhs <- .to_latex_prec(x@args[[1L]], names_map)
rhs <- .to_latex_prec(x@args[[2L]], names_map)
list(
latex = paste0(lhs$latex, " \\otimes ", rhs$latex),
prec = .LATEX_PREC$MUL
)
}
## -- Reshape --
method(.to_latex_prec, Reshape) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\operatorname{reshape}\\left(", inner$latex,
", ", x@shape[1L], ", ", x@shape[2L], "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Promote (transparent: just render the inner expression) --
method(.to_latex_prec, Promote) <- function(x, names_map = NULL, ...) {
.to_latex_prec(x@args[[1L]], names_map)
}
## -- Conj_ (complex conjugate) --
method(.to_latex_prec, Conj_) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\overline{", inner$latex, "}"),
prec = .LATEX_PREC$ATOM
)
}
## -- Real_ --
method(.to_latex_prec, Real_) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\operatorname{Re}\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Imag_ --
method(.to_latex_prec, Imag_) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\operatorname{Im}\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- HStack --
method(.to_latex_prec, HStack) <- function(x, names_map = NULL, ...) {
parts <- vapply(x@args, function(a) {
.to_latex_prec(a, names_map)$latex
}, character(1))
list(
latex = paste0("\\begin{bmatrix} ", paste(parts, collapse = " & "), " \\end{bmatrix}"),
prec = .LATEX_PREC$ATOM
)
}
## -- VStack --
method(.to_latex_prec, VStack) <- function(x, names_map = NULL, ...) {
parts <- vapply(x@args, function(a) {
.to_latex_prec(a, names_map)$latex
}, character(1))
list(
latex = paste0("\\begin{bmatrix} ", paste(parts, collapse = " \\\\ "), " \\end{bmatrix}"),
prec = .LATEX_PREC$ATOM
)
}
## -- DiagVec --
method(.to_latex_prec, DiagVec) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\diag\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- DiagMat --
method(.to_latex_prec, DiagMat) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\diag\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- UpperTri --
method(.to_latex_prec, UpperTri) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\operatorname{upper\\_tri}\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Convolve --
method(.to_latex_prec, Convolve) <- function(x, names_map = NULL, ...) {
lhs <- .to_latex_prec(x@args[[1L]], names_map)
rhs <- .to_latex_prec(x@args[[2L]], names_map)
list(
latex = paste0(lhs$latex, " * ", rhs$latex),
prec = .LATEX_PREC$MUL
)
}
## -- Cumsum --
method(.to_latex_prec, Cumsum) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\cumsum\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Wrap (transparent) --
method(.to_latex_prec, Wrap) <- function(x, names_map = NULL, ...) {
.to_latex_prec(x@args[[1L]], names_map)
}
# ==========================================================================
# Elementwise atoms
# ==========================================================================
## -- Abs --
method(.to_latex_prec, Abs) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\cvxabs{", inner$latex, "}"),
prec = .LATEX_PREC$ATOM
)
}
## -- Power --
method(.to_latex_prec, Power) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
p <- x@p_used
## Special cases
if (!is.null(p)) {
if (p == 0.5) {
return(list(
latex = paste0("\\sqrt{", inner$latex, "}"),
prec = .LATEX_PREC$ATOM
))
}
if (p == -1) {
return(list(
latex = paste0("\\frac{1}{", inner$latex, "}"),
prec = .LATEX_PREC$MUL
))
}
## General power
base <- if (inner$prec < .LATEX_PREC$POW) {
paste0("\\left(", inner$latex, "\\right)")
} else {
inner$latex
}
p_str <- .format_numeric(p)
return(list(
latex = paste0("{", base, "}^{", p_str, "}"),
prec = .LATEX_PREC$POW
))
}
## p is not constant: use general notation
p_latex <- .to_latex_prec(x@p, names_map)$latex
base <- if (inner$prec < .LATEX_PREC$POW) {
paste0("\\left(", inner$latex, "\\right)")
} else {
inner$latex
}
list(
latex = paste0("{", base, "}^{", p_latex, "}"),
prec = .LATEX_PREC$POW
)
}
## -- Exp --
method(.to_latex_prec, Exp) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("e^{", inner$latex, "}"),
prec = .LATEX_PREC$ATOM
)
}
## -- Log --
method(.to_latex_prec, Log) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\log\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Log1p --
method(.to_latex_prec, Log1p) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\log\\left(1 + ", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Ceil --
method(.to_latex_prec, Ceil) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\cvxceil{", inner$latex, "}"),
prec = .LATEX_PREC$ATOM
)
}
## -- Floor --
method(.to_latex_prec, Floor) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\cvxfloor{", inner$latex, "}"),
prec = .LATEX_PREC$ATOM
)
}
## -- Maximum (elementwise max of 2+ args) --
method(.to_latex_prec, Maximum) <- function(x, names_map = NULL, ...) {
args_latex <- vapply(x@args, function(a) {
.to_latex_prec(a, names_map)$latex
}, character(1))
list(
latex = paste0("\\max\\left(", paste(args_latex, collapse = ", "), "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Minimum (elementwise min of 2+ args) --
method(.to_latex_prec, Minimum) <- function(x, names_map = NULL, ...) {
args_latex <- vapply(x@args, function(a) {
.to_latex_prec(a, names_map)$latex
}, character(1))
list(
latex = paste0("\\min\\left(", paste(args_latex, collapse = ", "), "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Entr (entropy: -x log x) --
method(.to_latex_prec, Entr) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
i <- .paren_if(inner$latex, inner$prec, .LATEX_PREC$MUL)
list(
latex = paste0("-", i, " \\log\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$ADD
)
}
## -- RelEntr (relative entropy: x log(x/y)) --
method(.to_latex_prec, RelEntr) <- function(x, names_map = NULL, ...) {
x_ltx <- .to_latex_prec(x@args[[1L]], names_map)
y_ltx <- .to_latex_prec(x@args[[2L]], names_map)
x_s <- .paren_if(x_ltx$latex, x_ltx$prec, .LATEX_PREC$MUL)
list(
latex = paste0(x_s, " \\log\\left(\\frac{", x_ltx$latex, "}{",
y_ltx$latex, "}\\right)"),
prec = .LATEX_PREC$MUL
)
}
## -- KlDiv (KL divergence: x log(x/y) - x + y) --
method(.to_latex_prec, KlDiv) <- function(x, names_map = NULL, ...) {
x_ltx <- .to_latex_prec(x@args[[1L]], names_map)
y_ltx <- .to_latex_prec(x@args[[2L]], names_map)
x_s <- .paren_if(x_ltx$latex, x_ltx$prec, .LATEX_PREC$MUL)
list(
latex = paste0(x_s, " \\log\\left(\\frac{", x_ltx$latex, "}{",
y_ltx$latex, "}\\right) - ", x_ltx$latex, " + ", y_ltx$latex),
prec = .LATEX_PREC$ADD
)
}
## -- Logistic --
method(.to_latex_prec, Logistic) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\log\\left(1 + e^{", inner$latex, "}\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Huber --
method(.to_latex_prec, Huber) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
M_val <- if (S7_inherits(x@M, Expression)) {
.to_latex_prec(x@M, names_map)$latex
} else {
.format_numeric(as.numeric(x@M))
}
list(
latex = paste0("\\huber_{", M_val, "}\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Xexp (x * exp(x)) --
method(.to_latex_prec, Xexp) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
i <- .paren_if(inner$latex, inner$prec, .LATEX_PREC$MUL)
list(
latex = paste0(i, " e^{", inner$latex, "}"),
prec = .LATEX_PREC$MUL
)
}
# ==========================================================================
# Logic atoms
# ==========================================================================
method(.to_latex_prec, Not) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\neg ", .paren_if(inner$latex, inner$prec, .LATEX_PREC$UNARY)),
prec = .LATEX_PREC$UNARY
)
}
method(.to_latex_prec, And) <- function(x, names_map = NULL, ...) {
parts <- vapply(x@args, function(a) {
r <- .to_latex_prec(a, names_map)
.paren_if(r$latex, r$prec, .LATEX_PREC$MUL)
}, character(1))
list(latex = paste(parts, collapse = " \\land "), prec = .LATEX_PREC$MUL)
}
method(.to_latex_prec, Or) <- function(x, names_map = NULL, ...) {
parts <- vapply(x@args, function(a) {
r <- .to_latex_prec(a, names_map)
.paren_if(r$latex, r$prec, .LATEX_PREC$ADD)
}, character(1))
list(latex = paste(parts, collapse = " \\lor "), prec = .LATEX_PREC$ADD)
}
method(.to_latex_prec, Xor) <- function(x, names_map = NULL, ...) {
parts <- vapply(x@args, function(a) {
r <- .to_latex_prec(a, names_map)
.paren_if(r$latex, r$prec, .LATEX_PREC$ADD)
}, character(1))
list(latex = paste(parts, collapse = " \\oplus "), prec = .LATEX_PREC$ADD)
}
# ==========================================================================
# Norm atoms
# ==========================================================================
## -- Pnorm (general p-norm) --
method(.to_latex_prec, Pnorm) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
p <- x@p # numeric (class_numeric)
p_str <- if (is.infinite(p) && p > 0) {
"\\infty"
} else if (is.infinite(p) && p < 0) {
"-\\infty"
} else if (p == 2) {
"2"
} else {
.format_numeric(p)
}
list(
latex = paste0("\\cvxnorm{", inner$latex, "}_{", p_str, "}"),
prec = .LATEX_PREC$ATOM
)
}
## -- Norm1 --
method(.to_latex_prec, Norm1) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\cvxnorm{", inner$latex, "}_1"),
prec = .LATEX_PREC$ATOM
)
}
## -- NormInf --
method(.to_latex_prec, NormInf) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\cvxnorm{", inner$latex, "}_\\infty"),
prec = .LATEX_PREC$ATOM
)
}
## -- NormNuc (nuclear norm) --
method(.to_latex_prec, NormNuc) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\cvxnorm{", inner$latex, "}_*"),
prec = .LATEX_PREC$ATOM
)
}
## -- SigmaMax (spectral norm / max singular value) --
method(.to_latex_prec, SigmaMax) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\sigmamax\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
# ==========================================================================
# Reduction atoms (MaxEntries, MinEntries, Prod, SumLargest, etc.)
# ==========================================================================
## -- MaxEntries --
method(.to_latex_prec, MaxEntries) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\max\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- MinEntries --
method(.to_latex_prec, MinEntries) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\min\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Prod --
method(.to_latex_prec, Prod) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\prod\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- SumLargest --
method(.to_latex_prec, SumLargest) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
k <- x@k
list(
latex = paste0("\\operatorname{sum\\_largest}\\left(", inner$latex, ", ", k, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- LogSumExp --
method(.to_latex_prec, LogSumExp) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\logsumexp\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- GeoMean --
method(.to_latex_prec, GeoMean) <- function(x, names_map = NULL, ...) {
args_latex <- vapply(x@args, function(a) {
.to_latex_prec(a, names_map)$latex
}, character(1))
list(
latex = paste0("\\operatorname{geo\\_mean}\\left(", paste(args_latex, collapse = ", "), "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Cummax --
method(.to_latex_prec, Cummax) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\operatorname{cummax}\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Cumprod --
method(.to_latex_prec, Cumprod) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\operatorname{cumprod}\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
# ==========================================================================
# Matrix / spectral atoms
# ==========================================================================
## -- QuadForm --
method(.to_latex_prec, QuadForm) <- function(x, names_map = NULL, ...) {
x_ltx <- .to_latex_prec(x@args[[1L]], names_map)
P_ltx <- .to_latex_prec(x@args[[2L]], names_map)
list(
latex = paste0(x_ltx$latex, "\\T ", P_ltx$latex, " ", x_ltx$latex),
prec = .LATEX_PREC$MUL
)
}
## -- QuadOverLin --
method(.to_latex_prec, QuadOverLin) <- function(x, names_map = NULL, ...) {
x_ltx <- .to_latex_prec(x@args[[1L]], names_map)
y_ltx <- .to_latex_prec(x@args[[2L]], names_map)
list(
latex = paste0("\\frac{\\cvxnorm{", x_ltx$latex, "}_2^2}{", y_ltx$latex, "}"),
prec = .LATEX_PREC$MUL
)
}
## -- LogDet --
method(.to_latex_prec, LogDet) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\logdet\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- LambdaMax --
method(.to_latex_prec, LambdaMax) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\lambdamax\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- LambdaSumLargest --
method(.to_latex_prec, LambdaSumLargest) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
k <- x@k
list(
latex = paste0("\\sum_{i=1}^{", k, "} \\lambda_i\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- MatrixFrac --
method(.to_latex_prec, MatrixFrac) <- function(x, names_map = NULL, ...) {
x_ltx <- .to_latex_prec(x@args[[1L]], names_map)
P_ltx <- .to_latex_prec(x@args[[2L]], names_map)
list(
latex = paste0(x_ltx$latex, "\\T ", P_ltx$latex, "^{-1} ", x_ltx$latex),
prec = .LATEX_PREC$MUL
)
}
## -- TrInv --
method(.to_latex_prec, TrInv) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\tr\\left(", inner$latex, "^{-1}\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- EyeMinusInv --
method(.to_latex_prec, EyeMinusInv) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\tr\\left(\\left(I - ", inner$latex, "\\right)^{-1}\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- ConditionNumber --
method(.to_latex_prec, ConditionNumber) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0("\\condnum\\left(", inner$latex, "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- Perspective --
method(.to_latex_prec, Perspective) <- function(x, names_map = NULL, ...) {
f_ltx <- .to_latex_prec(x@args[[1L]], names_map)
s_ltx <- .to_latex_prec(x@args[[2L]], names_map)
list(
latex = paste0(s_ltx$latex, " \\, \\operatorname{f}\\left(\\frac{",
f_ltx$latex, "}{", s_ltx$latex, "}\\right)"),
prec = .LATEX_PREC$MUL
)
}
## -- Dotsort --
method(.to_latex_prec, Dotsort) <- function(x, names_map = NULL, ...) {
args_latex <- vapply(x@args, function(a) {
.to_latex_prec(a, names_map)$latex
}, character(1))
list(
latex = paste0("\\operatorname{dotsort}\\left(", paste(args_latex, collapse = ", "), "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- SymbolicQuadForm --
method(.to_latex_prec, SymbolicQuadForm) <- function(x, names_map = NULL, ...) {
x_ltx <- .to_latex_prec(x@args[[1L]], names_map)
P_ltx <- .to_latex_prec(x@args[[2L]], names_map)
list(
latex = paste0(x_ltx$latex, "\\T ", P_ltx$latex, " ", x_ltx$latex),
prec = .LATEX_PREC$MUL
)
}
# ==========================================================================
# DQCP atoms
# ==========================================================================
## -- GenLambdaMax --
method(.to_latex_prec, GenLambdaMax) <- function(x, names_map = NULL, ...) {
args_latex <- vapply(x@args, function(a) {
.to_latex_prec(a, names_map)$latex
}, character(1))
list(
latex = paste0("\\lambda_{\\max}\\left(", paste(args_latex, collapse = ", "), "\\right)"),
prec = .LATEX_PREC$FUNC
)
}
## -- DistRatio --
method(.to_latex_prec, DistRatio) <- function(x, names_map = NULL, ...) {
args_latex <- vapply(x@args, function(a) {
.to_latex_prec(a, names_map)$latex
}, character(1))
list(
latex = paste0("\\frac{\\cvxnorm{", args_latex[1L], " - ", args_latex[2L], "}_2}{",
"\\cvxnorm{", args_latex[1L], " - ", args_latex[3L], "}_2}"),
prec = .LATEX_PREC$MUL
)
}
# ==========================================================================
# Constraint methods
# ==========================================================================
## -- Zero (x == 0) --
method(.to_latex_prec, Zero) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0(inner$latex, " = 0"),
prec = .LATEX_PREC$COMPARE
)
}
## -- Equality (lhs == rhs) --
method(.to_latex_prec, Equality) <- function(x, names_map = NULL, ...) {
lhs <- .to_latex_prec(x@args[[1L]], names_map)
rhs <- .to_latex_prec(x@args[[2L]], names_map)
list(
latex = paste0(lhs$latex, " = ", rhs$latex),
prec = .LATEX_PREC$COMPARE
)
}
## -- NonPos (x <= 0) --
method(.to_latex_prec, NonPos) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0(inner$latex, " \\leq 0"),
prec = .LATEX_PREC$COMPARE
)
}
## -- NonNeg (x >= 0) --
method(.to_latex_prec, NonNeg) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(
latex = paste0(inner$latex, " \\geq 0"),
prec = .LATEX_PREC$COMPARE
)
}
## -- Inequality (lhs <= rhs) --
method(.to_latex_prec, Inequality) <- function(x, names_map = NULL, ...) {
## When LHS is a zero constant (e.g. from x >= 0 which stores
## Inequality(0, x)), flip to "RHS >= 0" for readability
if (.is_zero_constant(x@args[[1L]])) {
rhs <- .to_latex_prec(x@args[[2L]], names_map)
return(list(
latex = paste0(rhs$latex, " \\geq 0"),
prec = .LATEX_PREC$COMPARE
))
}
## When RHS is a zero constant, render as "LHS <= 0"
if (.is_zero_constant(x@args[[2L]])) {
lhs <- .to_latex_prec(x@args[[1L]], names_map)
return(list(
latex = paste0(lhs$latex, " \\leq 0"),
prec = .LATEX_PREC$COMPARE
))
}
lhs <- .to_latex_prec(x@args[[1L]], names_map)
rhs <- .to_latex_prec(x@args[[2L]], names_map)
list(
latex = paste0(lhs$latex, " \\leq ", rhs$latex),
prec = .LATEX_PREC$COMPARE
)
}
## -- PSD (X >> 0) --
## PSD stores args = list(expr) where expr = LHS - RHS (from %>>%).
## Try to decompose the subtraction for cleaner rendering.
method(.to_latex_prec, PSD) <- function(x, names_map = NULL, ...) {
arg <- x@args[[1L]]
## Detect A + (-B) pattern from %>>%: PSD(A - B)
## After broadcast_args, the NegExpression may be wrapped in Promote
.unwrap_neg <- function(e) {
if (S7_inherits(e, NegExpression)) return(e)
if (S7_inherits(e, Promote) && S7_inherits(e@args[[1L]], NegExpression)) return(e@args[[1L]])
NULL
}
neg_node <- if (S7_inherits(arg, AddExpression) && length(arg@args) == 2L)
.unwrap_neg(arg@args[[2L]]) else NULL
if (!is.null(neg_node)) {
lhs_expr <- arg@args[[1L]]
## Unwrap Promote on LHS too (scalar LHS promoted by broadcast_args)
if (S7_inherits(lhs_expr, Promote)) lhs_expr <- lhs_expr@args[[1L]]
rhs_expr <- neg_node@args[[1L]] # unwrap NegExpression
lhs <- .to_latex_prec(lhs_expr, names_map)
if (.is_zero_constant(rhs_expr)) {
## S %>>% 0 -> S \psd 0
return(list(latex = paste0(lhs$latex, " \\psd 0"), prec = .LATEX_PREC$COMPARE))
}
## S %>>% T -> S \psd T
rhs <- .to_latex_prec(rhs_expr, names_map)
return(list(latex = paste0(lhs$latex, " \\psd ", rhs$latex), prec = .LATEX_PREC$COMPARE))
}
## Fallback: just expr \psd 0
inner <- .to_latex_prec(arg, names_map)
list(
latex = paste0(inner$latex, " \\psd 0"),
prec = .LATEX_PREC$COMPARE
)
}
## -- SOC (||x||_2 <= t) --
method(.to_latex_prec, SOC) <- function(x, names_map = NULL, ...) {
## args: [X, t]
X_ltx <- .to_latex_prec(x@args[[1L]], names_map)
t_ltx <- .to_latex_prec(x@args[[2L]], names_map)
list(
latex = paste0("\\cvxnorm{", X_ltx$latex, "}_2 \\leq ", t_ltx$latex),
prec = .LATEX_PREC$COMPARE
)
}
## -- ExpCone --
method(.to_latex_prec, ExpCone) <- function(x, names_map = NULL, ...) {
x_ltx <- .to_latex_prec(x@args[[1L]], names_map)
y_ltx <- .to_latex_prec(x@args[[2L]], names_map)
z_ltx <- .to_latex_prec(x@args[[3L]], names_map)
list(
latex = paste0("\\left(", x_ltx$latex, ", ", y_ltx$latex, ", ",
z_ltx$latex, "\\right) \\in \\Kexp"),
prec = .LATEX_PREC$COMPARE
)
}
## -- PowCone3D --
method(.to_latex_prec, PowCone3D) <- function(x, names_map = NULL, ...) {
x_ltx <- .to_latex_prec(x@args[[1L]], names_map)
y_ltx <- .to_latex_prec(x@args[[2L]], names_map)
z_ltx <- .to_latex_prec(x@args[[3L]], names_map)
alpha <- if (length(x@alpha) == 1L) .format_numeric(x@alpha) else "\\alpha"
list(
latex = paste0("\\left(", x_ltx$latex, ", ", y_ltx$latex, ", ",
z_ltx$latex, "\\right) \\in \\Kpow{", alpha, "}"),
prec = .LATEX_PREC$COMPARE
)
}
## -- FiniteSet --
method(.to_latex_prec, FiniteSet) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
## x@vec is a Constant (Reshape); extract its numeric value
vec_val <- value(x@vec)
vals_str <- paste(vapply(as.numeric(vec_val), .format_numeric, character(1)), collapse = ", ")
list(
latex = paste0(inner$latex, " \\in \\left\\{", vals_str, "\\right\\}"),
prec = .LATEX_PREC$COMPARE
)
}
# ==========================================================================
# Objective methods
# ==========================================================================
method(.to_latex_prec, Minimize) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(latex = inner$latex, prec = inner$prec)
}
method(.to_latex_prec, Maximize) <- function(x, names_map = NULL, ...) {
inner <- .to_latex_prec(x@args[[1L]], names_map)
list(latex = inner$latex, prec = inner$prec)
}
# ==========================================================================
# Top-level to_latex() methods (user-facing)
# ==========================================================================
## -- Expression (generic fallback) --
method(to_latex, Expression) <- function(x, ...) {
.to_latex_prec(x, names_map = NULL)$latex
}
## -- Constraint --
method(to_latex, Constraint) <- function(x, ...) {
.to_latex_prec(x, names_map = NULL)$latex
}
## -- Objective --
method(to_latex, Objective) <- function(x, ...) {
sense <- if (S7_inherits(x, Minimize)) "\\text{minimize}" else "\\text{maximize}"
inner <- .to_latex_prec(x@args[[1L]], names_map = NULL)$latex
paste0(sense, " \\quad ", inner)
}
## -- Problem (optidef environment) --
method(to_latex, Problem) <- function(x, ...) {
## Build collision-safe name table
names_map <- .build_latex_names(x)
## Objective
obj <- x@objective
sense <- if (S7_inherits(obj, Minimize)) "mini" else "maxi"
obj_latex <- .to_latex_prec(obj@args[[1L]], names_map)$latex
## Variables for underset
vars <- variables(x)
var_names <- vapply(vars, function(v) {
id_key <- as.character(v@id)
if (exists(id_key, envir = names_map, inherits = FALSE)) {
get(id_key, envir = names_map)
} else {
.name_to_latex(expr_name(v))
}
}, character(1))
var_str <- paste(var_names, collapse = ", ")
## Constraints
constrs <- x@constraints
if (length(constrs) == 0L) {
## Unconstrained
lines <- c(
sprintf("\\begin{%s*}{%s}{%s}{}{}", sense, var_str, obj_latex),
sprintf("\\end{%s*}", sense)
)
return(paste(lines, collapse = "\n"))
}
constr_lines <- vapply(constrs, function(c) {
.to_latex_prec(c, names_map)$latex
}, character(1))
## Build optidef environment
## optidef format: \addConstraint{LHS}{relation RHS}
## We split on the relation symbol
lines <- character(0)
lines <- c(lines, sprintf("\\begin{%s*}{%s}{%s}{}{}", sense, var_str, obj_latex))
for (cl in constr_lines) {
lines <- c(lines, sprintf(" \\addConstraint{%s}{}", cl))
}
lines <- c(lines, sprintf("\\end{%s*}", sense))
paste(lines, collapse = "\n")
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.