高级约束

属性

变量和参数可以通过指定附加属性的方式创建。 例如,Variable(nonneg=True) 是一个被约束为非负的标量变量。 同样地,Parameter(nonpos=True) 是一个被约束为非正的标量参数。 LeafVariableParameter 的父类)的完整构造函数如下所示。

Leaf(shape=None, value=None, nonneg=False, nonpos=False, complex=False, imag=False, symmetric=False, diag=False, PSD=False, NSD=False, hermitian=False, boolean=False, integer=False, sparsity=None, pos=False, neg=False)

创建一个Leaf对象(例如,Variable或Parameter)。 只有一个属性可以是活动的(设置为True)。

Parameters:
shape : tuple or int

变量的维度,默认为标量(0D)。

value : numeric type

要分配给变量的值。

nonneg : bool

变量是否被限制为非负?

nonpos : bool

变量是否被限制为非正数?

complex : bool

变量是否被约束为复数值?

imag : bool

变量是否被限制为虚数?

symmetric : bool

变量是否被约束为对称的?

diag : bool

变量是否被限制为对角线的?

PSD : bool

变量是否被约束为对称半正定?

NSD : bool

变量是否被约束为对称负半定?

hermitian : bool

变量是否被约束为厄米特矩阵?

boolean : bool or list of tuple

变量是否为布尔类型(即0或1)?True,将整个变量约束为布尔类型,False,或者是一个索引列表,这些索引应被约束为布尔类型,其中每个索引是一个长度恰好等于形状长度的元组。

integer : bool or list of tuple

变量是整数吗?语义与布尔参数相同。

sparsity : list of tuplewith

固定变量的稀疏模式。

pos : bool

变量是否被限制为正数?

neg : bool

变量是否被限制为负数?

bounds : iterable of length two

变量是否有下界和/或上界?

变量和参数的value字段可以在构造后赋值,但所赋的值必须满足对象的属性。通过project方法,可以给出由属性定义的集合上的欧几里得投影。

p = Parameter(nonneg=True)
try:
    p.value = -1
except Exception as e:
    print(e)

print("Projection:", p.project(-1))
Parameter value must be nonnegative.
Projection: 0.0

为叶子节点赋值的一个明智的惯用方法是 leaf.value = leaf.project(val), 确保所赋的值满足叶子节点的属性。 一个稍微更高效的变体是 leaf.project_and_assign(val), 它直接投影并赋值,而不额外检查该值是否满足叶子节点的属性。在大多数情况下,project 和检查一个值是否满足叶子节点的属性是廉价的操作(即, \(O(n)\)),但对于对称半正定或半负定的叶子节点,这些操作会计算一个特征值分解。

许多属性,如非负性和对称性,可以通过约束轻松指定。 那么在变量中指定属性的优势是什么? 主要好处是,指定属性可以实现更细粒度的DCP分析。 例如,通过x = Variable(nonpos=True)创建变量x会告知DCP分析器x是非正的。 通过x = Variable()创建变量x并单独添加约束x >= 0不会向DCP分析器提供任何关于x符号的信息。

重要

使用属性而非显式约束的一个缺点是双变量不会被记录。双变量值仅针对显式约束进行记录。

稀疏属性

在版本1.6中新增。

在某些优化问题中,为变量定义稀疏属性是有益的。此属性定义了您希望优化的变量子集。在下面的示例中,问题是在上三角矩阵集上进行优化。

# Creates a upper triangular sparse variable
X = cp.Variable((10, 10), sparsity=np.triu_indices(n=10))

prob = cp.Minimize(cp.norm(X) + cp.sum(X))

稀疏属性避免了定义不必要的变量,并且可以在内存和计算方面带来显著的性能提升,同时保持表达式的期望形状。另一种定义稀疏属性的方法是使用np.where,并在给定的问题数据上设置条件。在下面的示例中,稀疏变量表示data中所有大于0.5的条目。

# define problem data (adapt to your use-case)
data = np.random.randn(10, 10)
# Creates a sparse variable given condition on data
X = cp.Variable((10, 10), sparsity=np.where(data > 0.5))

prob = cp.Minimize(cp.norm(X) + cp.sum(X))

最后,您也可以手动定义稀疏属性。属性的输入需要符合在np.indices中定义的索引格式。

# Creates a sparse variable manually
# The first tuple represent row indices and the second column indices
# This is equivalent to calling np.where(data == 1) on the following matrix
# [[1, 0, 0],
#  [0, 0, 1],
#  [0, 0, 0]]
X = cp.Variable((3, 3), sparsity=[(0, 1), (0, 2)])

prob = cp.Minimize(cp.norm(X) + cp.sum(X))

警告

稀疏属性目前还不支持用于设置变量或参数的值。 在未来的版本中,我们计划使用基于PyTorch的sparse_coo格式的自定义数据类型来实现稀疏属性的投影方法。

半定矩阵

