Search code examples
pythonmathematical-optimizationcvxpy

How to stack variables together in cvxpy?


I want to solve an optimisation problem using cvxpy. Suppose I want to use log_sum_exp function to build a constraint like this:

m >= log(1 + exp(m+z))

The syntax of cvxpy allows me to create a vector variable x = [z,m] of dimension 2 and apply matrix multiplication to create a vector of expressions 0, z+m:

import cvxpy
x = cvxpy.Variable(2)
coeff = np.array([
    [0,0],
    [1,1]
])
constraints = [ x[1] >= cvxpy.log_sum_exp(coeff * x)]

When coding like this, I lose some part of the logic because I want different names for different parts of my variable array. Is there some way to use the log_sum_exp transform more explicitly, like

z = cvxpy.Variable()
m = cvxpy.Variable()
constraints = [ m >= cvxpy.log_sum_exp([0, m+z]) ]

?

I couldn't find any hints in the official docs. Thanks!


Solution

  • As sascha pointed out, one of the manual pages

    Functions in CVXPY

    contains the answer. In particular, I can give an example of using log_sum_exp without matrix multiplication. Note that it is not possible to construct a correct problem within DCP (disciplined convex programming) framework using only operators exp and log because you will obtain concave function applied to convex one, which is considered as undefined behaviour. One should use a built-in constructor instead.

    If you want to encode the constraint

    F0 >= log( exp(F1) + exp(F2) + ... + exp(Fn) )
    

    where F1, F2, ..., Fn are some convex expressions, and F0 is a concave expression, then instead of introducing slack variables, one can type

    import cvxpy
    
    ... # Define variables and functions
    
    constraints = [
        ...,
        something >= cvxpy.log_sum_exp(
            cvxpy.vstack(
                F1,
                F2,
                ...,
                Fn
            )
        )
    ]
    
    ... # Solve the optimisation problem
    

    Note that vstack can be used both in multiple-argument style:

    cvxpy.vstack(z, u)
    

    and in list-style (but not tuples)

    cvxpy.vstack([z,u])