- 科学计算
【免费下载链接】cvxpy
A Python-embedded modeling language for convex optimization problems.
CVXPY 是一个内嵌于 Python 的凸优化建模语言。本指南以官方 API 参考(cvxpy.rst)为主线,系统梳理 CVXPY 的公开 API 命名空间、五大文档板块(Atoms、Constraints、Expressions、Problems、Transforms、Reductions),并结合仓库源码说明原子函数的实现机制、表达式树的结构、Problem 类的求解流程,以及不承诺向后兼容的归约(Reduction)系统。读完本文,你将能够按图索骥地查阅 CVXPY 的每一层 API,并理解cp.solve()背后从建模到求解的完整调用链。
API 参考的设计初衷:面向"习惯阅读技术文档"的用户
CVXPY 官方 API 参考文档开宗明义地指出:CVXPY 的设计目标是足够直观,以至于大多数用户不查阅 API 参考就能完成建模——官方教程(tutorial)足以让用户上手。API 参考是为"习惯阅读技术文档的人"准备的补充材料,这一点在 cvxpy.rst 中有明确说明。
这一设计哲学也贯穿于仓库的其他文档中:
- 教程与示例位于 doc/source/tutorial 与 doc/source/examples,面向入门读者;
- API 参考位于 doc/source/api_reference,面向进阶读者与贡献者;
- 归约(Reductions)系统甚至明确声明"不属于公开 API",仅供贡献者与好奇心强的用户阅读(详见下文)。
公开 API 命名空间:cvxpy.symbol约定
API 参考中最重要的一条约定是:所有被文档化的类和函数都被导入到cvxpy命名空间。也就是说,只要在 Python 源码中import cvxpy,就可以直接通过cvxpy.symbol使用任意符号,其中symbol是类或函数的名称。例如cvxpy.Problem、cvxpy.Variable、cvxpy.exp、cvxpy.lambda_max都是直接可用的。
这一约定的直接依据是 cvxpy.rst 的原文说明,同时也可以在仓库顶层的 cvxpy/init.py 中找到对应的导入逻辑——所有公开符号都在包初始化时统一汇总。
文档的六大板块划分
API 参考将全部文档分为六个部分(cvxpy.rst):
| 板块 | 文档文件 | 内容 |
|---|---|---|
| Atoms | cvxpy.atoms.rst | 实现原子数学函数的类,如exp、log、sqrt |
| Constraints | cvxpy.constraints.rst | 可施加在变量上的约束 |
| Expressions | cvxpy.expressions.rst | 实现数学表达式树的类,包括Variable与Parameter |
| Problems | cvxpy.problems.rst | Problem类及相关类 |
| Transforms | cvxpy.transforms.rst | 操纵 CVXPY 对象的附加操作 |
| Reductions | cvxpy.reductions.rst | 将问题从一种形式转换为等价形式的原理性操作 |
下文将逐一深入每个板块。
Atoms:构建表达式树的原子函数
什么是 Atom
在 CVXPY 的术语中,atom(小写 a)是可作用于Expression对象并返回Expression对象的数学函数。原子函数及其复合正是构建 CVXPY 数学表达式树的机制(cvxpy.atoms.rst)。
每个 atom 都携带关于其定义域(domain)、符号(sign)、曲率(curvature)、log-log 曲率以及单调性(monotonicity)的标签信息。正是这些信息让 atom 实例能够推理自身是否符合 DCP(Disciplined Convex Programming)或 DGP(Disciplined Geometric Programming)规则。仓库中的实现证据位于 cvxpy/atoms/atom.py:is_atom_convex、is_atom_concave、is_atom_affine、is_atom_log_log_convex、is_atom_log_log_concave、is_atom_log_log_affine、is_incr、is_decr、grad、domain等方法全部定义在Atom基类上(atom.py),这正是文档中autoclass:: cvxpy.atoms.atom.Atom所列出的成员。
Atom 的两种实现形态
从实现角度看,一个 atom 有两种形态(cvxpy.atoms.rst):
形态一:类构造器。大多数 atom 是一个类的构造函数。例如 atomX ↦ λ_max(X)通过构造cvxpy.atoms.lambda_max.lambda_max类的实例来应用,该类直接继承自Atom,间接继承自Expression。
形态二:包装函数。有些 atom 是一个包装器,初始化并返回其他某个类的 Atom 实例。文档给出了一个经典的交互式示例:
import cvxpy as cp X = cp.Variable(shape=(2,2), symmetric=True) expr = cp.lambda_min(X) print(type(expr))运行结果是:
<class 'cvxpy.atoms.affine.unary_operators.NegExpression'>这个结果乍看意外,实则由三个事实决定:
CVXPY 将
lambda_min实现为恒等式$$\lambda_{\min}(X) = -\lambda_{\max}(-X),$$
取负运算符本身是"基于类的 atom"(即 cvxpy/atoms/affine/unary_operators.py 中的
NegExpression类);一个 Expression 的精确类型由最后作用于它的基于类的 atom 决定。
也就是说,调用cp.lambda_min(X)时,真正返回的对象是NegExpression实例。这一细节对理解 CVXPY 表达式类型很有价值:表达式的运行时类型并不总是与调用的原子函数同名。
Atoms 的三个子板块
Atoms 文档分为三个子板块(cvxpy.atoms.rst):
Affine Atoms(cvxpy.atoms.affine.rst):所有在此列出的 atom 对其参数都是仿射的,包括AddExpression、MulExpression、DivExpression、bmat、block、conj、convolve、cumsum、diag、diff、einsum、hstack、imag、index、kron、matmul、multiply、outer、partial_trace、partial_transpose、promote、psd_wrap、real、reshape、vdot、sum、trace、transpose、broadcast_to、swapaxes、moveaxis、permute_dims、NegExpression、upper_tri、vec、vec_to_upper_tri、vstack、stack等。它们的实现分布在 cvxpy/atoms/affine 目录下的各模块中,例如AddExpression位于 add_expr.py,MulExpression与DivExpression位于 binary_operators.py。
Elementwise Atoms(cvxpy.atoms.elementwise.rst):所有在此列出的 atom 逐元素地作用于表达式。例如exp对其输入表达式的每个条目做指数运算。该板块包含abs、entr、exp、huber、inv_pos、kl_div、log、log_normcdf、log1p、loggamma、logistic、maximum、minimum、neg、pos、power、rel_entr、scalene、sqrt、square、xexp,以及基于 cvxpy/logic.py 的逻辑原子Not(~x)、And(x & y)、Or(x | y)、Xor(x ^ y)、implies(x => y)、iff(x <=> y)。实现位于 cvxpy/atoms/elementwise 目录。
Other Atoms(cvxpy.atoms.other_atoms.rst):既非仿射也非逐元素的 atom,包括cummax、cumprod、cvar、diff_pos、dotsort、eye_minus_inv、geo_mean、gmatmul、harmonic_mean、inv_prod、lambda_max、lambda_min、lambda_sum_largest、lambda_sum_smallest、log_det、log_sum_exp、matrix_frac、max、min、mixed_norm、norm、norm1、norm2、norm_inf、normNuc、one_minus_pos、perspective、pf_eigenvalue、pnorm、Pnorm、ptp、prod、quad_form、quad_over_lin、resolvent、sigma_max、std、sum_largest、sum_smallest、sum_squares、SuppFuncAtom、tr_inv、tv、var、von_neumann_entr等。实现分布于 cvxpy/atoms 目录的顶层模块中。
关于每个 atom 的定义域、符号、曲率等属性的紧凑可读汇总,文档建议查阅 doc/source/functions 页面(cvxpy.atoms.rst)。
Expressions:CVXPY 的表达式树表示
CVXPY 将数学对象表示为表达式树(expression tree)。表达式树是由一个或多个 atom 连接起来的一批数学表达式,编码为Expression类的实例。树中的每个Leaf(叶子)都是一个Variable、Parameter或Constant(cvxpy.expressions.rst)。
Expressions 板块(cvxpy.expressions.rst)文档化了以下类:
| 类 | 源码位置 | 关键成员 |
|---|---|---|
Expression | cvxpy/expressions/expression.py | value、grad、domain、name、curvature、is_constant、is_affine、is_convex、is_concave、is_dcp、is_log_log_affine、is_log_log_convex、is_log_log_concave、is_dgp、is_dqcp、is_dpp、sign、shape、size、ndim、T,以及全套运算符重载__pow__、__add__、__mul__、__matmul__、__div__、__rshift__、__lshift__、__eq__、__le__、__ge__等 |
Leaf | cvxpy/expressions/leaf.py | shape、size、ndim、T、value、project、project_and_assign |
Variable | cvxpy/expressions/variable.py | 在Leaf基础上增加name |
Parameter | cvxpy/expressions/constants/parameter.py | 在Leaf基础上增加round |
Constant | cvxpy/expressions/constants/constant.py | shape、size、ndim、T、value |
CallbackParam | cvxpy/expressions/constants/callback_param.py | 在Parameter基础上支持回调参数 |
值得注意的是Expression基类通过运算符重载实现了+、-、*、@、/、>>、<<、==、<=、>=等语法——这正是 CVXPY 能让建模代码"看起来像数学"的底层机制。例如x + y实际上构造了一个加法表达式节点,x <= y则构造了一个约束对象。
Constraints:约束变量的七种约束类型
约束(constraint)是限制优化问题定义域的等式或不等式。CVXPY 共有七种约束类型(cvxpy.constraints.rst):
- NonPos(非正不等式)—— cvxpy/constraints/nonpos.py
- Zero(等式)—— cvxpy/constraints/zero.py
- PSD(半正定)—— cvxpy/constraints/psd.py
- SOC(二阶锥)—— cvxpy/constraints/second_order.py
- ExpCone(指数锥)—— cvxpy/constraints/exponential.py
- PowCone3D / PowConeND(3 维与 N 维幂锥)—— cvxpy/constraints/power.py
文档明确指出:绝大多数用户只需要创建前三种约束(NonPos、Zero、PSD),而且多数用户除了"如何创建约束"之外无需了解更多。不过,约束 API 仍为高级用户提供了有用的方法,例如检查对偶变量值(dual_value)与残差(violation)。
约束板块文档化的类及其成员如下(cvxpy.constraints.rst):
| 类 | 成员 |
|---|---|
Constraint(基类) | value、violation、is_dcp |
NonPos | value、violation、is_dcp、shape、size、dual_value |
Zero | value、violation、is_dcp |
PSD | value、violation、is_dcp |
SOC | value、violation、is_dcp |
ExpCone | value、violation、is_dcp |
RelEntrConeQuad | value、is_dcp |
PowCone3D | value、violation、is_dcp |
PowConeND | value、violation、is_dcp |
FiniteSet | is_dcp、size、shape、ineq_form、violation |
OpRelEntrConeQuad | value、is_dcp |
约束的基类Constraint定义于 cvxpy/constraints/constraint.py,FiniteSet定义于 cvxpy/constraints/finite_set.py。RelEntrConeQuad与OpRelEntrConeQuad位于 exponential.py,分别代表相对熵锥的二次型与算子形式。
Problems:建模与求解的入口
Problem 类:封装目标与约束
Problem类是"指定并求解优化问题"的入口。每个Problem实例封装一个优化问题,即一个目标函数和一组约束(cvxpy.problems.rst)。其核心源码位于 cvxpy/problems/problem.py。
Problem.solve()方法要么求解该实例编码的问题(返回最优值并将变量值设为最优点),要么报告问题不可行(infeasible)或无界(unbounded)。文档给出了完整的建模—求解—检查范式:
problem = Problem(Minimize(expression), constraints) problem.solve() if problem.status not in ["infeasible", "unbounded"]: # Otherwise, problem.value is inf or -inf, respectively. print("Optimal value: %s" % problem.value) for variable in problem.variables(): print("Variable %s: value %s" % (variable.name(), variable.value))这段代码对应了Problem类上的核心 API:solve(problem.py)、status、value、variables()、name()。从源码看,Problem.solve接受*args, **kwargs,这些参数最终会传递给求解链与具体的求解器后端。
问题的不可变性(Immutability)
Problem 是不可变的,唯一的例外是通过设置Parameter的值来改变问题。这意味着创建之后不能修改问题的目标或约束(cvxpy.problems.rst)。
如果需要在已有问题上追加约束,文档给出的标准惯用法是创建一个新问题:
problem = Problem(Minimize(expression), constraints) problem = Problem(problem.objective, problem.constraints + new_constraints)这个"重建而非原地修改"的设计与Problem类的不可变语义一致,同时也解释了为什么 CVXPY 会提供Parameter机制——通过parameter.value = ...在保持问题结构不变的前提下实现参数化重求解。
大多数用户需要的三个方法
文档明确指出,大多数用户除了实例化、调用solve、查询status与value之外,无需了解Problem类的更多细节(cvxpy.problems.rst)。
不过Problem类还提供了丰富的进阶 API(完整成员列表见 cvxpy.problems.rst):
- 问题性质:
is_dcp、is_dgp、is_dqcp、is_qp、is_dpp - 对象遍历:
variables、parameters、constants、atoms、var_dict(problem.py) - 灵敏性分析:
backward(problem.py)与derivative(problem.py),用于计算参数扰动对解的影响 - 求解链接口:
get_problem_data(problem.py)、unpack_results(problem.py)、register_solve(problem.py) - 统计信息:
size_metrics、solver_stats、compilation_time
Minimize / Maximize 目标与统计类
目标函数由Minimize与Maximize两个类表示,均文档化了is_dcp与is_dgp两个方法,实现位于 cvxpy/problems/objective.py。
此外,问题规模信息与最近一次求解的统计信息分别由两个类捕获:
SizeMetrics:记录问题实例的规模信息,可通过Problem.size_metrics属性访问;SolverStats:记录最近一次求解调用的统计信息,可通过Problem.solver_stats属性访问。
两者都定义于 cvxpy/problems/problem.py。
Transforms:超越原子函数的附加操作
Transforms 提供原子函数之外的额外方式来操纵 CVXPY 对象。关键区别在于:原子函数只作用于表达式,而transforms 可以接收 Problem、Objective 或 Constraint 对象作为输入;且 transforms 不必遵循任何特定 API(cvxpy.transforms.rst)。
Transforms 板块(cvxpy.transforms.rst)包含三类:
SuppFunc(支撑函数):SuppFunctransform 以"某个 CVXPY Variable 的隐式表示"描述一个凸集,返回代表该凸集支撑函数的函数句柄。当该句柄被求值时,返回一个SuppFuncAtom对象,该对象可像任何其他 CVXPY Expression 一样用于凸优化建模。实现位于 cvxpy/transforms/suppfunc.py,SuppFuncAtom原子位于 cvxpy/atoms/suppfunc.py。
Scalarize(标量化):将一组目标转换为单个目标,例如加权和。所有标量化对每个目标都是单调的,这意味着在标量化目标上优化总是返回相对于原始目标列表的 Pareto 最优点;而且除边界点外,Pareto 曲线上的所有点都可以通过某种目标加权实现。该板块文档化了四种标量化方法(cvxpy/transforms/scalarize.py):
weighted_sum:加权和max:极大化log_sum_exp:log-sum-exp 平滑targets_and_priorities:目标与优先级
其他 transforms:
indicator(cvxpy/transforms/indicator.py):约束的指示函数linearize(cvxpy/transforms/linearize.py):表达式的线性化partial_optimize(cvxpy/transforms/partial_optimize.py):部分变量优化的算子
Reductions:将问题重写为求解器可接受的形式
什么是 Reduction
Reduction 是从一个问题到另一个等价问题的变换。两个问题等价当且仅当其中一个问题的解可以"以不超过适度的工作量"转换为另一个问题的解。CVXPY 使用 reductions 将问题重写为求解器接受的形式(cvxpy.reductions.rst)。
例如,Dcp2Cone将符合 DCP 规则的问题转换为锥规划(conic)形式,Qp2SymbolicQp将具有二次/分段仿射目标、仿射等式约束与分段线性不等式约束的问题转换为更接近求解器输入的形式(cvxpy.reductions.back_end.rst)。转换后的输出还需经过一系列未在此文档化的 reduction 才能交给求解器。
重要声明:Reduction 不属于公开 API
Reductions 文档附有明确的免责声明(cvxpy.reductions.rst):
大多数用户不需要知道 CVXPY 归约系统的任何内容,甚至不需要知道 reductions 的存在。为保证提升速度与能力的同时保留向后兼容的灵活性,归约系统不被视为 CVXPY 公开 API 的一部分,其各方面内容可能在未来的版本中不经通知地改变。
这份文档面向的是:CVXPY 贡献者、好奇心强的用户,以及愿意构建在一个"未来版本可能不可用"的 API 之上的人。因此,普通用户应通过Problem.solve()使用归约系统,而不是直接调用 reduction 类。
中间端(Middle-End)与后端(Back-End)归约
借用软件编译器的术语,CVXPY 将归约分为两类(cvxpy.reductions.rst):
中间端归约(middle-end reduction):在不考虑目标求解器的情况下简化源问题。此类归约与求解器类型无关,无论目标是二次规划求解器还是锥求解器都可应用。包括(cvxpy.reductions.middle_end.rst):
Complex2Real:cvxpy/reductions/complex2real/complex2real.py,将复值问题转换为实值问题CvxAttr2Constr:cvxpy/reductions/cvx_attr2constr.py,将变量属性(如 PSD、对称)转换为约束Dgp2Dcp:cvxpy/reductions/dgp2dcp/dgp2dcp.py,将 DGP(log-log 凸)问题转换为 DCP 问题EvalParams:cvxpy/reductions/eval_params.py,求值参数FlipObjective:cvxpy/reductions/flip_objective.py,翻转目标(最大化↔最小化)
后端归约(back-end reduction):将源问题转换为某类求解器可接受的形式。每个求解器(连同其调用模式)被称为一个back-end或target。当前支持两类后端:锥求解器与二次规划求解器。当通过
Problem.solve()求解问题时,CVXPY 会尝试为问题找到最佳后端。包括(cvxpy.reductions.back_end.rst):Dcp2Cone:cvxpy/reductions/dcp2cone/dcp2cone.pyQp2SymbolicQp:cvxpy/reductions/qp2quad_form/qp2symbolic_qp.pyDualize与Slacks:cvxpy/reductions/cone2cone/affine2direct.py,对锥问题施加对偶化与松弛变换
归约系统的核心类
归约系统通过以下核心类交换数据并运作(cvxpy.reductions.rst):
Solution(cvxpy/reductions/solution.py):求解器返回的解的载体,携带最优值、变量值、对偶值及状态信息。Reduction(cvxpy/reductions/reduction.py):抽象基类,核心方法为__init__、accepts(判断是否接受某问题)、reduce(执行归约)、retrieve、apply与invert(将解映射回原问题空间)。Chain(cvxpy/reductions/chain.py):按顺序组合多个 reduction 的容器。SolvingChain(cvxpy/reductions/solvers/solving_chain.py):一条完整的"问题 → 归约 → 求解器 → 解"链路,Problem.solve()内部正是通过它完成从用户模型到求解器调用的全过程。
后端求解器接口的实现位于 cvxpy/reductions/solvers 目录,按锥求解器(conic_solvers)、二次规划求解器(qp_solvers)、非线性求解器(nlp_solvers)分类组织。
总结:如何高效使用这份 API 参考
把六个板块串起来,就得到了 CVXPY 的完整工作流:
- 建模:用
Variable、Parameter、Constant构造叶子,用 atoms(exp、log、norm、quad_form等)组合成表达式树; - 约束:用
<=、>=、==等运算符重载创建NonPos、Zero、PSD等约束; - 封装:用
Minimize/Maximize与约束列表构造Problem; - 求解:调用
problem.solve(),由 CVXPY 自动选择后端归约链(Dcp2Cone → 锥求解器,或 Qp2SymbolicQp → QP 求解器); - 后处理:通过
problem.status、problem.value、problem.solver_stats检查结果,用backward()/derivative()做灵敏性分析,或用problem.get_problem_data()拿到送给求解器的原始数据。
对于进阶用户,建议结合 functions_table 快速查阅每个 atom 的曲率、符号与单调性属性;对于想深入源码的读者,本指南中给出的每个类都标注了对应的实现文件,可以直接跳转阅读。
最后提醒:Reductions 板块是唯一被官方声明"不属于公开 API"的部分,普通建模代码应始终通过Problem.solve()间接使用归约能力;只有当你作为贡献者或确实需要构建在归约系统之上、且不介意未来版本变更时,才应直接调用这些类。
- 科学计算
【免费下载链接】cvxpy
A Python-embedded modeling language for convex optimization problems.
相关推荐
CVXPY 表达式体系详解:表达式树、叶节点与核心 API 参考指南
CVXPY 表达式体系详解:表达式树、叶节点与核心 API 参考指南 导读 CVXPY 是一个内嵌于 Python 的凸优化建模语言,它的所有建模能力都建立在
科学计算Hunk 测试体系全解:跨模块、跨进程与终端边界的分层测试布局与命令指南
Hunk 测试体系全解:跨模块、跨进程与终端边界的分层测试布局与命令指南 本篇技术指南围绕 Hunk(Review first 终端 diff 查看器)仓库的顶
科学计算CPython 表达式语言参考精读:从原子、推导式到运算符优先级的完整语法与求值语义
CPython 表达式语言参考精读:从原子、推导式到运算符优先级的完整语法与求值语义 本篇是 CPython 语言参考(Language Reference)中
编程语言语言运行时解释器标准库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考