许多凸优化问题涉及将矩阵约束为半正定或半负定(例如,SDPs)。 你可以在CVXPY中以两种方式实现这一点。 第一种方式是使用 Variable((n, n), PSD=True) 来创建一个 nn 的变量,该变量被约束为对称且半正定。例如,

# Creates a 100 by 100 positive semidefinite variable.
X = cp.Variable((100, 100), PSD=True)

# You can use X anywhere you would use
# a normal CVXPY variable.
obj = cp.Minimize(cp.norm(X) + cp.sum(X))

第二种方法是使用>><<运算符创建半正定锥约束。 如果XYnn的变量, 约束X >> Y意味着对于所有\(z \in \mathcal{R}^n\)\(z^T(X - Y)z \geq 0\)。 换句话说,\((X - Y) + (X - Y)^T\)是半正定的。 该约束不要求XY是对称的。 半正定锥约束的两边必须是方阵且是仿射的。

以下代码展示了如何将矩阵表达式约束为正定或负定(但不一定对称)。

# expr1 must be positive semidefinite.
constr1 = (expr1 >> 0)

# expr2 must be negative semidefinite.
constr2 = (expr2 << 0)

要约束矩阵表达式为对称的,只需编写

# expr must be symmetric.
constr = (expr == expr.T)

你也可以使用 Variable((n, n), symmetric=True) 来创建一个 nn 的变量,该变量被约束为对称的。 通过属性指定变量为对称与添加约束 X == X.T 的区别在于, 属性会被解析为DCP信息,并且对称变量是在(较低维度的)对称矩阵的向量空间上定义的。

混合整数规划

在混合整数规划中,某些变量被限制为布尔值(即0或1)或整数值。 您可以通过创建具有仅布尔值或整数值条目的属性来构建混合整数规划:

# Creates a 10-vector constrained to have boolean valued entries.
x = cp.Variable(10, boolean=True)

# expr1 must be boolean valued.
constr1 = (expr1 == x)

# Creates a 5 by 7 matrix constrained to have integer valued entries.
Z = cp.Variable((5, 7), integer=True)

# expr2 must be integer valued.
constr2 = (expr2 == Z)

CVXPY 提供了许多混合整数求解器的接口,包括开源和商业求解器。 由于许可原因,CVXPY 默认不安装任何首选求解器。

CVXPY中首选的开放源代码混合整数求解器是HiGHS、GLPK_MI、CBC和SCIP。CVXOPT Python包为CVXPY提供了访问GLPK_MI的途径;CVXOPT可以通过在命令行或终端中运行pip install cvxopt来安装。SCIP支持非线性模型,但GLPK_MI和CBC不支持。

如果您需要快速解决一个大型混合整数问题,或者如果您有一个对SCIP或HiGHS具有挑战性的非线性混合整数模型,那么您将需要使用商业求解器,如CPLEX、GUROBI、XPRESS、MOSEK或COPT。商业求解器需要许可证才能运行。CPLEX、GUROBI和MOSEK为学术界(包括学生和教职员工)提供免费许可证,并为非学术界人士提供试用版本。

CPLEX 免费版无论学术状态如何均可免费使用,但仍需在线注册,且仅限于最多1000个变量和1000个约束的问题。 XPRESS 有一个免费的社区版,不需要注册,但仅限于变量数量和约束数量之和不超过5000的问题。 COPT 也有一个免费的社区版,仅限于最多2000个变量和2000个约束的问题。

注意

如果您开发了一个使用宽松许可证(如Apache 2.0)的开源混合整数求解器,并且有兴趣将您的求解器集成到CVXPY的默认安装中,请通过我们的GitHub issues联系我们。我们特别感兴趣的是集成一个简单的混合整数SOCP求解器。

复数值表达式

默认情况下,变量和参数是实数值的。可以通过设置属性complex=True来创建复数值的变量和参数。同样,可以通过设置属性imag=True来创建纯虚数的变量和参数。包含复变量、参数或常数的表达式可能是复数值的。函数is_realis_complexis_imag分别返回一个表达式是否为纯实数、复数或纯虚数。

# A complex valued variable.
x = cp.Variable(complex=True)
# A purely imaginary parameter.
p = cp.Parameter(imag=True)

print("p.is_imag() = ", p.is_imag())
print("(x + 2).is_real() = ", (x + 2).is_real())
p.is_imag() = True
(x + 2).is_real() = False

问题目标中的顶层表达式必须是实值的,但子表达式可以是复数。算术和所有线性原子都定义为复数表达式。非线性原子abs和除了norm(X, p)(对于p < 1)之外的所有范数也定义为复数表达式。所有定义域为对称矩阵的原子都定义为埃尔米特矩阵。同样,原子quad_form(x, P)matrix_frac(x, P)定义为复数x和埃尔米特P。所有约束都定义为复数表达式。

以下额外的原子用于处理复杂表达式:

  • real(expr) 给出 expr 的实部。

  • imag(expr) 给出 expr 的虚部(即 expr = real(expr) + 1j*imag(expr))。

  • conj(expr) 给出 expr 的复共轭。

  • expr.H 给出 expr 的共轭转置。