INSTALLED_SOLVERS <- installed_solvers() TOL <- 1e-4 x <- Variable(2, name = "x") slope <- Variable(1, name = "slope") offset <- Variable(1, name = "offset") quadratic_coeff <- Variable(1, name = "quadratic_coeff") #LP, QP, SOCP, SDP, MIP, GP test_that("Test a basic LP with all solvers", { skip_on_cran() # Example from # http://cvxopt.org/userguide/coneprog.html?highlight=solvers.lp#cvxopt.solvers.lp objective <- Minimize(-4*x[1] - 5*x[2]) constraints <- list(2*x[1] + x[2] <= 3, x[1] + 2*x[2] <= 3, x[1] >= 0, x[2] >= 0) prob <- Problem(objective, constraints) ## scs3.0 fix for tol have to be more lenient... for(solver in INSTALLED_SOLVERS){ result <- solve(prob, solver = solver) expect_equal(result$value, -9, tolerance = 10 * TOL) expect_equal(result$getValue(x), as.matrix(c(1, 1)), tolerance = 10 * TOL) } #Example from # https://www.cvxpy.org/examples/basic/linear_program.html m <- 15 n <- 10 set.seed(1) s0 <- rnorm(m) lamb0 <- pmax(-s0, 0) s0 <- pmax(s0, 0) x0 <- rnorm(n) A <- matrix(rnorm(m * n), nrow = m, ncol = n) b <- A %*% x0 + s0 c <- -t(A) %*% lamb0 x_var <- Variable(n) prob <- Problem(Minimize(t(c) %*% x_var), list(A %*% x_var <= b)) for(solver in INSTALLED_SOLVERS){ suppressWarnings( result <- solve(prob, solver = solver, abstol = 1e-6, verbose = TRUE) ) expect_equal(result$value, -7.83446, tolerance = TOL) } }) #QP test_that("Test a basic QP with all solvers", { skip_on_cran() set.seed(0) #Regression 1 test-g01-qp.R n <- 100 # Specify the true value of the variable true_coeffs <- matrix(c(2, -2, 0.5), ncol = 1) # Generate data x_data <- 5*rnorm(n) x_data_expanded <- sapply(1:3, function(i) { x_data^i }) y_data <- x_data_expanded %*% true_coeffs + 0.5*runif(n) line <- offset + slope*x_data residuals <- line - y_data fit_error <- sum_squares(residuals) p <- Problem(Minimize(fit_error), list()) model <- lm(y_data ~ x_data) ##CBC, ECOS_BB, GLPK_MI, GLPK don't support QPs applicable_solvers <- setdiff(INSTALLED_SOLVERS, list("CBC", "ECOS_BB", "GLPK_MI", "GLPK")) ## SCS is known to cause problems ## Works sometimes if we set rho_x = 1e-2, but too problematic applicable_solvers <- setdiff(applicable_solvers, c("SCS", "CVXOPT")) for(solver in applicable_solvers) { result <- solve(p, solver) print(sprintf("Solver: %s status: %s\n", solver, result$status)) expect_equal(sum((model$residuals)^2), result$value, tolerance = TOL) expect_equal(as.numeric(model$coefficients[1]), result$getValue(offset), tolerance = TOL) expect_equal(as.numeric(model$coefficients[2]), result$getValue(slope), tolerance = TOL) } ##Regression 2 test-g01-qp.R quadratic <- offset + slope*x_data + quadratic_coeff*x_data^2 residuals <- quadratic - y_data fit_error <- sum_squares(residuals) p <- Problem(Minimize(fit_error), list()) x_data_sq <- x_data^2 model <- lm(y_data ~ x_data + x_data_sq) for(solver in applicable_solvers) { ##cat(sprintf("Doing %s\n", solver)) result <- solve(p, solver) expect_equal(sum((model$residuals)^2), result$value, tolerance = TOL) expect_equal(as.numeric(model$coefficients[1]), result$getValue(offset), tolerance = TOL) expect_equal(as.numeric(model$coefficients[2]), result$getValue(slope), tolerance = TOL) expect_equal(as.numeric(model$coefficients[3]), result$getValue(quadratic_coeff), tolerance = TOL) } }) test_that("Test a basic SOCP with solvers", { skip_on_cran() applicable_solvers <- setdiff(INSTALLED_SOLVERS, list("CBC", "ECOS_BB", "GLPK_MI", "GLPK", "OSQP")) # Example from # http://cvxopt.org/userguide/coneprog.html?highlight=solvers.lp#cvxopt.solvers.lp objective <- Minimize(-4*x[1] - 5*x[2]) constraints <- list(2*x[1] + x[2] <= 3, (x[1] + 2*x[2])^2 <= 9, x[1] >= 0, x[2] >= 0) prob <- Problem(objective, constraints) for(solver in applicable_solvers) { result <- solve(prob, solver = solver) print(sprintf("Solver: %s status: %s\n", solver, result$status)) expect_equal(result$value, -9, tolerance = TOL) expect_equal(result$getValue(x), as.matrix(c(1, 1)), tolerance = TOL) } # Example from # https://www.cvxpy.org/examples/basic/socp.html set.seed(1) m <- 3 n <- 10 p <- 5 n_i <- 5 f = rnorm(n) A <- vector(mode="list", m) b <- vector(mode="list", m) c <- vector(mode="list", m) d <- vector(mode="list", m) x0 <- rnorm(n) soc_constraints <- vector(mode="list", m+1) x_var <- Variable(n) for(i in 1:m){ A[[i]] <- matrix(rnorm(n_i * n), nrow = n_i, ncol = n) b[[i]] <- rnorm(n_i) c[[i]] <- rnorm(n) d[[i]] <- norm(A[[i]] %*% x0 + b[[i]], "f") soc_constraints[[i]] <- CVXR:::SOC(t(c[[i]]) %*% x_var + d[[i]], A[[i]] %*% x_var + b[[i]]) } Fmat <- matrix(rnorm(p * n), nrow = p, ncol = n) g <- Fmat %*% x0 soc_constraints[[m+1]] <- Fmat %*% x_var == g prob <- Problem(Minimize(t(f)%*%x_var), soc_constraints) for(solver in applicable_solvers) { result <- solve(prob, solver=solver) print(sprintf("Solver: %s status: %s\n", solver, result$status)) expect_equal(result$value, -10.13341, tolerance=TOL) } }) test_that("Test a basic SDP with all solvers", { skip_on_cran() # Example from # https://www.cvxpy.org/examples/basic/sdp.html n <- 3 p <- 3 set.seed(4) C <- matrix(rnorm(n^2), nrow = n) A <- vector(mode="list", p) b <- numeric(p) X <- Variable(n, n, PSD=T) constraints <- list() for(i in 1:p){ A[[i]] <- matrix(rnorm(n^2), nrow = n) b[i] <- rnorm(1) constraints <- c(constraints, matrix_trace(A[[i]] %*% X) == b[i]) } prob <- Problem(Minimize(matrix_trace(C %*% X)), constraints) check_matrix <- matrix(c(.3771707, -.5692205, .1166577, -.5692205, .8590441, -.1760618, .11665767, -.17606183, .03608155), nrow = n) # SCS and MOSEK and CVXOPT, CLARABEL are the only solvers that support SDPs ## Not adding clarabel for now for(solver in intersect(INSTALLED_SOLVERS, c("SCS", "MOSEK", "CVXOPT"))) { result <- solve(prob, solver = solver) print(sprintf("Solver: %s status: %s\n", solver, result$status)) expect_equal(result$value, 1.395479, tolerance=TOL) expect_equal(result$getValue(X), check_matrix, tolerance=TOL) } }) test_that("Test a mixed-integer quadratic program",{ skip_on_cran() # Example from # https://www.cvxpy.org/examples/basic/mixed_integer_quadratic_program.html m <- 40 n <- 25 set.seed(1) A <- matrix(runif(m*n), nrow = m) b <- rnorm(m) x <- Variable(n, integer=T) obj <- Minimize(sum_squares(A %*% x - b)) prob <- Problem(obj) applicable_solvers <- intersect(INSTALLED_SOLVERS, c("ECOS_BB", "CPLEX", "GUROBI", "MOSEK")) for(solver in applicable_solvers) { result <- solve(prob, solver=solver) expect_equal(result$value, 13.34086, tolerance=TOL) } }) test_that("Test a simple geometric program", { skip_on_cran() # Example from # https://www.cvxpy.org/examples/dgp/dgp_fundamentals.html x <- Variable(pos=TRUE) y <- Variable(pos=TRUE) z <- Variable(pos=TRUE) obj <- x * y * z constraints <- list(4*x*y*z + 2*x*z <= 10, x <= 2*y, y <= 2*x, z >=1) prob <- Problem(Maximize(obj), constraints) ## Leave out CLARABEL for now for(solver in c("ECOS", "SCS")){ result <- solve(prob, solver=solver, gp=TRUE) expect_equal(result$value, 2, tolerance=TOL) expect_equal(result$getValue(x), 1, tolerance=TOL) expect_equal(result$getValue(y), 2, tolerance=TOL) expect_equal(result$getValue(z), 1, tolerance=TOL) } })