几何规划的规范化
规范化几何规划(DGP)是对于*对数对数凸*函数(即,相对于几何平均值而不是算术平均值凸性的正变量函数)的DPC的模拟。
虽然DPC是一种用于构造凸程序的规则集,DGP是一种用于对数对数凸程序(LLCP)的规则集,即在将变量、目标函数和约束函数替换为它们的对数之后仍然是凸的问题,我们将这个操作称为*对数对数*变换。每个几何规划(GP)和广义几何规划(GGP)都是LLCP,但是有些LLCP既不是GP也不是GGP。
CVXPY允许您形成和解决DGP问题,就像对于DCP问题一样。例如,以下代码解决了一个简单的几何规划问题,
import cvxpy as cp
# DGP要求通过`pos=True`声明变量为正。
x = cp.Variable(pos=True)
y = cp.Variable(pos=True)
z = cp.Variable(pos=True)
objective_fn = x * y * z
constraints = [
4 * x * y * z + 2 * x * z <= 10, x <= 2*y, y <= 2*x, z >= 1]
problem = cp.Problem(cp.Maximize(objective_fn), constraints)
problem.solve(gp=True)
print("Optimal value: ", problem.value)
print("x: ", x.value)
print("y: ", y.value)
print("z: ", z.value)
并打印以下结果。
Optimal value: 1.9999999938309496
x: 0.9999999989682057
y: 1.999999974180587
z: 1.0000000108569758
注意,要解决DGP问题,必须向 solve()
方法传递选项 gp=True
。
本节解释了什么是DGP,并介绍了如何使用CVXPY构造和解决DGP问题。本节最后列出了可用于 DGP 问题的所有原子,与 DCP atoms 一节中的表格类似。
关于DGP的深入参考,请参见我们的 配套论文 。关于交互式代码示例,请查看我们的 notebooks 。
注意:DGP是CVXPY最近增加的内容。如果您有反馈意见,请在 Github 上提出问题或拉动请求。
对数曲率
正如CVXPY中的每个Expression都有一个曲率(常数、仿形、凸、凹或未知),每个Expression也有一个对数曲率。
一个函数 \(f : D \subseteq \mathbf{R}^n_{++} \to \mathbf{R}\) 被称为对数凸,如果函数 \(F(u) = \log f(e^u)\) ,其域为 \(\{u \in \mathbf{R}^n : e^u \in D\}\) 是凸的(其中 \(\mathbf{R}^n_{++}\) 表示正实数的集合,对数和指数是指元素性的); 该函数 \(F\) 被称为 f 的对数-对数变换。 \(f\) 是对数凹的,如果 \(F\) 是凹的,如果 \(F\) 是仿形的,则是对数仿形的。
每个对数仿生函数的形式是
其中 \(x\) 在 \(\mathbf{R}^n_{++}\) 中, \(a_i\) 是实数, \(c\) 是正标量。在几何规划的上下文中,这样的函数被称为单项式函数。一组单项式的和在几何规划中被称为一个幂函数,它是 log-log 凸函数。所有具有已知 log-log 曲率的 原子 的表格在本页面末尾显示。
在下面的表格中,\(F\) 是 \(f\) 的 log-log 转换,\(u=\log x\),\(v=\log y\),其中 \(x\) 和 \(y\) 在 \(f\) 的定义域中。
Log-Log 曲率 |
含义 |
---|---|
log-log 常数 |
\(F\) 是一个常数(所以 f 是一个正常数) |
log-log 仿射 |
\(F(\theta u + (1-\theta)v) = \theta F(u) + (1-\theta)F(v), \; \forall u, \; v,\; \theta \in [0,1]\) |
log-log 凸 |
\(F(\theta u + (1-\theta)v) \leq \theta F(u) + (1-\theta)F(v), \; \forall u, \; v,\; \theta \in [0,1]\) |
log-log 凹 |
\(F(\theta u + (1-\theta)v) \geq \theta F(u) + (1-\theta)F(v), \; \forall u, \; v,\; \theta \in [0,1]\) |
未知 |
DGP 分析无法确定曲率 |
CVXPY 的 log-log 曲率分析可以标记表达式为未知,即使它们是 log-log 凸函数或 log-log 凹函数。请注意,任何 log-log 常量表达式也是 log-log 仿射的,任何 log-log 仿射表达式也是 log-log 凸的和 log-log 凹的。
表达式的 log-log 曲率存储在其 .log_log_curvature
属性中。例如,运行以下脚本:
import cvxpy as cp
x = cp.Variable(pos=True)
y = cp.Variable(pos=True)
constant = cp.Constant(2.0)
monomial = constant * x * y
posynomial = monomial + (x ** 1.5) * (y ** -1)
reciprocal = posynomial ** -1
unknown = reciprocal + posynomial
print(constant.log_log_curvature)
print(monomial.log_log_curvature)
print(posynomial.log_log_curvature)
print(reciprocal.log_log_curvature)
print(unknown.log_log_curvature)
打印以下输出。
LOG-LOG CONSTANT
LOG-LOG AFFINE
LOG-LOG CONVEX
LOG-LOG CONCAVE
UNKNOWN
您还可以通过调用以下方法来检查表达式的对数-对数曲率
is_log_log_constant()
,is_log_log_affine()
,
is_log_log_convex()
,is_log_log_concave()
。例如,
posynomial.is_log_log_convex()
将返回 True
。
对数-对数曲率规则
要使一个表达式具有已知的对数-对数曲率,它引用的所有常量、变量和参数都必须是逐元素正数。如果
常量的数值为正,则它是正数。变量和参数仅在构造函数中提供关键字参数
pos=True
(例如, x = cvxpy.Variable(shape=(), pos=True)
)时为正数。总之,
在制定DGP问题时,所有常量都应是逐元素正数,所有变量和参数必须使用属性
pos=True
进行构造。
DGP分析与DCP分析完全类似。它是基于具有已知单调性和对数-对数曲率的原子(函数) 库和单个组合规则。 原子库 在本页面末尾展示; 组合规则如下所述。
函数 \(f(expr_1, expr_2, ..., expr_n)\) 在以下条件之一成立时是对数-对数凸的:
\(f\) 在参数 \(i\) 上增加,且 \(expr_{i}\) 是对数-对数凸的。
\(f\) 在参数 \(i\) 上减少,且 \(expr_{i}\) 是对数-对数凹的。
\(expr_{i}\) 是对数-对数仿射的。
函数 \(f(expr_1, expr_2, ..., expr_n)\) 在以下条件之一成立时是对数-对数凹的:
\(f\) 在参数 \(i\) 上是递增的,且 \(expr_{i}\) 是对数-对数凹函数。
\(f\) 在参数 \(i\) 上是递减的,且 \(expr_{i}\) 是对数-对数凸函数。
\(expr_{i}\) 是对数-对数仿射函数。
函数 \(f(expr_1, expr_2, ..., expr_n)\) 是对数-对数仿射函数当且仅当 \(\text{ } f\) 是对数-对数仿射函数,且每个 \(expr_{i}\) 都是对数-对数仿射函数。
如果这三条规则都不适用,表达式 \(f(expr_1, expr_2, ..., expr_n)\) 会被标记为曲率未知。
如果一个表达式满足合成规则,我们俗称该表达式 “是DGP”。您可以通过调用方法 is_dgp()
来检查一个表达式是否是DGP。例如,下面代码块中的断言将通过。
import cvxpy as cp
x = cp.Variable(pos=True)
y = cp.Variable(pos=True)
monomial = 2.0 * constant * x * y
posynomial = monomial + (x ** 1.5) * (y ** -1)
assert monomial.is_dgp()
assert posynomial.is_dgp()
一个表达式是DGP,当且仅当它具有已知的对数-对数曲率,这意味着至少有一个方法 is_log_log_constant()
、
is_log_log_affine()
、 is_log_log_convex()
、is_log_log_concave()
将返回 True
。
DGP 问题
Problem
是由目标和约束列表构建的。如果问题遵循 DGP 规则,
它将保证是一个 LLCP 问题,并且可以通过 CVXPY 求解。DGP 规则要求问题目标有以下两种形式之一:
最小化 (对数对数凸)
最大化 (对数对数凹)
根据 DGP 规则,唯一有效的约束条件是:
对数对数仿射 == 对数对数仿射
对数对数凸 <= 对数对数凹
对数对数凹 >= 对数对数凸
您可以通过调用 object.is_dgp()
检查问题、约束或目标是否满足 DGP 规则。
以下是一些 DGP 和非 DGP 问题的示例:
import cvxpy as cp
#DGP 要求变量通过 `pos=True` 声明为正数。
x = cp.Variable(pos=True)
y = cp.Variable(pos=True)
z = cp.Variable(pos=True)
objective_fn = x * y * z
constraints = [
4 * x * y * z + 2 * x * z <= 10, x <= 2*y, y <= 2*x, z >= 1]
assert objective_fn.is_log_log_concave()
assert all(constraint.is_dgp() for constraint in constraints)
problem = cp.Problem(cp.Maximize(objective_fn), constraints)
assert problem.is_dgp()
# 所有变量必须声明为正数,才能使一个表达式成为DGP。
w = cp.Variable()
objective_fn = w * x * y
assert not objective_fn.is_dgp()
problem = cp.Problem(cp.Maximize(objective_fn), constraints)
assert not problem.is_dgp()
如果在一个非DGP问题上调用 problem.solve(gp=True)
,CVXPY将引发异常。
DGP原子
本教程的这一部分介绍了DGP原子库,即已知具有对数-对数弯曲率和单调性的原子函数。 CVXPY使用本节中的函数信息和DGP规则来标记具有对数-对数弯曲的表达式。请注意,每个DGP表达式都是正的。
中缀运算符
中缀运算符 +、*、/
被视为原子。运算符 *
和 /
是对数-对数仿射函数。运算符 +
在其两个参数中是对数-对数凸的。
请注意,在CVXPY中,当 expr1
和 expr2
是矩阵时, expr1 * expr2
表示矩阵乘法;如果你使用的是Python 3,你还可以使用 @
运算符进行矩阵乘法。无论你的Python版本如何,你也可以使用 matmul atom 来对两个矩阵进行乘法运算。要对两个数组或矩阵进行逐元素乘法,请使用 multiply atom 。最后,要求一个Expression的所有元素的乘积,请使用 prod atom 。
转置
可以使用语法 expr.T
来获得任何表达式的转置。转置是一个对数-对数仿射函数。
幂函数
对于任何CVXPY表达式 expr
,幂运算符 expr**p
等同于函数 power(expr, p)
。取幂是一个对数-对数仿射函数。
标量函数
标量函数接受一个或多个标量、向量或矩阵作为参数,并返回一个标量。需要注意的是,这些原子可以沿着一个轴应用;有关详细信息,请参阅API参考或 DCP atoms tutorial 。
Function |
Meaning |
Domain |
Log-log curvature |
Monotonicity |
---|---|---|---|---|
\(p \in \mathbf{R}^n_{+}\) \(p \neq 0\) |
\(x_1^{1/n} \cdots x_n^{1/n}\) \(\left(x_1^{p_1} \cdots x_n^{p_n}\right)^{\frac{1}{\mathbf{1}^T p}}\) |
\(x \in \mathbf{R}^n_{+}\) |
||
\(\frac{n}{\frac{1}{x_1} + \cdots + \frac{1}{x_n}}\) |
\(x \in \mathbf{R}^n_{+}\) |
|||
\(\max_{ij}\left\{ X_{ij}\right\}\) |
\(X \in\mathbf{R}^{m \times n}_{++}\) |
|||
\(\min_{ij}\left\{ X_{ij}\right\}\) |
\(X \in\mathbf{R}^{m \times n}_{++}\) |
|||
norm(x, 2) |
\(\sqrt{\sum_{i} \lvert x_{i} \rvert^2 }\) |
\(X \in\mathbf{R}^{n}_{++}\) |
||
\(\sqrt{\sum_{ij}X_{ij}^2 }\) |
\(X \in\mathbf{R}^{m \times n}_{++}\) |
|||
\(\sum_{ij}\lvert X_{ij} \rvert\) |
\(X \in\mathbf{R}^{m \times n}_{++}\) |
|||
\(\max_{ij} \{\lvert X_{ij} \rvert\}\) |
\(X \in\mathbf{R}^{m \times n}_{++}\) |
|||
\(p \geq 1\) or |
\(\|X\|_p = \left(\sum_{ij} |X_{ij}|^p \right)^{1/p}\) |
\(X \in \mathbf{R}^{m \times n}_{++}\) |
||
\(0 < p < 1\) |
\(\|X\|_p = \left(\sum_{ij} X_{ij}^p \right)^{1/p}\) |
\(X \in \mathbf{R}^{m \times n}_{++}\) |
||
\(\prod_{ij}X_{ij}\) |
\(X \in\mathbf{R}^{m \times n}_{++}\) |
|||
\(x^T P x\) |
\(x \in \mathbf{R}^n\), \(P \in \mathbf{R}^{n \times n}_{++}\) |
|||
\(\left(\sum_{ij}X_{ij}^2\right)/y\) |
\(x \in \mathbf{R}^n_{++}\) \(y > 0\) |
|||
\(\sum_{ij}X_{ij}\) |
\(X \in\mathbf{R}^{m \times n}_{++}\) |
|||
\(\sum_{ij}X_{ij}^2\) |
\(X \in\mathbf{R}^{m \times n}_{++}\) |
|||
\(\mathrm{tr}\left(X \right)\) |
\(X \in\mathbf{R}^{n \times n}_{++}\) |
|||
spectral radius of \(X\) |
\(X \in\mathbf{R}^{n \times n}_{++}\) |
逐元素函数
这些函数作用于它们参数的每个元素。例如,如果 X
是一个 5x4 的矩阵变量,则 sqrt(X)
是一个 5x4 的矩阵表达式。 sqrt(X)[1, 2]
相当于 sqrt(X[1, 2])
。
对于接受多个参数的逐元素函数,如 maximum
和 multiply
,它们在每个参数的对应元素上进行操作。例如,如果 X
和 Y
都是 3x3 的矩阵变量,则 maximum(X, Y)
是一个 3x3 的矩阵表达式。 maximum(X, Y)[2, 0]
相当于 maximum(X[2, 0], Y[2, 0])
。这意味着所有参数必须具有相同的维度或者是标量,如果是标量,则会被升级。
Function |
Meaning |
Domain |
Curvature |
Monotonicity |
---|---|---|---|---|
diff_pos(x, y) |
\(x - y\) |
\(0 < y < x\) |
||
\(-x \log (x)\) |
\(0 < x < 1\) |
None |
||
\(e^x\) |
\(x > 0\) |
|||
\(\log(x)\) |
\(x > 1\) |
|||
\(\max \left\{x, y\right\}\) |
\(x,y > 0\) |
|||
\(\min \left\{x, y\right\}\) |
\(x, y > 0\) |
|||
\(x*y\) |
\(x, y > 0\) |
|||
\(1 - x\) |
\(0 < x < 1\) |
|||
\(1\) |
\(x > 0\) |
constant |
constant |
|
\(x\) |
\(x > 0\) |
|||
\(\sqrt x\) |
\(x > 0\) |
|||
\(x^2\) |
\(x > 0\) |
|||
\(x e^x\) |
\(x > 0\) |
向量/矩阵函数
向量/矩阵函数接受一个或多个标量、向量或矩阵作为参数,并返回一个向量或矩阵。
Function |
Meaning |
Domain |
Curvature |
Monotonicity |
---|---|---|---|---|
\(\left[\begin{matrix} X^{(1,1)} & \cdots & X^{(1,q)} \\ \vdots & & \vdots \\ X^{(p,1)} & \cdots & X^{(p,q)} \end{matrix}\right]\) |
\(X^{(i,j)} \in\mathbf{R}^{m_i \times n_j}_{++}\) |
|||
\(\left[\begin{matrix}x_1 & & \\& \ddots & \\& & x_n\end{matrix}\right]\) |
\(x \in\mathbf{R}^{n}_{++}\) |
|||
\(\left[\begin{matrix}X_{11} \\\vdots \\X_{nn}\end{matrix}\right]\) |
\(X \in\mathbf{R}^{n \times n}_{++}\) |
|||
eye_minus_inv(X) |
\((I - X)^{-1}\) |
\(X \in\mathbf{R}^{n \times n}_{++}, \lambda_{\text{pf}}(X) < 1\) |
||
\(A \in \mathbf{R}^{m \times n}\) |
\(\left[\begin{matrix}\prod_{j=1}^n x_j^{A_{1j}} \\\vdots \\\prod_{j=1}^n x_j^{A_{mj}}\end{matrix}\right]\) |
\(x \in \mathbf{R}^n_{++}\) |
||
\(\left[\begin{matrix}X^{(1)} \cdots X^{(k)}\end{matrix}\right]\) |
\(X^{(i)} \in\mathbf{R}^{m \times n_i}_{++}\) |
|||
\(XY\) |
\(X \in\mathbf{R}^{m \times n}_{++}, Y \in\mathbf{R}^{n \times p}_{++}`\) |
|||
\((sI - X)^{-1}\) |
\(X \in\mathbf{R}^{n \times n}_{++}, \lambda_{\text{pf}}(X) < s\) |
|||
\(X' \in\mathbf{R}^{m' \times n'}\) |
\(X \in\mathbf{R}^{m \times n}_{++}\) \(m'n' = mn\) |
|||
\(x' \in\mathbf{R}^{mn}\) |
\(X \in\mathbf{R}^{m \times n}_{++}\) |
|||
\(\left[\begin{matrix}X^{(1)} \\ \vdots \\X^{(k)}\end{matrix}\right]\) |
\(X^{(i)} \in\mathbf{R}^{m_i \times n}_{++}\) |