DCCP package provides an organized heuristic for convex-concave programming. It tries to solve nonconvex problems where every function in the objective and the constraints has any known curvature according to the rules of disciplined convex programming (DCP). For instance, DCCP can be used to maximize a convex function. The full details of our approach are discussed in the associated paper. DCCP is built on top of CVXPY, a domain-specific language for convex optimization embedded in Python.
You should first install CVXPY 1.1.
To install the most updated DCCP, please download the repository and run python setup.py install
inside.
To install DCCP from pip, please run pip install dccp
.
A problem satisfies the rules of disciplined convex-concave programming (DCCP) if it has the form
minimize/maximize o(x)
subject to l_i(x) ~ r_i(x), i=1,...,m,
where o
(the objective), l_i
(left-hand sides), and r_i
(right-hand sides) are expressions (functions
in the variable x
) with curvature known from the DCP composition rules, and ∼
denotes one of the
relational operators ==
, <=
, or >=
.
In a disciplined convex program, the curvatures of o
, l_i
, and r_i
are restricted to ensure that the problem is convex. For example, if the objective is maximize o(x)
, then o
must be concave according to the DCP composition rules. In a disciplined convex-concave program, by contrast, the objective and right and left-hand sides of the constraints can have any curvature, so long as all expressions satisfy the DCP composition rules.
The variables, parameters, and constants in DCCP should be real numbers. Problems containing complex numbers may not be supported by DCCP.
The following code uses DCCP to approximately solve a simple nonconvex problem.
import cvxpy as cvx
import dccp
x = cvx.Variable(2)
y = cvx.Variable(2)
myprob = cvx.Problem(cvx.Maximize(cvx.norm(x - y,2)), [0 <= x, x <= 1, 0 <= y, y <= 1])
print("problem is DCP:", myprob.is_dcp()) # false
print("problem is DCCP:", dccp.is_dccp(myprob)) # true
result = myprob.solve(method='dccp')
print("x =", x.value)
print("y =", y.value)
print("cost value =", result[0])
The output of the above code is as follows.
problem is DCP: False
problem is DCCP: True
x = [ 1. -0.]
y = [-0. 1.]
cost value = 1.4142135623730951
The solutions obtained by DCCP can depend on the initial point of the CCP algorithm.
The algorithm starts from the values of any variables that are already specified; for any that are not specified, random values are used.
You can specify an initial value manually by setting the value
field of the variable.
For example, the following code runs the CCP algorithm with the specified initial values for x
and y
:
x.value = numpy.array([1, 2])
y.value = numpy.array([-1, 1])
result = myprob.solve(method='dccp')
An option is to use random initialization for all variables by prob.solve(method=‘dccp’, random_start=TRUE)
, and by setting the parameter ccp_times
you can specify the times that the CCP algorithm runs starting from random initial values for all variables each time.
The components of the variable, the objective, and the constraints are constructed using standard CVXPY syntax. Once a problem object has been constructed, the following solve method can be applied.
problem.solve(method='dccp')
applies the CCP heuristic, and returns the value of the cost function, the maximum value of the slack variables, and the value of each variable. Additional arguments can be used to specify the parameters.
Solve method parameters:
- The
ccp_times
parameter specifies how many random initial points to run the algorithm from. The default is 1. - The
max_iter
parameter sets the maximum number of iterations in the CCP algorithm. The default is 100. - The
solver
parameter specifies what solver to use to solve convex subproblems. - The
tau
parameter trades off satisfying the constraints and minimizing the objective. Largertau
favors satisfying the constraints. The default is 0.005. - The
mu
parameter sets the rate at whichtau
increases inside the CCP algorithm. The default is 1.2. - The
tau_max
parameter upper bounds how largetau
can get. The default is 1e8.
If the convex solver for subproblems accepts any additional keyword arguments, such as warm_start=True
, then you can set them in the problem.solve()
function, and they will be passed to the convex solver.
After running the solve method, the result status is stored in problem.status
.
The status Converged
means that the algorithm has converged, i.e., the slack variables converge to 0, and changes in the objective value are small enough.
The obtained solution is at least a feasible point, but it is not guaranteed to be globally optimum.
The status Not converged
indicates that the algorithm has not converged, and specifically, if the slack variables (printed in the log) are not close to 0, then it usually indicates that some nonconvex constraint has not been satisfied.
is_dccp(problem)
returns a boolean indicating if an optimization problem satisfies DCCP rules.linearize(expression)
returns the linearization of a DCP expression at the point specified byvariable.value
.convexify_obj(objective)
returns the convexified objective of a DCCP objective.convexify_constr(constraint)
returns the convexified constraint (without slack variables) of a DCCP constraint, and if any expression is linearized, its domain is also returned.