Nothing
#####
## DO NOT EDIT THIS FILE!! EDIT THE SOURCE INSTEAD: rsrc_tree/reductions/solvers/conic_solvers/scs_conif.R
#####
## CVXPY SOURCE: reductions/solvers/conic_solvers/scs_conif.py
## SCS solver interface
##
## SCS uses lower-triangular svec format for PSD constraints.
## Convention: A*x + s = b, s in K
# -- SCS status map ------------------------------------------------
## CVXPY SOURCE: scs_conif.py lines 127-135
SCS_STATUS_MAP <- list(
"1" = OPTIMAL,
"2" = OPTIMAL_INACCURATE,
"-1" = UNBOUNDED,
"-6" = UNBOUNDED_INACCURATE,
"-2" = INFEASIBLE,
"-7" = INFEASIBLE_INACCURATE,
"-4" = SOLVER_ERROR,
"-3" = SOLVER_ERROR,
"-5" = SOLVER_ERROR
)
# -- dims_to_solver_dict_scs --------------------------------------
## CVXPY SOURCE: scs_conif.py lines 36-42
## SCS 3.0+ uses 'z' instead of 'f' for zero cone.
##
## DELIBERATE DEVIATION (2026-08-13): CVXPY guards this with a runtime
## `Version(scs.__version__) >= Version('3.0.0')` probe (scs_conif.py:40). CVXR
## does not, because DESCRIPTION requires `scs (>= 3.2)` -- the pre-3.0 arm is
## unreachable, so the probe was dead code that read and parsed scs's
## DESCRIPTION off disk on every solve. (CVXPY's own pin is `scs >= 3.2.4.post1`
## in pyproject.toml:86, so its check is equally vestigial; behavior is
## identical, only the dead branch is gone.) If the DESCRIPTION floor is ever
## lowered below 3.0, restore the probe at both sites.
dims_to_solver_dict_scs <- function(cone_dims) {
cones <- dims_to_solver_dict(cone_dims)
## SCS 3.x renamed 'f' to 'z'
cones[["z"]] <- cones[["f"]]
cones[["f"]] <- NULL
cones
}
# -- SCS_Solver class ---------------------------------------------
## CVXPY SOURCE: scs_conif.py lines 116-155
SCS_Solver <- new_class("SCS_Solver", parent = ConicSolver, package = "CVXR",
constructor = function() {
if (FALSE) new_object(S7_object()) ## S7 static-check guard
.fast_new(SCS_Solver, S7_object(),
.cache = new.env(parent = emptyenv()),
MIP_CAPABLE = FALSE,
BOUNDED_VARIABLES = FALSE,
## CVXPY SOURCE: scs_conif.py lines 91-92
PSD_TRIANGLE_KIND = TriangleKind$LOWER,
PSD_SQRT2_SCALING = TRUE,
## CVXPY SOURCE: scs_conif.py:88-89
SUPPORTED_CONSTRAINTS = list(Zero, NonNeg, SOC, ExpCone,
SvecPSD, PowCone3D),
EXP_CONE_ORDER = c(0L, 1L, 2L),
REQUIRES_CONSTR = TRUE
)
}
)
method(solver_name, SCS_Solver) <- function(x) SCS_SOLVER
## CVXPY v1.8.2: SCS >= 3.0 supports quadratic objective
method(supports_quad_obj, SCS_Solver) <- function(x) TRUE
# -- SCS invert ----------------------------------------------------
## CVXPY SOURCE: scs_conif.py lines 235-278
## Parses SCS-specific result format.
method(reduction_invert, SCS_Solver) <- function(x, solution, inverse_data, ...) {
attr_list <- list()
## SCS result format: solution is the raw list from scs::scs()
## SCS 3.x: status_val in info, SCS 2.x: statusVal in info
info <- solution[["info"]]
status_val <- info[["status_val"]]
if (is.null(status_val)) status_val <- info[["statusVal"]]
status <- SCS_STATUS_MAP[[as.character(status_val)]]
if (is.null(status)) status <- SOLVER_ERROR
## Timing attributes
solve_time <- info[["solve_time"]]
if (is.null(solve_time)) solve_time <- info[["solveTime"]]
setup_time <- info[["setup_time"]]
if (is.null(setup_time)) setup_time <- info[["setupTime"]]
if (!is.null(solve_time)) attr_list[[RK_SOLVE_TIME]] <- solve_time / 1000
if (!is.null(setup_time)) attr_list[[RK_SETUP_TIME]] <- setup_time / 1000
attr_list[[RK_NUM_ITERS]] <- info[["iter"]]
if (status %in% SOLUTION_PRESENT) {
primal_val <- info[["pobj"]]
opt_val <- primal_val + inverse_data[[SD_OFFSET]]
primal_vars <- list()
primal_vars[[as.character(inverse_data[[SOLVER_VAR_ID]])]] <- solution[["x"]]
## Dual variables: split at zero cone boundary
y <- solution[["y"]]
zero_dim <- inverse_data[[SD_DIMS]]@zero
if (zero_dim > 0L) {
eq_dual <- get_dual_values(
y[seq_len(zero_dim)],
extract_dual_value,
inverse_data[[SOLVER_EQ_CONSTR]]
)
} else {
eq_dual <- list()
}
if (zero_dim < length(y)) {
ineq_dual <- get_dual_values(
y[(zero_dim + 1L):length(y)],
extract_dual_value,
inverse_data[[SOLVER_NEQ_CONSTR]]
)
} else {
ineq_dual <- list()
}
dual_vars <- c(eq_dual, ineq_dual)
Solution(status, opt_val, primal_vars, dual_vars, attr_list)
} else {
failure_solution(status, attr_list)
}
}
# -- SCS solve_via_data --------------------------------------------
## CVXPY SOURCE: scs_conif.py lines 304-354
method(solve_via_data, SCS_Solver) <- function(x, data, warm_start = FALSE, verbose = FALSE,
solver_opts = list(), ...) {
.require_solver_package(SCS_SOLVER)
dots <- list(...)
solver_cache <- dots[["solver_cache"]]
args <- list(A = data[[SD_A]], b = data[[SD_B]], c = data[[SD_C]])
cone <- dims_to_solver_dict_scs(data[[SD_DIMS]])
## Parse solver options. SCS 3.x tolerance names; the pre-3.0 `eps` arm is
## unreachable under DESCRIPTION's `scs (>= 3.2)` -- see the note on
## dims_to_solver_dict_scs above.
opts <- solver_opts
## CVXPY SOURCE: scs_conif.py:206-209 -- under SCS >= 3.0 the legacy `eps`
## keyword is replaced by eps_abs and eps_rel, both set to it (and `eps`
## overrides explicit eps_abs/eps_rel, exactly as upstream). Previously a
## user's eps= was silently ignored and the solve ran at the defaults.
if (!is.null(opts[["eps"]])) {
opts[["eps_abs"]] <- opts[["eps"]]
opts[["eps_rel"]] <- opts[["eps"]]
opts[["eps"]] <- NULL
}
if (is.null(opts[["eps_abs"]])) opts[["eps_abs"]] <- 1e-5
if (is.null(opts[["eps_rel"]])) opts[["eps_rel"]] <- 1e-5
## Anderson acceleration. CVXPY passes nothing for these (scs_conif.py:211-212
## sets only the two eps), so upstream gets SCS's own C defaults. The R `scs`
## package's scs_control() ships DIFFERENT defaults for both, which meant CVXR
## ran a materially different algorithm from CVXPY on every SCS solve:
##
## R scs 3.2.7 SCS C default (python scs 3.2.11)
## acceleration_lookback 0 10
## acceleration_interval 1 10
##
## (Every other setting agrees: alpha 1.5, scale 0.1, adaptive_scale, rho_x
## 1e-6, eps_infeas 1e-7, normalize, max_iters 100000.)
##
## Measured on the DQCP hypersonic subproblem, the SAME A/b/c handed to python
## scs 3.2.11 three ways:
## lookback 10 (C default) -> "infeasible", 2,500 iters
## lookback 0 (R default) -> "infeasible (inaccurate -
## reached max_iters)", 100,000 iters
## The second is exactly what R scs 3.2.7 returned on that data, so the R
## default alone accounts for it. End to end it cost accuracy: CVXR's SCS
## answer was 0.1345 against a true 0.14590 (7.8% off).
##
## These are DEFAULTS, applied like eps above -- an explicit user value still
## wins, and every SCS solve gets them (direct, DQCP bisection subproblems,
## warm-started), which is what makes CVXR's SCS behave as CVXPY's does.
if (is.null(opts[["acceleration_lookback"]]))
opts[["acceleration_lookback"]] <- 10L
if (is.null(opts[["acceleration_interval"]]))
opts[["acceleration_interval"]] <- 10L
## Pass P for QP path -- SCS expects symmetric sparse (dsCMatrix)
if (!is.null(data[[SD_P]])) {
args[["P"]] <- Matrix::forceSymmetric(Matrix::triu(data[[SD_P]]), uplo = "U")
}
## Warm-start: pass previous x/y/s as initial point
## CVXPY SOURCE: scs_conif.py lines 327-331
cache_key <- SCS_SOLVER
initial <- NULL
if (warm_start && !is.null(solver_cache) && exists(cache_key, envir = solver_cache)) {
cached <- get(cache_key, envir = solver_cache)
initial <- list(x = cached$x, y = cached$y, s = cached$s)
}
## Call SCS
result <- scs::scs(
A = args$A, b = args$b, obj = args$c, P = args[["P"]], cone = cone,
initial = initial,
control = c(list(verbose = verbose), opts)
)
## Cache result for future warm-starts (only on optimal)
## CVXPY SOURCE: scs_conif.py lines 352-353
if (!is.null(solver_cache)) {
status <- SCS_STATUS_MAP[[as.character(result$info$status_val)]]
if (!is.null(status) && status == OPTIMAL) {
assign(cache_key, result, envir = solver_cache)
}
}
result
}
method(print, SCS_Solver) <- function(x, ...) {
cat("SCS_Solver()\n")
invisible(x)
}
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.