cvxgrp / CVXR

@@ -298,10 +298,23 @@
Loading
298 298
  if(gp)
299 299
    reductions <- c(reductions, Dgp2Dcp())
300 300
301 -
  if(!gp && !is_dcp(problem))
302 -
    stop("Problem does not follow DCP rules. However, the problem does follow DGP rules. Consider calling this function with gp = TRUE")
303 -
  else if(gp && !is_dgp(problem))
304 -
    stop("Problem does not follow DGP rules. However, the problem does follow DCP rules. Consider calling this function with gp = FALSE")
301 +
  if(!gp) {
302 +
      if (!is_dcp(problem)) {
303 +
          err_msg  <- "Problem does not follow DCP rules."
304 +
          if (is_dgp(problem)) {
305 +
              err_msg  <- paste(err_msg, "However, the problem does follow DGP rules. Consider calling this function with gp = TRUE")
306 +
          }
307 +
          stop(err_msg)
308 +
      }
309 +
  } else {
310 +
      if(!is_dgp(problem)) {
311 +
          err_msg  <- "Problem does not follow DGP rules."
312 +
          if (is_dcp(problem)) {
313 +
              err_msg  <- paste(err_msg, "However, the problem does follow DCP rules. Consider calling this function with gp = FALSE")
314 +
          }
315 +
          stop(err_msg)
316 +
      }
317 +
  }
305 318
306 319
  # Dcp2Cone and Qp2SymbolicQp require problems to minimize their objectives.
307 320
  if(class(problem@objective) == "Maximize")

@@ -69,7 +69,8 @@
Loading
69 69
    OSQP = list(max_iter = 10000, eps_abs = 1e-5, eps_rel = 1e-5, eps_prim_inf = 1e-4),
70 70
    ECOS = list(maxit = 100, abstol = 1e-8, reltol = 1e-8, feastol = 1e-8),
71 71
    ECOS_BB = list(maxit = 1000, abstol = 1e-6, reltol = 1e-3, feastol = 1e-6),
72 -
    ##CVX_OPT = list(max_iters = 100, abstol = 1e-7, reltol = 1e-6, feastol = 1e-7, refinement = 1L, kktsolver = "chol"),
72 +
    ## Until cccp fixes the bug I reported, we set the tolerances as below
73 +
    CVXOPT = list(max_iters = 100, abstol = 1e-6, reltol = 1e-6, feastol = 1e-6, refinement = 1L, kktsolver = "chol"),
73 74
    SCS = list(max_iters = 2500, eps = 1e-4, alpha = 1.8, scale = 5.0),
74 75
    CPLEX = list(itlim = 10000),
75 76
    MOSEK = list(num_iter = 10000),

@@ -208,6 +208,9 @@
Loading
208 208
    coeff_sum <- Reduce("+", coeffs)
209 209
    offset_sum <- Reduce("+", offsets)
210 210
    return(list(coeff_sum, as.vector(offset_sum)))
211 +
  } else if(class(constr) == "PSDConstraint") {
212 +
    ## Sign flipped relative fo NonPos, Zero.
213 +
    return(list(-coeffs[[1L]], offsets[[1L]]))
211 214
  } else
212 215
    # subclasses must handle PSD constraints.
213 216
    stop("Unsupported constraint type.")
