CVXOPT
CVXOPT 是一个用于凸优化问题的 Python 库。它提供了一系列用于求解凸优化问题的函数,包括线性规划、二次规划、二次约束等。CVXOPT 的 API 设计得非常直观,使得用户可以方便地定义和求解各种凸优化问题。
二次规划 (QP)
二次规划 (Quadratic Programming, QP) 是一种常见的凸优化问题,其目标函数为二次函数,约束条件为线性不等式。在 CVXOPT 中提供了 QP solver 来求解 QP 问题。
在金融领域,二次规划常用于资产组合优化。
下面我们来拆解一下这个词:
Q - Quadratic (二次的)
“二次”指的是问题的核心目标像一个”碗”或者”山谷”。它的数学表达式里包含变量的平方,比如 x2。
为什么”碗”这个形状很重要?因为它只有一个最低点。求解器的任务就是精准地找到这个碗底。如果是个平面,那就没有最低点了;如果坑坑洼洼,就会有很多个局部最低点,难以抉择。二次问题(QP)保证了我们能找到那个唯一的、全局的最低点。
P - Programming (规划)
这里的”规划”不是指我们平时说的写代码(Programming)。它是一个历史悠久的数学术语,意思是”做计划”或者”寻找最优方案”。
所以,“二次规划”指的就是为二次型问题寻找最优解决方案的计划。
Solver (求解器)
- 这个最好理解,它就是执行这个计划的工具或引擎。你把问题描述给它,它内部有一套非常高效的算法,能自动、快速地帮你找到答案。
总结: 一个 QP 求解器 (QP Solver),就是一个专门用来寻找”碗状”问题最低点的自动化数学工具。你需要做的就是告诉它你的”碗”长什么样(目标),以及你必须遵守的”规则边界”(约束),然后它就能帮你找到在这些边界内的最低点在哪里。
QP solver流程示例
问题描述
求解以下 QP 问题。
目标函数 (Minimize): 2x12 + x22 + x1x2 + x1 + x2
约束条件 (Subject to): - x1 ≥ 0 - x2 ≥ 0 - x1 + x2 = 1
“碗”就是目标函数: 2x12 + x22 + x1x2 + x1 + x2
“规则边界”就是约束条件: x1 ≥ 0, x2 ≥ 0, 和 x1 + x2 = 1
CVXOPT 里的 solvers.qp
就是那个能解决这个问题的求解器。
我们把使用这个库想象成 “给一个很厉害但一板一眼的厨师下订单” 的过程。这位厨师(CVXOPT)厨艺高超,但你必须用他唯一能看懂的、格式固定的订单(矩阵)来告诉他你要什么。
整个过程分为三步:
第 1 步:用大白话描述你的”目标”和”规则”
这一步是给自己看的,先理清思路。
我的目标是什么?(Objective)
我想要一个”菜”(由原料 x1 和 x2 组成),它的”成本”由公式 2x12 + x22 + x1x2 + x1 + x2 决定。我的目标是让这个成本最低。
我有什么规则?(Constraints)
- 规则1:原料 x1 的用量不能是负数 (x1 ≥ 0)。
- 规则2:原料 x2 的用量也不能是负数 (x2 ≥ 0)。
- 规则3:两种原料的总用量必须正好是 1 (x1 + x2 = 1)。
第 2 步:把”目标”和”规则”翻译成”厨师的订单格式”(矩阵)
这是最关键的一步。这位厨师(CVXOPT)不认识我们的大白话,他只认识一张叫 P, q, G, h, A, b 的标准订单。我们必须把信息填进去。
填写 P 和 q (描述你的”目标”)
厨师通过 P 和 q 来理解你想要的那个”成本函数”(那个”碗”)。
- P 矩阵描述成本函数里二次项(带平方的项)的部分,它决定了”碗”的形状。
- q 矩阵描述成本函数里一次项(不带平方的项)的部分,它决定了”碗”的位置。
怎么填: 根据数学规则(后面介绍),我们计算出
填写 G 和 h (描述你的”不严格”规则)
厨师通过 G 和 h 来理解所有”小于等于”或”大于等于”的规则。
我们的规则是 x1 ≥ 0 和 x2 ≥ 0。订单格式要求写成”小于等于”,所以我们改写成 −x1 ≤ 0 和 −x2 ≤ 0。
怎么填: 我们把这个信息填入订单,得到
填写 A 和 b (描述你的”死规定”)
厨师通过 A 和 b 来理解所有”必须正好等于”的死规定。
我们的死规定是 x1 + x2 = 1。
怎么填: 我们把这个信息填入订单,得到
现在,我们的订单填好了!我们已经成功把我们的想法,翻译成了计算机能懂的语言。
第 3 步:用 Python 代码把”订单”交给”厨师”
这一步就是写代码,把我们刚刚整理好的矩阵告诉 CVXOPT。
1 | from cvxopt import matrix, solvers |
数学规则
目标函数
要理解这些矩阵的含义,首先需要了解 cvxopt.solvers.qp
所求解的二次规划问题的标准数学形式。其目标是找到一个向量 x,使得:
- 最小化 (Minimize):
- 约束条件 (Subject to):
其中 ≼ 表示逐元素小于等于。
下面我们来逐一解释每个矩阵和向量的含义。
矩阵和向量的含义
P 和 q:定义目标函数
这两个参数共同构成了你需要最小化的目标函数,它是一个二次函数。
- P (Matrix, 矩阵):
- 这是一个 n × n 的半正定矩阵(positive semi-definite),其中 n 是你要求解的变量 x 的维度。
- 矩阵 P 描述了目标函数中所有变量的二次关系。在数学上,P 是目标函数 f(x) 的海森矩阵 (Hessian Matrix),即由目标函数的二阶偏导数组成的方阵,描述了函数的局部曲率。
- 格式:在 cvxopt 中,P
必须是
cvxopt.matrix类型。 - 计算方法:
- 写出目标函数 f(x) = 2x12 + x22 + x1x2 + x1 + x2。
- 计算 f(x)
对所有变量的一阶偏导(梯度 ∇f(x)):
- 计算二阶偏导,构成海森矩阵 H:
- P (Matrix, 矩阵):
为什么有
数学上,在目标函数中加入
- q (Vector, 向量):
- 这是一个 n × 1 的向量。
- 向量 q 描述了目标函数中所有变量的线性关系。它是在剥离了二次项和常数项后,与 x 线性组合的系数向量。它影响了目标函数曲面的位置。
- 格式:q 也必须是
cvxopt.matrix类型。 - 计算方法:
- 审视目标函数 f(x) = 2x12 + x22 + x1x2 + x1 + x2。
- 找出所有只与变量一次幂相乘的项:x1 + x2。
- 将这部分写成向量内积的形式 qTx:
- 对比系数可得 q1 = 1, q2 = 1。
- 结果:
G 和 h:定义不等式约束
这两个参数共同定义了问题中的不等式约束。标准形式 Gx ≼ h 要求所有不等式都是”小于等于”的形式,并且每一行代表一个独立的约束。≼ 符号代表逐元素比较。
- G (Matrix, 矩阵):
- 这是一个 m × n 的矩阵,其中 m 是不等式约束的数量。
- G 的每一行定义了一个线性不等式。例如,G 的第一行和 h 的第一个元素共同定义了第一个约束: G1, 1x1 + G1, 2x2 + ⋯ + G1, nxn ≤ h1
- 格式:
cvxopt.matrix类型。
- h (Vector, 向量):
- 这是一个 m × 1 的向量。
- 作用:h 包含了 m 个不等式约束的右侧边界值。
- 格式:
cvxopt.matrix类型。
- 计算方法:
- 逐一标准化:将每一个不等式约束都转换为 [...] ≤ [...] 的形式。
- 约束1:x1 ≥ 0 ⟹ −x1 ≤ 0
- 约束2:x2 ≥ 0 ⟹ −x2 ≤ 0
- 提取系数行向量(每个约束中x的系数):
- 对于 −x1 ≤ 0,系数行向量是
,右侧常数是 0。 - 对于 −x2 ≤ 0,系数行向量是
,右侧常数是 0。
- 堆叠成矩阵:将所有系数行向量按顺序堆叠,形成矩阵 G,右侧常数堆叠成向量 h。
- 结果:
- G (Matrix, 矩阵):
A 和 b:定义等式约束
这两个参数共同定义了问题中的等式约束(Ax = b)。
- A (Matrix, 矩阵):
- 这是一个 p × n 的矩阵,其中 p 是等式约束的数量。
- 作用:A 的每一行定义了一个线性等式约束。
- 格式:
cvxopt.matrix类型。
- b (Vector, 向量):
- 这是一个 p × 1 的向量。
- 作用:b 包含了 p 个等式约束右侧的目标值。
- 格式:
cvxopt.matrix类型。
- 计算方法 (与G,h类似):
- 审视等式:本例只有一个等式约束 x1 + x2 = 1。
- 提取系数行向量:
,右侧常数为 1。 - 堆叠成矩阵:本例只有一行。
- 结果:
- A (Matrix, 矩阵):
总结:
- P, q:描述了你想最小化的”成本”函数。
- G, h:设定了变量 x 的活动范围(例如 x1 ≥ 0 可以写成 −x1 ≤ 0)。
- A, b:设定了变量 x 必须满足的精确关系(例如 x1 + x2 = 1)。