带等式约束与上下界的二次规划求解问题:Python求解返回None但Matlab可正常运行
带等式约束与上下界的凸二次规划求解:Python问题排查与解决方案
首先,你的问题是典型的**凸二次规划(QP)**问题(因为Q是对角半正定矩阵),Matlab的fmincon能解但Python中solve_qp返回None,大概率是参数格式不匹配、求解器调用错误,或者代码中的小bug导致的。下面一步步拆解问题:
一、先排查代码中的基础错误
你的代码里有几个明显的问题,先修正这些才能让求解器正常工作:
random.randint用法错误:Python标准库的random.randint只能生成单个整数,要生成数组得用numpy.random.randint。比如:M = np.random.randint(200, 300, size=(24,1)) ppp = np.random.randint(150, 200, size=(24,))- 上下界构造的维度隐患:你用
np.bmat拼接上下界时,第三块的[0.1],0.1*np.ones(22),[0.9]是行方向拼接,转置后虽然能得到列向量,但这种写法容易出错。建议改用np.concatenate更清晰:# 构造lb的第三部分:1个0.1 + 22个0.1 + 1个0.9,共24个元素的列向量 lb_part3 = np.concatenate([[0.1], 0.1*np.ones((22,1)), [0.9]]) lb = np.concatenate([np.zeros((24,1)), np.zeros((24,1)), lb_part3, np.zeros((24,1))]) # ub同理 ub_part3 = np.concatenate([[0.9], 0.9*np.ones((22,1)), [0.9]]) ub = np.concatenate([max_pch*np.ones((24,1)), max_pdch*np.ones((24,1)), ub_part3, 500*np.ones((24,1))]) solve_qp的参数格式问题:如果你的solve_qp来自cvxopt,它要求所有输入必须是cvxopt.matrix类型,且向量维度要严格匹配(比如q必须是列向量,而非一维数组)。直接传numpy数组会导致求解器无法解析,返回None。
二、适合你场景的求解器推荐
针对你的凸QP问题(带等式约束+上下界,Q稀疏对角),推荐以下几种Python求解器,按易用性和效率排序:
1. CVXPY(最易用,推荐快速验证)
CVXPY是高层优化建模语言,不需要关心底层求解器的参数格式,只需像写数学公式一样定义问题,它会自动调用合适的求解器(比如OSQP、ECOS等)。
示例代码:
import numpy as np import cvxpy as cp # 初始化参数(先修正基础错误) n = 24 I = np.eye(n) Z = np.zeros((n,n)) a = 0.012 b = 1.1 gamma1 = 0.9/80 gamma2 = 1.1/80 MM = np.eye(n) for i in range(n-2): MM[i+1,i] = -1 MM[0, n-1] = -1 M = np.random.randint(200, 300, size=(n,1)) max_pch = 30.0 max_pdch = 30.0 ppp = np.random.randint(150, 200, size=(n,)) # 构造Q、C、Aeq、beq、lb、ub Q = np.block([[a*I, Z, Z, Z], [Z, a*I, Z, Z], [Z, Z, 1e-5*I, Z], [Z, Z, Z, 1e-5*I]]) C = np.concatenate([b*np.ones(n), b*np.ones(n), np.zeros(n), ppp]).reshape(-1,1) Aeq = np.block([[-I, I, Z, I], [-gamma1*I, gamma2*I, MM, Z], [Z, Z, Z, Z], [Z, Z, Z, Z]]) beq = np.concatenate([M, np.zeros((3*n,1))]) lb_part3 = np.concatenate([[0.1], 0.1*np.ones((n-2,1)), [0.9]]) lb = np.concatenate([np.zeros((n,1)), np.zeros((n,1)), lb_part3, np.zeros((n,1))]) ub_part3 = np.concatenate([[0.9], 0.9*np.ones((n-2,1)), [0.9]]) ub = np.concatenate([max_pch*np.ones((n,1)), max_pdch*np.ones((n,1)), ub_part3, 500*np.ones((n,1))]) # 定义CVXPY变量和问题 x = cp.Variable((4*n, 1)) objective = cp.quad_form(x, Q) + cp.sum(cp.multiply(C, x)) constraints = [ Aeq @ x == beq, lb <= x, x <= ub ] prob = cp.Problem(cp.Minimize(objective), constraints) # 求解(默认用OSQP,适合稀疏QP) prob.solve(solver=cp.OSQP) print("QP solution status:", prob.status) print("QP solution: x = {}".format(x.value))
2. OSQP(高效,适合大规模稀疏QP)
OSQP是专门针对稀疏二次规划的求解器,你的Q是对角矩阵(极端稀疏),Aeq也有大量零元素,用OSQP效率会很高。如果不想用CVXPY的高层接口,可以直接调用OSQP:
import numpy as np import osqp # (参数构造部分同CVXPY示例,略) # 转换为OSQP需要的稀疏矩阵格式 Q_sparse = osqp.utils.dense_to_sparse(Q) Aeq_sparse = osqp.utils.dense_to_sparse(Aeq) # 创建OSQP求解器实例 solver = osqp.OSQP() solver.setup( P=Q_sparse, q=C.flatten(), A=Aeq_sparse, l=beq.flatten(), # 等式约束的上下界都是beq u=beq.flatten(), lb=lb.flatten(), ub=ub.flatten(), verbose=True # 可以看求解过程 ) # 求解 res = solver.solve() print("QP solution status:", res.info.status) print("QP solution: x = {}".format(res.x))
3. SciPy的minimize(兼容类似fmincon的SQP方法)
如果你习惯Matlabfmincon的思路,可以用SciPy的scipy.optimize.minimize,选择SLSQP方法(和fmincon的核心算法类似):
import numpy as np from scipy.optimize import minimize # (参数构造部分同CVXPY示例,略) # 定义目标函数(二次型) def objective(x): return 0.5 * x.T @ Q @ x + C.T @ x # 注意QP的标准形式通常带0.5,有些求解器需要这个 # 定义等式约束 def eq_constraint(x): return Aeq @ x - beq.flatten() constraints = [{ 'type': 'eq', 'fun': eq_constraint }] # 初始值(随便给一个可行的初始值,或者全零) x0 = np.zeros(4*n) # 求解 res = minimize( objective, x0, method='SLSQP', bounds=list(zip(lb.flatten(), ub.flatten())), constraints=constraints, options={'disp': True} ) print("QP solution status:", res.message) print("QP solution: x = {}".format(res.x))
三、为什么你的原代码返回None?
大概率是以下两个原因:
- 参数类型不匹配:如果用的是
cvxopt.solvers.qp,你直接传入numpy数组而不是cvxopt.matrix,求解器无法正确解析输入,导致求解失败返回None。 - 初始代码中的
random.randint错误:这个错误会直接导致数组生成失败,程序报错,但你说返回None,可能是你实际运行时修改了,但参数维度或类型仍有问题。
用上面推荐的任何一种方法,只要参数构造正确,都能得到和Matlabfmincon类似的结果。
内容的提问来源于stack exchange,提问作者sajad parvizi
相关产品推荐
相关产品推荐