@@ -1116,7 +1119,7 @@
Loading
1116 1119
# Solver capabilities.
1117 1120
#' @describeIn CVXOPT Can the solver handle mixed-integer programs?
1118 1121
setMethod("mip_capable", "CVXOPT", function(solver) { FALSE })
1119 -
setMethod("supported_constraints", "CVXOPT", function(solver) { c(supported_constraints(ConicSolver()), "SOC", "ExpCone", "PSDConstraint") })
1122 +
setMethod("supported_constraints", "CVXOPT", function(solver) { c(supported_constraints(ConicSolver()), "SOC", "PSDConstraint") })
1120 1123
1121 1124
# Map of CVXOPT status to CVXR status.
1122 1125
#' @param solver,object,x A \linkS4class{CVXOPT} object.
@@ -1125,11 +1128,13 @@
Loading
1125 1128
setMethod("status_map", "CVXOPT", function(solver, status) {
1126 1129
  if(status == "optimal")
1127 1130
    OPTIMAL
1128 -
  else if(status == "infeasible")
1131 +
  else if(status %in% c("infeasible", "primal infeasible",
1132 +
                        "LP relaxation is primal infeasible"))
1129 1133
    INFEASIBLE
1130 -
  else if(status == "unbounded")
1134 +
  else if(status %in% c("unbounded", "LP relaxation is dual infeasible",
1135 +
                        "dual infeasible"))
1131 1136
    UNBOUNDED
1132 -
  else if(status == "solver_error")
1137 +
  else if(status %in% c("solver_error", "unknown", "undefined"))
1133 1138
    SOLVER_ERROR
1134 1139
  else
1135 1140
    stop("CVXOPT status unrecognized: ", status)
@@ -1139,16 +1144,16 @@
Loading
1139 1144
setMethod("name", "CVXOPT", function(x) { CVXOPT_NAME })
1140 1145
1141 1146
#' @describeIn CVXOPT Imports the solver.
1142 -
##setMethod("import_solver", "CVXOPT", function(solver) { requireNamespace("cccopt", quietly = TRUE) })
1143 -
##setMethod("import_solver", "CVXOPT", function(solver) { requireNamespace("cccp", quietly = TRUE) })
1147 +
1144 1148
## CVXOPT is not implemented as there is no R package equivalent to cccopt. We should check out cccp, though
1145 -
setMethod("import_solver", "CVXOPT", function(solver) { FALSE })
1149 +
setMethod("import_solver", "CVXOPT", function(solver) { requireNamespace("cccp", quietly = TRUE) })
1146 1150
1147 1151
#' @param problem A \linkS4class{Problem} object.
1148 1152
#' @describeIn CVXOPT Can CVXOPT solve the problem?
1149 1153
setMethod("accepts", signature(object = "CVXOPT", problem = "Problem"), function(object, problem) {
1150 1154
  # Can CVXOPT solver the problem?
1151 1155
  # TODO: Check if the matrix is stuffed.
1156 +
  import_solver(object)
1152 1157
  if(!is_affine(problem@objective@args[[1]]))
1153 1158
    return(FALSE)
1154 1159
  for(constr in problem@constraints) {
@@ -1195,21 +1200,124 @@
Loading
1195 1200
  return(list(object, data, inv_data))
1196 1201
})
1197 1202
1203 +
#' @param solution The raw solution returned by the solver.
1204 +
#' @param inverse_data A list containing data necessary for the inversion.
1205 +
#' @describeIn CVXOPT Returns the solution to the original problem given the inverse_data.
1206 +
setMethod("invert", signature(object = "CVXOPT", solution = "list", inverse_data = "list"), function(object, solution, inverse_data) {
1207 +
  status <- solution$status
1208 +
  primal_vars <- list()
1209 +
  dual_vars <- list()
1210 +
  if(status %in% SOLUTION_PRESENT){
1211 +
    opt_val <- solution$value + inverse_data[[OFFSET]]
1212 +
    primal_vars[[as.character(inverse_data[[object@var_id]])]] <- solution$primal
1213 +
    eq_dual <- get_dual_values(solution$eq_dual, extract_dual_value, inverse_data[[object@eq_constr]])
1214 +
    leq_dual <- get_dual_values(solution$ineq_dual, extract_dual_value, inverse_data[[object@neq_constr]])
1215 +
    eq_dual <- utils::modifyList(eq_dual, leq_dual)
1216 +
    dual_vars <- eq_dual
1217 +
    return(Solution(status, opt_val, primal_vars, dual_vars, list()))
1218 +
  } else {
1219 +
    return(failure_solution(status))
1220 +
  }
1221 +
})
1222 +
1198 1223
#' @param data Data generated via an apply call.
1199 1224
#' @param warm_start A boolean of whether to warm start the solver.
1200 1225
#' @param verbose A boolean of whether to enable solver verbosity.
1201 1226
#' @param solver_opts A list of Solver specific options
1202 1227
#' @param solver_cache Cache for the solver.
1203 1228
#' @describeIn CVXOPT Solve a problem represented by data returned from apply.
1204 -
setMethod("solve_via_data", "CVXOPT", function(object, data, warm_start, verbose, solver_opts, solver_cache) {
1205 -
    stop("Unimplemented!")
1206 -
  ## if (missing(solver_cache)) solver_cache <- new.env(parent=emptyenv())
1207 -
  ## solver <- CVXOPT_OLD()
1208 -
  ## prob_data <- list()
1209 -
  ## prob_data[[name(object)]] <- ProblemData()
1210 -
  ## solve(solver, data$objective, data$constraints, prob_data, warm_start, verbose, solver_opts)
1229 +
setMethod("solve_via_data", "CVXOPT", function(object, data, warm_start, verbose, feastol, reltol, abstol,
1230 +
                                               num_iter, solver_opts, solver_cache) {
1231 +
  #Tweak parameters
1232 +
  if(is.null(feastol)) {
1233 +
    feastol <- SOLVER_DEFAULT_PARAM$CVXOPT$feastol
1234 +
  }
1235 +
  if(is.null(reltol)) {
1236 +
    reltol <- SOLVER_DEFAULT_PARAM$CVXOPT$reltol
1237 +
  }
1238 +
  if(is.null(abstol)) {
1239 +
    abstol <- SOLVER_DEFAULT_PARAM$CVXOPT$abstol
1240 +
  }
1241 +
  if(is.null(num_iter)) {
1242 +
    num_iter <- SOLVER_DEFAULT_PARAM$CVXOPT$max_iters
1243 +
  }
1244 +
  param <- cccp::ctrl(maxiters=as.integer(num_iter), abstol=abstol, reltol=reltol,
1245 +
                      feastol=feastol, trace=as.logical(verbose))
1246 +
  param$params[names(solver_opts)] <- solver_opts
1247 +
1248 +
  G <- as.matrix(data[[G_KEY]])
1249 +
  h <- as.matrix(data[[H_KEY]])
1250 +
  nvar <- dim(G)[2]
1251 +
  dims <- data[[DIMS]]
1252 +
  zero_dims <- dims@zero
1253 +
  nonpos_dims <- dims@nonpos
1254 +
  soc_dims <- dims@soc
1255 +
  psd_dims <- dims@psd
1256 +
  clistLength <- (nonpos_dims > 0) + length(soc_dims) + length(psd_dims)
1257 +
1258 +
  # For all the constraints except the zero constraint
1259 +
  clist <- vector(mode="list", length = clistLength)
1260 +
  clistCounter <- 0
1261 +
  ghCounter <- 0
1262 +
1263 +
  # Deal with non positive constraints
1264 +
    if(nonpos_dims > 0){
1265 +
        clistCounter <- clistCounter + 1
1266 +
        indices  <- seq.int(from = ghCounter + 1, length.out = nonpos_dims)
1267 +
        clist[[clistCounter]] <- cccp::nnoc(G = G[indices, , drop = FALSE],
1268 +
                                            h = h[indices, , drop = FALSE])
1269 +
        ghCounter <- ghCounter + nonpos_dims
1270 +
    }
1271 +
1272 +
  # Deal with SOC constraints
1273 +
    for(i in soc_dims){
1274 +
        clistCounter <- clistCounter + 1
1275 +
        indices  <- seq.int(from = ghCounter + 2, length.out = i - 1)
1276 +
        clist[[clistCounter]] <- cccp::socc(F = -G[indices, , drop = FALSE],
1277 +
                                            g = h[indices, , drop = FALSE],
1278 +
                                            d = -G[ghCounter + 1, , drop = FALSE],
1279 +
                                            f = h[ghCounter + 1, , drop = FALSE])
1280 +
        ghCounter <- ghCounter + i
1281 +
    }
1282 +
1283 +
  # Deal with PSD constraints
1284 +
  for(i in psd_dims){
1285 +
      Flist <- vector(mode="list", length = nvar+1)
1286 +
      indices  <- seq.int(from = ghCounter + 1, length.out = i^2)
1287 +
      currG <- G[indices, , drop = FALSE]
1288 +
      currh <- h[indices, , drop = FALSE]
1289 +
      Flist[[1]] <- matrix(currh, nrow = i)
1290 +
      for(j in seq_len(nvar)){
1291 +
          Flist[[j+1]] <- matrix(currG[, j, drop = FALSE], nrow = i)
1292 +
      }
1293 +
      clistCounter <- clistCounter + 1
1294 +
      clist[[clistCounter]] <- cccp::psdc(Flist = Flist[-1], F0 = Flist[[1]])
1295 +
      ghCounter <- ghCounter + i
1296 +
  }
1297 +
1298 +
  if(zero_dims > 0){
1299 +
    results <- cccp::cccp(q=data[[C_KEY]],
1300 +
                    A=as.matrix(data[[A_KEY]]),
1301 +
                    b=as.matrix(data[[B_KEY]]),
1302 +
                    cList=clist,
1303 +
                    optctrl=param)
1304 +
  } else {
1305 +
    results <- cccp::cccp(q=data[[C_KEY]],
1306 +
                    cList=clist,
1307 +
                    optctrl=param)
1308 +
  }
1309 +
  solution <- list()
1310 +
  solution$status <- status_map(object, results$status)
1311 +
  solution$value <- (results$state[1] + data[[OFFSET]])[[1]]
1312 +
  solution$primal <- cccp::getx(results)
1313 +
  solution$eq_dual <- cccp::gety(results)
1314 +
  solution$ineq_dual <- unlist(cccp::getz(results))
1315 +
  #solution$ineq_dual <- as.matrix(c(temp[[1]], temp[[2]], temp[[3]], temp[[4]][abs(temp[[4]]) > 1e-8])[1:nrow(G)])
1316 +
1317 +
  return(solution)
1211 1318
})
1212 1319
1320 +
1213 1321
#' An interface for the ECOS BB solver.
1214 1322
#'
1215 1323
#' @name ECOS_BB-class
@@ -1884,7 +1992,7 @@
Loading
1884 1992
#' @param solver,object,x A \linkS4class{MOSEK} object.
1885 1993
#' @describeIn MOSEK Can the solver handle mixed-integer programs?
1886 1994
setMethod("mip_capable", "MOSEK", function(solver) { TRUE })
1887 -
setMethod("supported_constraints", "MOSEK", function(solver) { c(supported_constraints(ConicSolver()), "SOC", "PSDConstraint") })
1995 +
setMethod("supported_constraints", "MOSEK", function(solver) { c(supported_constraints(ConicSolver()), "SOC", "PSDConstraint", "ExpCone") })
1888 1996
1889 1997
#' @describeIn MOSEK Imports the solver.
1890 1998
#' @importFrom utils packageDescription
@@ -1938,7 +2046,7 @@
Loading
1938 2046
    offset <- coeff_offs[[2]]
1939 2047
    matrices <- c(matrices, list(coeff))
1940 2048
    offsets <- c(offsets, offset)
1941 -
    lengths <- c(lengths, prod(dim(offset)))
2049 +
    lengths <- c(lengths, prod(dim(as.matrix(offset))))
1942 2050
    ids <- c(ids, id(con))
1943 2051
  }
1944 2052
  coeff <- Matrix(do.call(rbind, matrices), sparse = TRUE)
@@ -2202,7 +2310,7 @@
Loading
2202 2310
    running_idx <- running_idx + unlist_dims_SOC_DIM[[i]]
2203 2311
  }
2204 2312
  if(floor(sum(unlist_dims_EXP_DIM, na.rm = TRUE)/3) != 0){ # check this, feels sketchy
2205 -
    for(k in 1:(floor(sum(unlist_dims_EXP_DIM, na.rm = TRUE)/3)+1) ) {
2313 +
    for(k in 1:floor(sum(unlist_dims_EXP_DIM, na.rm = TRUE)/3) ) {
2206 2314
      prob$cones[,(length_dims_SOC_DIM+k)] <- list("PEXP", as.numeric((running_idx+1):(running_idx + 3)) )
2207 2315
      running_idx <- running_idx + 3
2208 2316
    }

@@ -6,7 +6,8 @@
Loading
6 6
# solver_conic_intf <- list(ECOS(), ECOS_BB(), CVXOPT(), GLPK(), XPRESS(), GLPK_MI(), CBC_CONIC(), SCS(), SuperSCS(), GUROBI_CONIC(), MOSEK(), CPLEX_CONIC())
7 7
# solver_qp_intf <- list(OSQP(), GUROBI_QP(), CPLEX_QP())
8 8
9 -
solver_conic_intf <- list(ECOS(), ECOS_BB(), CBC_CONIC(), CPLEX_CONIC(), GLPK_MI(), GLPK(), SCS(), GUROBI_CONIC(), MOSEK())
9 +
solver_conic_intf <- list(ECOS(), ECOS_BB(), CBC_CONIC(), CPLEX_CONIC(), CVXOPT(),
10 +
                          GLPK_MI(), GLPK(), SCS(), GUROBI_CONIC(), MOSEK())
10 11
solver_qp_intf <- list(OSQP(), GUROBI_QP(), CPLEX_QP())
11 12
12 13
SOLVER_MAP_CONIC <- solver_conic_intf
Files Coverage
R 59.87%
inst/include 100.00%
src 73.96%
Project Totals (45 files) 61.26%
1
comment: false
2

3
coverage:
4
  status:
5
    project:
6
      default:
7
        target: auto
8
        threshold: 1%
9
    patch:
10
      default:
11
        target: auto
12
        threshold: 1%
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading