如何在CVXPY中为Mosek传递回调函数提前终止ILP求解
问题描述
我用CVXPY构建了一个整数线性规划(ILP)模型,目标是寻找可行解。直接将其作为可行性问题求解时往往得不到结果,因此我改成了优化问题并设置超时,求解后再检查解是否满足约束。但即便可行解很早就被找到,求解过程通常仍会耗尽全部超时时间。我希望添加一个回调函数,在求解过程中检查可行性,一旦找到目标值≥10的可行解便终止求解。我找到了Mosek官方关于回调的文档,但不清楚如何在CVXPY中实现。我的疑问是:
- 如何将
stop_criterion函数加入mosek_params? stop_criterion函数应接收哪些输入,输出什么?
示例代码:
import cvxpy as cvx import mosek import numpy as np m,n = 320,160 np.random.seed(0) A = np.random.randint(10, size = (m,n)) b = np.random.randint(10, size = m) + 100 c = np.random.randint(10, size = n) def stop_criterion(data): if data.sum() >= 10: break x = cvx.Variable(n, integer = True) prob = cvx.Problem(cvx.Maximize(c @ x), [A @ x <= b, x >= 0]) prob.solve(solver = cvx.MOSEK, mosek_params={mosek.dparam.optimizer_max_time: 10}, verbose = True)
解决方案
1. 回调函数的注册方式
在CVXPY中调用Mosek求解时,需要通过mosek_params里的mosek.iparam.callback参数注册回调函数,直接将定义好的回调函数赋值给该参数即可。
2. 回调函数的输入与输出要求
Mosek的回调函数必须遵循固定的参数格式,接收以下4个输入:
callbackcode:回调事件类型(比如找到新整数解、内点法迭代等)din:双精度数组,存储当前求解的相关数值(比如当前目标值)intin:整数数组,存储求解过程中的整数状态信息strin:字符串数组,存储求解过程中的文本信息
回调函数需要返回一个mosek.rescode类型的结果:
- 返回
mosek.rescode.ok:告诉求解器继续运行 - 返回
mosek.rescode.err_user_stop:触发求解器立即终止
我们需要关注mosek.callbackcode.mio_new_int_sol事件——这是MIP求解过程中找到新整数可行解的触发点,在此事件下检查目标值是否满足条件。
修改后的完整代码
import cvxpy as cvx import mosek import numpy as np m,n = 320,160 np.random.seed(0) A = np.random.randint(10, size = (m,n)) b = np.random.randint(10, size = m) + 100 c = np.random.randint(10, size = n) # 符合Mosek规范的回调函数 def stop_criterion(callbackcode, din, intin, strin): # 当找到新的整数可行解时检查目标值 if callbackcode == mosek.callbackcode.mio_new_int_sol: # din[0]存储当前找到的整数解的目标值 current_obj = din[0] if current_obj >= 10: # 返回终止信号 return mosek.rescode.err_user_stop # 其他情况继续求解 return mosek.rescode.ok x = cvx.Variable(n, integer = True) prob = cvx.Problem(cvx.Maximize(c @ x), [A @ x <= b, x >= 0]) # 注册回调并求解 prob.solve( solver=cvx.MOSEK, mosek_params={ mosek.dparam.optimizer_max_time: 10, mosek.iparam.callback: stop_criterion }, verbose=True ) # 检查求解结果 if prob.status in [cvx.OPTIMAL, cvx.USER_LIMIT]: print(f"找到符合条件的可行解,目标值: {prob.value:.2f}") else: print("未找到满足目标值要求的可行解")
关键说明
- 回调函数必须严格匹配Mosek的参数格式,否则会触发求解器报错
- 终止后CVXPY的问题状态会变为
USER_LIMIT,需要手动确认解的有效性 - 如果需要更细致的求解状态监控,可以扩展回调函数处理其他
callbackcode事件
内容的提问来源于stack exchange,提问作者Hennich
相关产品推荐
相关产品推荐

