安装了CVX,就可以通过在Matlab脚本或函数中输入CVX代码或直接从命令提示符开始使用它。 为了从Matlab代码去别出CVX代码,它以cvx_begin开始,以cvx_end语句结尾。 CVX代码可以包含一般的Matlab语句和用于声明原始、对偶优化变量以及特定约束、目标函数的CVX命令。
在CVX规范中,优化变量没有数值,它们是特殊的Matlab对象。这使Matlab能够区分普通命令和CVX目标函数和约束。当CVX读取问题规范时,它会构建优化问题的内部表示。如果遇到违反DCP规则的情况(例如使用无效的构图规则或无效约束),则会产生错误信息。当Matlab执行到cvx_end命令时,就完成了CVX规范到Matlab正常形式的转换,并调用底层核心解析器来解决它。
如果优化问题成功解决,则将CVX规范中声明的优化变量从对象转换为可用于Matlab计算的普通数值。 另外,CVX还给一些其他相关的Matlab变量赋值。 例如,可以给出了问题的状态(即是否找到了最佳解决方案,或问题是否被确定为不可行或无界)。 或者可以给出问题的最优值。 对偶变量也可以赋值。
介绍一些简单的例子后,这个流程会变得更加清晰。 我们邀请读者在Matlab中运行CVX发行版examples子目录中的quickstart脚本。 例如,如果你在Windows上的E:\Matlab\Install\bin\e.g\cvx中安装了CVX发行版,那么你应该键入:
cd E:\Matlab\Install\bin\e.g\cvx\examples
quickstart
在Matlab命令提示符下,该脚本将自动打印其代码的关键摘要,并定期暂停,以便您可以检查其输出。 (按“Enter”或“Return”可以恢复进度。)
Least squares 最小二乘
首先我们考虑最基本的凸优化问题-最小二乘(也叫线性回归)。在最小二乘问题中,我们寻找使得
最小。其中
是满秩的(例如m>=n,并且Rank(A)=n)。为Matlab中的一个小测试问题创建数据:
m = 16; n = 8;
A = randn(m,n);
b = randn(m,1);
最小二乘的解用反斜杠算子 \ 很容易计算出来:
x_ls = A \ b;
使用CVX,同样的问题解决如下:
cvx_begin
variable x(n)
minimize( norm(A*x-b) )
cvx_end
(缩进纯粹是为了容易阅读)下面我们逐行查看这段CVX代码:
- cvx_begin为新的CVX规范创建一个占位符,并准备让Matlab接受变量声明,约束,目标函数等等。
- variable x(n)声明x是n维的优化变量。CVX要求所有问题变量在用于目标函数或约束之前都要声明。
- minimize(norm(A*x-b))指定要最小化的目标函数。
- cvx_end表示CVX规范的结束,并且问题得到解决。
显然没有理由使用CVX来解决简单的最小二乘问题。 但是这个例子在CVX中像“Hello world!”程序一样; 即最简单的代码确实有用。
当Matlab运行到cvx_end命令时,最小二乘问题得到解决,并且Matlab变量x被最小二乘问题的解所替代(overwritten),即:。现在x是一个长度为n的数值向量,与传统方法中得到的数值向量相同。另外还创建了几个Matlab变量:
- cvx_optval是目标函数的值。
- cvx_status是描述计算状态的字符串(可以参见Interpreting the results)。
这些所有的量x,cvx_optval,cvx_status等等,现在都可以在其他Matlab语句像其他数字或字符串值一样自由使用。
Bound-constrained least squares约束最小二乘
假设我们希望上述最小二乘问题增加一些简单的上下限,即:
其中 l和u 是所给的和x相同维数的数据向量。向量不等式是各分量的比较,即对所有i来说ui≤vi.
我们不能再使用简单的反斜杠符号来解决这个问题,但它可以被转换成二次规划(QP)来求解。
下面我们为l和u赋值:
bnds = randn(n,2);
l = min( bnds, [], 2 );
u = max( bnds, [], 2 );
如有你有Matlab Optimization Toolbox,你可以使用quadprog来解决问题:
x_qp = quadprog( 2*A'*A, -2*A'*b, [], [], [], [], l, u );
这实际上最小化了范数的平方,这与最小化范数本身相同。 相比之下,可用下面的CVX规范实现:
cvx_begin
variable x(n)
minimize( norm(A*x-b) )
subject to
l <= x <= u
cvx_end
CVX规范中添加了两行新CVX代码:
- subject to 语句不做任何事—CVX提供此声明只是为了使得规范更具可读性。与缩进一样,它是可选的。
- l<=x<=u表示2n个不等式约束。
如前所示,当执行到cvx_end命令时,问题就解决了,数值解赋值给变量x。 顺便说一下,CVX不会通过把目标平方化来将此问题转化为QP; 相反,它会将其转化为SOCP。 结果是一样的,转换是自动完成的。
在这个例子中,和前面的例子一样,CVX规范比对应的Matlab语句更长。另一方面,很容易读懂CVX规范并且把它和原问题联系起来。
Other norms and functions
然而,由于这些范数是CVX基本库函数中的一部分,所以CVX可以直接解决这些问题。
例如。为了找到最小化切比雪夫范数 中x的值,我们可以利用Matlab优化工具箱中的linprog命令:
f = [ zeros(n,1); 1 ];
Ane = [ +A, -ones(m,1) ; ...
-A, -ones(m,1) ];
bne = [ +b; -b ];
xt = linprog(f,Ane,bne);
x_cheb = xt(1:n,:);
利用CVX,同样的问题可用下述代码完成:
cvx_begin
variable x(n)
minimize( norm(A*x-b,Inf) )
cvx_end
基于linprog的代码和CVX代码都能解决上述切比雪夫范数最小化问题。
Other constraints
norm(x,Inf) == 1;
将导致如下错误:
??? Error using ==> cvx.eq
Disciplined convex programming error:
Both sides of an equality constraint must be affine.
只有当f是凸函数,g是凹函数的时候,f(x)<=g(x)或者g(x)>=f(x)形式的不等式约束才能用CVX。所以如下约束:
norm(x,Inf) >= 1;
返回下述错误信息:
??? Error using ==> cvx.ge
Disciplined convex programming error:
The left-hand side of a ">=" inequality must be concave.
在DCP规则集中更详细地讨论了CVX的细节。如果你知道凸分析和凸优化的基础知识,这些规则是相对直观的。
An optimal trade-off curve 最优折衷曲线
gamma = logspace( -2, 2, 20 );
l2norm = zeros(size(gamma));
l1norm = zeros(size(gamma));
fprintf( 1, ' gamma norm(x,1) norm(A*x-b)\n' );
fprintf( 1, '---------------------------------------\n' );
for k = 1:length(gamma),
fprintf( 1, '%8.4e', gamma(k) );
cvx_begin
variable x(n);
minimize( norm(A*x-b)+gamma(k)*norm(x,1) );
cvx_end
l1norm(k) = norm(x,1);
l2norm(k) = norm(A*x-b);
fprintf( 1, ' %8.4e %8.4e\n', l1norm(k), l2norm(k) );
end
plot( l1norm, l2norm );
xlabel( 'norm(x,1)' );
ylabel( 'norm(A*x-b)' );
grid on
上面的minimize语句是 The DCP ruleset中要讨论的构造规则之一。凸分析的一个基本原则是:凸函数可以乘以非负标量,或者加上另一个凸函数,结果也是凸的。CVX识别这样的组合,并允许在任何地方使用简单的凸函数——例如最小化的目标函数,或者在不等式约束的适当的一侧。所以在例子中,下述表达:
norm(A*x-b)+gamma(k)*norm(x,1)
只要gamma(k)是非负值,CVX都认为上述表达是凸的。如果gamma(k)是负的,那么这个表达式就变成了凸函数和凹函数的和,这会引起CVX产生下述错误信息:
??? Error using ==> cvx.plus
Disciplined convex programming error:
Addition of convex and concave terms is forbidden.
- 如果在命令提示符中键入who或whos,你也可能看到其他不熟悉的变量。任何以前缀cvx_开头的变量都被CVX保留为内部使用,并且不应该被改变。
- 也有一些专门设计用来解决边界约束最小二乘问题的解决方案,比如 BCLS by Michael Friedlander。
- 实际上,在上述的cvx_end命令之后,您可能会发现返回的大部分值都是0。这是因为,像许多数值算法一样,解决方案只能在一些非零的数值范围内确定。所以等式约束条件会得到很好的满足,但通常不完全满足。