This is the first CRAN release since 1.8.2 and is a large one: it tracks CVXPY 1.9.1 and folds in the changes from the internal 1.8.2-1 and 1.9.0 development cycles. The headline additions are a derivative API for differentiable convex programs, disciplined nonlinear programming (DNLP), and interval-bounds propagation with native solver-bound support.
psolve(prob, nlp = TRUE). The problem must satisfy the new
is_dnlp() grammar (disciplined nonlinear program); DCP
problems are a subset, so any DCP problem is also a valid DNLP.sin(),
cos(), tan(), sinh(),
tanh(), asinh(), atanh(), and
normcdf().prod() is now recognized as a smooth atom, so problems
involving products of entries (including
prod_entries(X, axis = ...)) are DNLP and solvable through
the NLP path.is_atom_smooth() matching CVXPY 1.9: affine atoms,
exp, log, entr,
logistic, kl_div, rel_entr,
xexp, power (with a constant exponent),
geo_mean, log_sum_exp, quad_form,
quad_over_lin, and prod. As a result smooth
convex/concave functions such as sum_squares() are
linearizable in both directions (is_smooth() is
TRUE), while piecewise atoms (abs,
min, max) remain convex/concave-only. New
predicate exports: is_smooth(),
is_atom_smooth(), is_linearizable_convex(),
is_linearizable_concave().abs,
max, max_elemwise, norm_inf,
sum_largest, and sum_smallest) now initialize
their epigraph variables during canonicalization, so the NLP backend has
a complete starting point. sum_largest initializes its
threshold to the (k+1)-th largest entry, matching CVXPY’s warm-start
fix.convolve() atom, CVXPY 1.9’s preferred
(numpy-style) name for the 1-D discrete-convolution conv()
atom. For numeric input it falls through to
stats::convolve().sparsediff package and can solve through the optional
ipopt and Uno R packages. NLP solver names:
"IPOPT", "UNO" (and the variants
"uno_ipm" / "uno_sqp"), "KNITRO",
and "COPT"; "KNITRO" and "COPT"
are registered but require solver bindings not yet available in R. When
installed, IPOPT is the first automatic NLP solver,
matching CVXPY’s preference order.quad_over_lin() and sum_squares() now
canonicalize axis-aware reductions (axis = 1 and
axis = 2) into batched second-order cone constraints,
closing the CVXPY parity gap for
sum_squares(..., axis = ...) in objectives and
constraints.UNO interface defaults to the
interior-point ipopt preset (plus MUMPS), which differs
from CVXPY’s filtersqp default. CVXPY’s
filtersqp uses the BQPD active-set solver for its quadratic
subproblems, which handles indefinite Hessians. The bundled Uno build is
HiGHS-only (BQPD is not bundled and is not CRAN-license compatible), and
HiGHS solves only convex quadratic subproblems, so
filtersqp fails on nonconvex DNLPs. The interior-point
ipopt preset factors the regularized KKT system with MUMPS
and is robust on both convex and nonconvex problems. Force the SQP path
with solver = "uno_sqp" (or
preset = "filtersqp"); it is reliable only for problems
whose subproblem Hessians stay positive semidefinite.psolve(prob, nlp = TRUE, best_of = n) solves from
n random initial points and keeps the best result. Random
initialization draws from a variable’s
sample_bounds(var) <- c(low, high) (when set) or its
finite variable bounds. The per-run objectives are available via
solver_stats(prob)@extra_stats$all_objs_from_best_of.dual_value() returns the constraint
duals recovered from the solver (a CVXR addition; the duals are not
exposed by CVXPY’s NLP interface).diffcp R package:
psolve(prob, requires_grad = TRUE) followed by
backward(prob) or derivative(prob);
gradient(x)<- and delta(x)<- set tangent
/ cotangent values on leaves. The chain rule is wired through
Dgp2Dcp (log/exp) and Complex2Real (real/imag
split).get_bounds() now works on any expression, not just
variables: it propagates interval bounds through affine, elementwise,
and piecewise-linear atoms (e.g. get_bounds(A %*% x + b),
get_bounds(abs(x)), get_bounds(sum(x))).
Bounds are returned shaped to the expression’s dimensions.Matrix objects or
symbolic bounds involving Parameters.
get_bounds() preserves sparse numeric bounds and skips
symbolic bounds, while solve-time attribute lowering enforces the
symbolic constraints. Parameter values containing matching
Inf entries now validate correctly.Parameter bounds updates native solver bounds on DPP
re-solves.Parameters before rebuilding their full
expressions.Parameters embedded in expression
bounds and include those bounds in DPP/DGP compliance checks.psolve(prob, solver = "HIGHS", warm_start = TRUE) reuses
the previous solution via the persistent-solver API
(hi_solver_set_solution) across LP, MILP, and QP paths.
This requires highs (>= 1.14), now available on CRAN; on
older highs the warm-start tests skip and solves fall back
to cold starts.validate_column_name() checks a name against the HiGHS
LP-file column-name rules, mirroring CVXPY’s
highs_conif.validate_column_name.highs package
exposes no column-name setter, so a written model would carry generic
names (c0, c1, …).gp = TRUE
(e.g. Variable(pos = TRUE, bounds = list(lb, ub)) with
lb/ub Parameters). The DGP
reduction log-transforms the bounds into the log domain and lowers them
to constraints; parametric bounds canonicalize through the DGP tree
without eagerly evaluating log(value(param)), so DPP
re-solves with changed bound parameters work.is_dpp() gains a context argument
("dcp" or "dgp"), matching CVXPY’s
is_dpp(context=...). In the "dgp" context a
variable’s symbolic bounds must be log-log-affine (e.g. a product of
positive Parameters is DGP-DPP though not DCP-DPP;
norm()/sum() bounds are rejected).partial_optimize() transform.Rcplex quadratic
constraints.sign() atom (DQCP, scalar input).psolve() gains a solver_path argument for
solver fallback chains.set_label() / label<- /
format_labeled() for attaching user-supplied labels to
expressions.power(), geo_mean(), and
p_norm() with approx = TRUE now warn when the
selected solver supports power cones (approx = FALSE uses
the exact PowCone3D / PowConeND form).Rmosek 11.1.1+).solver_stats() for MOSEK now populates
solve_time, num_iters, and
extra_stats.param_dict(), var_dict(), and
size_metrics() accessors on Problem.Variable(value = ...) accepts an initial value at
construction.CallbackParam class — a Parameter
subclass whose value is computed on every read by a user-supplied
callback.project() accepts index-based
boolean = c(i, j, ...) and
integer = c(i, j, ...) attributes (1-based).validate_arguments() now called on Cummax,
Cumprod, and MinEntries constructors..fast_new helper for S7 expression construction
(roughly 40% lower wall-clock on atom-dense and PSD-heavy problems), and
routing S7 class-membership checks on the hot canonicalization path
through a cached check on the object’s class vector instead of
S7::S7_inherits() (which rebuilt the class name on every
call; the per-node cone-detection scan benefits most). Deterministic
memory allocation is unchanged. These are internal changes with no
user-visible API difference.problem_data() and
get_problem_data() now take an explicit gp
argument. Previously gp = TRUE passed through
... was silently ignored, compiling a geometric program as
a DCP problem.psolve() now takes
explicit enforce_dpp and ignore_dpp arguments,
matching CVXPY’s solve(). Previously they were silently
swallowed by ...; enforce_dpp = TRUE now
raises on a non-DPP parametrized problem instead of falling back to a
non-DPP compile.quad_over_lin(expr, constant) so the decision uses the
canonicalized denominator, matching CVXPY 1.9 behavior for cases such as
quad_over_lin(exp(x), 1) that should keep
ExpCone without adding an SOC block.Variable(c(n, n), diag = TRUE) handling through
CvxAttr2Constr.perspective() canonicalizer on PSD, NSD, and diag
matrix variables.project() on sparse Matrix-package
objects.Complex2Real
because CVXR’s R sparse-matrix backend cannot carry complex sparse
parameter tensors. Ordinary complex expression solving is still
supported through Complex2Real.scip package.
Supports LP, SOCP, MI-LP, and MI-SOCP problems. SCIP is registered in
the conic solver path with
SUPPORTED_CONSTRAINTS = list(Zero, NonNeg, SOC) and
MIP_CAPABLE = TRUE.scip_params
sub-list (matching CVXPY convention) for path-style parameter names like
"limits/time", "limits/gap".scip package lacks
dual API).TestSCIP class.xpress
package with dual-interface architecture: QP path
(XPRESS_QP_Solver) for LP/QP and conic path
(XPRESS_Conic_Solver) for SOCP/MI-LP/MI-SOCP. Both paths
support Zero and NonNeg constraints; the conic path also supports SOC.
MIP warm-start is supported.diag() and
norm() dispatch fixesdiag() now works on CVXR expressions:
diag(vector_expr) creates a diagonal matrix
(DiagVec), diag(square_matrix_expr) extracts
the diagonal (DiagMat). Falls through to
Matrix::diag() for non-Expression inputs, correctly
handling both Matrix S4 objects and base R matrices.norm() fallthrough now delegates to
Matrix::norm() instead of base::norm(), fixing
norm(sparse_matrix) when CVXR is loaded.t() and mean() switched from S7
method() to registerS3method() to avoid
demoting Matrix’s S4 generics.All applicable bug fixes from CVXPY v1.8.2 have been ported, each
annotated with ## CVXPY v1.8.2 fix: in the source.
solver_opts() and
use_quad_objsolver_opts() constructor for unified solver
options. Standard tolerance parameters (feastol,
reltol, abstol, num_iter) and
solver-specific parameters now flow through psolve(...) via
the solver_opts() constructor.use_quad_obj option (default TRUE):
when FALSE, forces conic decomposition path instead of QP,
enabling quad_form_canon to detect indefinite P
matrices.supports_quad_obj() generic on conic solvers
(Clarabel and SCS return TRUE, others
FALSE).SpecialIndex)New SpecialIndex atom (mirroring CVXPY’s
special_index) enables R-idiomatic element-wise selection
on matrix expressions:
x[cbind(rows, cols)]x[mask]x[c(1, 5, 9)]x[c(TRUE, FALSE, ...)]This makes it natural to constrain specific entries of a matrix variable, e.g., fixing observed entries of a partially-known matrix:
ind <- which(!is.na(Rmiss), arr.ind = TRUE)
prob <- Problem(Minimize(obj), list(X[ind] == Rmiss[ind]))Previously, x[i] on a matrix expression errored with
“Single-index selection on matrices not supported.” The workaround
required an element-wise multiply with a binary mask, which was
unintuitive.
Internally uses sparse selection matrix multiplication (reshape → sparse matmul), requiring no C++ changes.
sum_squares(),
power(x, 2), quad_over_lin(), and
huber() when the first argument has many elements (e.g.,
regression residuals with thousands of observations). The quadratic
canonicalizer was converting a sparse identity matrix to dense form.
Memory now scales linearly with problem size.This is a ground-up rewrite of CVXR using R’s S7 object system, designed to be isomorphic with CVXPY 1.8.1 for long-term maintainability. ~4-5x faster than CVXR 1.0-15 on typical problems.
boolean = TRUE or integer = TRUE in
Variable()).Parameter() class with DPP
(Disciplined Parameterized Programming) for efficient re-solves when
only parameter values change.psolve() as the primary solve interface, returning the
optimal value directly.solve() backward-compatibility wrapper returning a
cvxr_result list with $value,
$status, $solver, $getValue(),
and $getDualValue().verbose = TRUE option in psolve() for
structured solve output with timing information.feastol,
reltol, abstol, num_iter) in
psolve() with automatic translation to solver-native names
via solver_default_param(). Solver-native parameters in
... take priority.problem_data(),
solve_via_data(), problem_unpack_results() for
compile-once/solve-many workflows.visualize() for problem introspection: text display
with Smith form annotations, interactive HTML output (D3 tree + KaTeX),
on-demand DCP violation analysis for non-compliant problems, curvature
coloring on constraint nodes, and matrix stuffing visualization (Stages
4-5: Standard Form + Solver Data) via the new solver
parameter.as_cvxr_expr().
Sparse Matrix objects (dgCMatrix, dgeMatrix,
etc.) use S4 dispatch which preempts S7/S3, so direct arithmetic like
sparseA %*% x fails. Wrapping with
as_cvxr_expr(sparseA) %*% x converts to a CVXR Constant
while preserving sparsity. Base R matrix and
numeric work natively without wrapping.+ and -
operators, matching CVXPY’s Expression.broadcast().
Expressions with compatible but different shapes (e.g.,
(1, n) + (m, 1) → (m, n)) now work correctly
in constraints and objectives. Previously only scalar promotion was
supported.MatrixFrac,
TrInv, SigmaMax, NormNuc,
LambdaSumLargest) are now correctly classified, ensuring
solvers like ECOS that don’t support SDP are rejected with a clear error
message instead of a cryptic dimension mismatch. Approximate variants
(PnormApprox, PowerApprox,
GeoMeanApprox) correctly route to SOC; exact variants route
to their native cone types (PowCone3D,
PowConeND).Variable(n) instead of
new("Variable", n).chooseOpsMethod() and S7
@ support).solve() now returns a cvxr_result S3 list,
not an S4 object. Use psolve() for a direct numeric return
value.getValue(x) and getDualValue(con) are
deprecated. Use value(x) and dual_value(con)
after solving instead.problem_status(), problem_solution(), and
get_problem_data() are deprecated. Use
status(), solution(), and
problem_data() instead.axis parameter uses 1-based indexing matching R’s
apply() MARGIN convention: axis = 1 for
row-wise (reduce columns), axis = 2 for column-wise (reduce
rows), axis = NULL for all entries.is(x, "Variable") no longer work.
Use S7_inherits(x, Variable).ptp(x, axis, keepdims) – peak-to-peak (range):
max(x) - min(x).cvxr_mean(x, axis, keepdims) – arithmetic mean along an
axis.cvxr_std(x, axis, keepdims, ddof) – standard
deviation.cvxr_var(x, axis, keepdims, ddof) – variance.vdot(x, y) – vector dot product (inner product).scalar_product(x, y) – alias for
vdot.cvxr_outer(x, y) – outer product of two vectors.inv_prod(x) – reciprocal of product of entries.loggamma(x) – elementwise log of gamma function
(piecewise linear approximation).log_normcdf(x) – elementwise log of standard normal CDF
(quadratic approximation).cummax_expr(x, axis) – cumulative maximum along an
axis.dotsort(X, W) – weighted sorted dot product
(generalization of
sum_largest/sum_smallest).diff_pos(), resolvent() – DGP convenience
functions.status(prob) – returns the solution status (replaces
problem_status()).solution(prob) – returns the raw Solution object
(replaces problem_solution()).problem_data(prob, solver) – returns solver-ready data
(replaces get_problem_data()).tv() is a deprecated alias for
total_variation().norm2(x) is a deprecated alias for
p_norm(x, 2).multiply(x, y) is a deprecated alias for
x * y.installed_solvers() lists available solver
packages.psolve(prob, gp = TRUE). New atoms:
prod_entries(), cumprod(),
one_minus_pos(), eye_minus_inv(),
pf_eigenvalue(), gmatmul().psolve(prob, qcp = TRUE). New atoms:
ceil_expr(), floor_expr(),
condition_number(), gen_lambda_max(),
dist_ratio().Variable(n, complex = TRUE) and Complex2Real
reduction. Atoms: Conj_(), Real_(),
Imag_(), expr_H() for conjugate
transpose.perspective(f, s) atom for perspective functions.FiniteSet(expr, values) constraint for discrete
optimization.Not(), And(),
Or(), Xor(), implies(),
iff().%>>% and %<<% operators for
PSD and NSD constraints.as_cvxr_expr() helper for wrapping R objects as CVXR
constants. Required for Matrix package objects (dgCMatrix,
dgeMatrix, etc.) which use S4 dispatch that preempts S7/S3.
Preserves sparsity (unlike as.matrix()). Base R
matrix/numeric work natively without
wrapping.kron() KRON_L coefficient matrix
construction.CvxAttr2Constr index type and matrix variable
handling.diag offset for super/sub-diagonals
(k != 0).suc - slc
reconstruction).dgCMatrix,
dgeMatrix, ddiMatrix,
sparseVector) cannot be used directly with CVXR operators
due to S4 dispatch preempting S7/S3. Wrap with
as_cvxr_expr() first. Base R
matrix/numeric work natively. This is an R
dispatch limitation requiring upstream changes in S7 and/or Matrix.highs
1.14) but the upstream R highs PR exposing
setSolution() has not yet been actioned by the maintainers;
lives on branch highs-warm-start.complex2real.py:56 declares
UNIMPLEMENTED_COMPLEX_DUALS = (SOC, OpRelEntrConeQuad)).
Complex SOC primal canonicalization works.clarabel requirement and use enhance rather than
import until really necessary (Issue 142).user_limit etc to be
infeasible_inaccurate to match CVXPY for
gurobisolve().upper_tri_to_full to C++Power object (Issue 145).qp2quad_form.Rextract_quadratic_coeffs to use sparse matrix and
sweep in place for better memory use (reported by Marissa Reitsma)Rmosek to be removed from CRAN, so moved to drat
repodgCMatrix via
(as(as(<matrix>, "CsparseMatrix"), "generalMatrix"))inherits()MOSEK and
uncommented associated test.ParameterOSQP and updates to solver cacheDropped delay_load parameter dropped in
reticulate::import_from_path, per changes in
reticulate.
Cleaned up hooks into reticulate for commercial solvers.
LPSOLVE via lpSolveAPIGLPK via RglpkMOSEKGUROBIdrop = FALSE (in function
Index.get_special_slice) as suspected.Added a note that CVXR can probably be compiled from source for earlier versions of R. This is issue #24
Bug fix: issue
#28 Function intf_sign (interface.R) was
unnecessarily using a tolerance parameter, now eliminated.
Updated Solver.solve to adapt to new ECOSolveR. Require version 0.4 of ECOSolveR now.
Updated unpack_results to behave exactly like in
CVXPY. Added documentation and testthat tests. Documented in the Speed.
Need a high-speed mirror for your open-source project?
Contact our mirror admin team at info@clientvps.com.
This archive is provided as a free public service to the community.
Proudly supported by infrastructure from VPSPulse , RxServers , BuyNumber , UnitVPS , OffshoreName and secure payment technology by ArionPay.