CVXGEN: Code Generation for Convex Optimization

Using the C interface

For the most flexible and best performing solver, use C directly. Once you have generated code online, do the following.

  1. Download and extract the ‘cvxgen.zip’ archive for your problem. This will create a subdirectory called cvxgen/.

  2. CVXGEN automatically creates random example data, (which may not be a feasible, unbounded problem instance). Use this to test compilation and basic operation, by typing make, then ./testsolver. Check several iterations occur.

  3. Integrate the CVXGEN solver using testsolver.c, or the following example.

Complete example

This is a complete C file that uses a CVXGEN solver. It is similar to the testsolver.c file produced by CVXGEN.

// Load and instantiate global namespaces with relevant data and working space.
#include "solver.h"
Vars vars;
Params params;
Workspace work;
Settings settings;

void load_data(Params params) {
  // In this function, load all problem instance data.
  params.A[i] = ...;
  ...
}

void use_solution(Vars vars) {
  // In this function, use the optimization result.
  ... = vars.x[i];
}

int main(int argc, char **argv) {
  set_defaults();  // Set basic algorithm parameters.
  setup_indexing();

  for (;;) {  // Main control loop.
    load_data(params);

    // Solve our problem at high speed!
    num_iters = solve();
    // Recommended: check work.converged == 1.

    use_solution(vars)
  }
}

Now we will look at the parts of this example in more detail.

Allocating memory

You need to allocate space for four structures, all defined in solver.h. No other space in memory is required or used. Thus, the exact memory usage for each solver may be found ahead of time. The recommended approach is to statically allocate these structures as global variables. They are then treated as namespaces in the CVXGEN code. In the example above, this is done with the lines

Vars vars;
Params params;
Workspace work;
Settings settings;

In a future revision, these may be passed between functions to allow more flexibility. Contact me if you have thoughts about this.

Compilers and libraries

We strongly recommend you use the latest possible version of gcc, for best performance. For some examples and architectures, version 4.3 can produce code three times faster than version 4.0, with version 4.4 another 15% faster. It is worth experimenting with compiler options as well; start with -Os.

CVXGEN uses the stdio and math libraries by default. The only functions used from each of these are the printf and sqrt functions, respectively — and both are used in verbose mode only. To completely remove all library dependencies, add

#define ZERO_LIBRARY_MODE

to solver.h. This will disable verbose output of solver progress and other reporting, but will not affect core solver functionality.

Setting parameters and retrieving variables

All data in CVXGEN are stored in flat arrays, in column-major form with zero-based indices. (Thus, entries 0–3, say, are the first column of a matrix with 4 rows.) In particular, for a parameter matrix A in mathbf{R}^{m times n}, entry A_{ij} is stored in params.A[(i-1) + (j-1)*m)]. For consistency, the same applies for vectors, and even scalars. Symmetric matrices are stored in exactly the same way, but only the diagonal entries are stored for diagonal matrices. Here are some examples.

// Set all entries of a 5x3 matrix A.
for (int i = 0; i < 5; i++)
  for (int j = 0; i < 3; i++)
    params.A[i+j*5] = ...;

// Set the scalar parameter lambda. Note the 0 array index.
params.lambda[0] = ...;

// Variables are retrieved in the same way, from the vars structure.
for (int i = 0; i < n; i++)
  printf("  %9.4f\n", vars.x[i]);

Parameter checking

For performance and embedding reasons, no checks will be performed on parameters of sizes, shapes or attributes. CVXGEN may do something useful if you violate your parameter specifications, but it's unlikely, and definitely not recommended or guaranteed.

Indexed parameters or variables

In your CVXGEN problem specification, you may have specified indexed parameters or variables, such as

parameters
  y[i] (3), i=1..3
end

You can provide data for these parameters, in C, in two different ways. With the first, an underscore separates the index and the variable name. With the second, we use two indices to index into the parameter, and then into the parameter's data array. The latter is more convenient for use with loops.

// Assign initial parameters, option 1.
params.y_1[j] = ...;

// Assign initial parameters, option 2. Equivalent.
setup_indexing();  // call this once, at set up, before using option 2.
params.y[1][j] = ...;

Modifying solver settings

If you want to modify solver behavior, you can change the settings. See more information about the settings.

settings.verbose = 0;  // disable output of solver progress.
settings.max_iters = 10;  // reduce the maximum iteration count, from 25.
settings.eps = 0.1;  // reduce the required objective tolerance, from 1e-6.
settings.eps = 1e-2;  // reduce the required residual tolerances, from 1e-4.

// Solve with these settings.
solve();