--- title: "Introduction to CVXR" author: "Anqi Fu and Balasubramanian Narasimhan" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction to CVXR} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## Overview `CVXR` is an R package that provides an object-oriented modeling language for convex optimization, similar to [CVXPY](https://www.cvxpy.org/) for Python. It allows you to formulate and solve convex optimization problems in a natural mathematical syntax. ## A Simple Example: Least Squares Consider a simple linear regression problem where we want to estimate parameters using a least squares criterion. We generate synthetic data where we know the true model: $$ Y = X\beta + \epsilon $$ where $Y$ is a $100 \times 1$ vector, $X$ is a $100 \times 10$ matrix, $\beta = (-4, -3, \ldots, 5)^\top$ is a $10 \times 1$ vector, and $\epsilon \sim N(0, 1)$. ```{r} set.seed(123) n <- 100 p <- 10 beta <- -4:5 X <- matrix(rnorm(n * p), nrow = n) Y <- X %*% beta + rnorm(n) ``` Using base R, we can estimate $\beta$ via `lm`: ```{r} ls.model <- lm(Y ~ 0 + X) ``` ### The CVXR formulation The same problem can be expressed as: $$ \underset{\beta}{\text{minimize}} \quad \|Y - X\beta\|_2^2 $$ In CVXR, this translates directly: ```{r} library(CVXR) betaHat <- Variable(p) objective <- Minimize(sum((Y - X %*% betaHat)^2)) problem <- Problem(objective) result <- psolve(problem, solver = "CLARABEL") ``` The optimal value and estimated coefficients: ```{r} cat("Optimal value:", result, "\n") cbind(CVXR = round(value(betaHat), 3), lm = round(coef(ls.model), 3)) ``` ## Adding Constraints The real power of CVXR is the ability to add constraints easily. ### Nonnegative Least Squares Suppose we know the $\beta$s should be nonnegative: ```{r} problem <- Problem(objective, constraints = list(betaHat >= 0)) result <- psolve(problem, solver = "CLARABEL") round(value(betaHat), 3) ``` ### Custom Constraints Now suppose $\beta_2 + \beta_3 \le 0$ and all other $\beta$s are nonnegative: ```{r} A <- matrix(c(0, 1, 1, rep(0, 7)), nrow = 1) B <- diag(c(1, 0, 0, rep(1, 7))) constraint1 <- A %*% betaHat <= 0 constraint2 <- B %*% betaHat >= 0 problem <- Problem(objective, constraints = list(constraint1, constraint2)) result <- psolve(problem, solver = "CLARABEL", verbose = TRUE) ## verbose = TRUE for details round(value(betaHat), 3) ``` This demonstrates the chief advantage of CVXR: _flexibility_. Users can quickly modify and re-solve a problem, making the package ideal for prototyping new statistical methods. Its syntax is simple and mathematically intuitive. ## Available Solvers CVXR supports multiple solvers: ```{r} installed_solvers() ``` You can specify a solver explicitly: ```{r, eval = FALSE} psolve(problem, solver = "CLARABEL") ``` ## Further Reading - The [CVXR website](https://cvxr.rbind.io) has many worked examples - The [CVXPY documentation](https://www.cvxpy.org/) covers the underlying mathematical framework - Fu, Narasimhan, and Boyd (2020). "CVXR: An R Package for Disciplined Convex Optimization." _Journal of Statistical Software_, 94(14). doi:10.18637/jss.v094.i14 ## Session Info ```{r} sessionInfo() ```