Reference

Table of Contents

Operations for declaring and sizing variables

Variables are created with one of the following idioms:

//instantiate a scalar variable
val x = variable()
val x = variable(scalar())
//instantiate a vector variable of the given length
val x = variable(vector(length))
//instantiates a symmetric matrix variable of the given size
val x = variable(smatrix(size))
//instantiates a variable with the same shape as variable y
val x = variable(shapeof(y)) 

Note the use of the val keyword instead of the var keyword, and the lack of a type annotation. Using var may result in unexpected behavior. Here, length and size must both be integer-valued (although they need not be compile-time constants), while y must be the name of an already-declared variable.

All variables are also OptiCVX expressions, and are assigned a vexity of "affine".

Operations on expressions

In addition to variable expressions as described above, expressions can also be created from scalar constants. To use a double-typed scalar as an expression in OptiCVX code, simply include it in the code where you want the expression to be used. If the scalar is a compile-time constant, then its sign will be determined and used for DCP validation. If the value is known only at runtime, this can't be done, so it needs to be annotated with its sign using either the positive or negative function, as illustrated below:

//if f is some double-typed variable:
positive(f)*max(x,y) //result is still convex
negative(f)*max(x,y) //result is now concave

The positive and negative functions check the sign of their arguments at runtime and will emit a warning if the sign is not what was expected, since this indicates a runtime DCP error.

Expressions can be combined into new expressions using any of the following mathematical operators:

//sum or difference of two expressions A and B
A + B
A - B
//negation of an expression A
-A
//product of an expression A with a scalar constant c
c * A
A * c
//indexing of a vector at index i or a matrix at index (i,j)
A(i)
A(i,j)

These operations assign vexity as specified in the DCP rules. In particular, for an expression to be indexed it must be affine, and the result of the indexing will be a scalar affine expression.

Operations for declaring constraints

OptiCVX supports constraints created by the following functions and operators:

//affine constraints
constrain_zero(A)
A == B
//inequality constraints (linear programming)
constrain_nonnegative(A)
A <= B
A >= B
//second-order cone constraint: norm(A: vector) <= B: scalar
constrain_secondordercone(A,B)
//semidefinitness constraint (for smatrix-shaped A)
constrain_semidefinite(A)

Affine and conic constraints require their arguments to be affine. Inequality constraints require their arguments to have appropriate vexity to satisfy the DCP requirements.

Operations for optimization

OptiCVX supports the following optimization expressions:

//minimization and maximization of expression E over variables Vi
minimize(E) over (V1,V2,...,Vn)
maximize(E) over (V1,V2,...,Vn)
//recover the value of a (scalar-shaped) expression X after the solver has run
resolve(X)

Minimization expressions require convex arguments and maximization expressions require concave arguments. It is an error to try to resolve an expression when some of its dependant variables have not yet been bound to a solver.

Operations for creating custom functions

OptiCVX supports the creation of arbitrary convex functions by the user, provided those functions can be expressed as partial optimization functions over the provided conic primitives (positive simplex, second-order cone, and semidefinite-cone). The syntax for doing so is illustrated below:

//create a custom function foo with n arguments
val foo = cvxfun (v) arguments (m1,m2,...,mn) body ((x1,x2,...,xn) => {
  ...
  body of function
  ...
})
//where v represents the vexity of the function, and can take on the values
affine
convex
concave
novexity
//and mi represents the monotonicity of the i-th argument, and can take on the values
constant
nondecreasing
nonincreasing
nomonotonicity

The body of the function must be a function of the arguments xi, and should typically be a partial optimization operation. See the example code for an example of this.

Once we have defined the function, we can invoke it as follows:

//invoke function foo just as we would a normal function
foo(x1,x2,...,xn)

The result is an OptiCVX expression with vexity as defined in the DCP ruleset.

Operations for analyzing DCP structure

OptiCVX contains one operator that allows the user to analyze the structure of the convex program that he/she is writing. The introspect operator will print a diagnostic message at staging-time that specifies the vexity of the expression. It is used as follows:

//print a diagnostic message about the vexity of expression A
introspect(A)
introspect(A,"name")

The second form here will tag the printed vexity expression with the given name, which must be a string constant.