Python 3.10与Cplex 12.10环境配置及线性规划实战指南
1. 环境准备与安装
在开始使用Cplex进行优化计算之前,确保你的开发环境已经正确配置。我们将从Python环境搭建开始,逐步完成整个安装过程。
1.1 Python 3.10安装
首先需要安装Python 3.10版本。虽然Cplex支持多个Python版本,但3.10提供了更好的性能和一些新特性:
# 在Linux/macOS上使用pyenv安装特定版本 pyenv install 3.10.6 # 在Windows上可以从官网下载安装包 # https://www.python.org/downloads/安装完成后,验证Python版本:
python --version # 应该显示 Python 3.10.x1.2 Cplex 12.10安装
Cplex提供了学术版和商业版,对于学习用途,我们可以使用免费的学术版或试用版:
pip install cplex==12.10安装完成后,可以通过以下命令验证是否安装成功:
import cplex print(cplex.__version__) # 应该输出 12.10.0.0 或类似版本号常见安装问题解决方案:
网络连接问题:如果下载速度慢,可以尝试使用国内镜像源:
pip install cplex -i https://pypi.tuna.tsinghua.edu.cn/simple版本冲突:确保没有其他优化求解器如Gurobi同时安装,可能会引起冲突
权限问题:在Linux/macOS上可能需要使用
sudo,但建议使用虚拟环境
2. 开发环境配置
2.1 PyCharm项目设置
在PyCharm中创建一个新项目,并配置Python解释器:
- 打开PyCharm,选择"New Project"
- 在"Location"中选择项目路径
- 在"Python Interpreter"中选择已安装的Python 3.10
- 点击"Create"完成项目创建
2.2 虚拟环境配置
为避免包冲突,建议使用虚拟环境:
# 创建虚拟环境 python -m venv cplex_env # 激活虚拟环境 # Windows: cplex_env\Scripts\activate # Linux/macOS: source cplex_env/bin/activate在虚拟环境中安装Cplex:
pip install cplex3. 第一个线性规划模型
让我们从一个简单的生产计划问题开始,这是线性规划的经典案例。
3.1 问题描述
假设一家工厂生产两种产品A和B,每单位产品A利润为3元,产品B为5元。生产受到以下限制:
- 原材料限制:产品A消耗4kg,产品B消耗2kg,总原材料不超过80kg
- 工时限制:产品A需要2小时,产品B需要3小时,总工时不超过100小时
- 市场需求:产品A最多可销售20单位,产品B最多可销售30单位
我们的目标是最大化总利润。
3.2 模型构建
首先导入Cplex库并创建问题实例:
import cplex # 创建Cplex实例 problem = cplex.Cplex() # 设置问题名称 problem.set_problem_name("Production_Planning")3.3 添加决策变量
定义产品A和B的生产量作为决策变量:
# 添加变量:产品A和B的生产量 # 参数说明:names, lower bounds, upper bounds, types, objective coefficients problem.variables.add( names=["Product_A", "Product_B"], lb=[0, 0], # 生产量不能为负 ub=[20, 30], # 市场需求上限 types=["I", "I"], # 整数变量 obj=[3, 5] # 单位利润 )3.4 添加约束条件
添加原材料和工时约束:
# 添加原材料约束:4*A + 2*B <= 80 problem.linear_constraints.add( lin_expr=[[["Product_A", "Product_B"], [4.0, 2.0]]], senses=["L"], # Less than or equal rhs=[80.0], # Right-hand side value names=["Material_Constraint"] ) # 添加工时约束:2*A + 3*B <= 100 problem.linear_constraints.add( lin_expr=[[["Product_A", "Product_B"], [2.0, 3.0]]], senses=["L"], rhs=[100.0], names=["Labor_Constraint"] )3.5 设置优化目标
设置最大化利润的目标:
# 设置优化方向为最大化 problem.objective.set_sense(problem.objective.sense.maximize)4. 模型求解与结果分析
4.1 求解模型
调用求解器进行计算:
# 求解问题 problem.solve() # 检查求解状态 status = problem.solution.get_status() print(f"Solution status: {problem.solution.status[status]}")4.2 结果提取与分析
获取并分析求解结果:
# 获取最优解 if problem.solution.is_primal_feasible(): print("Optimal solution found!") print(f"Total profit: {problem.solution.get_objective_value():.2f}元") # 获取各变量值 values = problem.solution.get_values() print(f"Produce {values[0]:.0f} units of Product A") print(f"Produce {values[1]:.0f} units of Product B") # 计算资源使用情况 material_used = 4 * values[0] + 2 * values[1] labor_used = 2 * values[0] + 3 * values[1] print(f"Material used: {material_used:.1f}kg / 80kg") print(f"Labor used: {labor_used:.1f} hours / 100 hours") else: print("No feasible solution found")4.3 结果可视化
虽然Cplex本身不提供可视化功能,但我们可以使用Matplotlib进行简单的图形展示:
import matplotlib.pyplot as plt import numpy as np # 绘制可行域 A = np.linspace(0, 20, 400) B_material = (80 - 4*A)/2 B_labor = (100 - 2*A)/3 plt.figure(figsize=(10, 6)) plt.plot(A, B_material, label='Material Constraint') plt.plot(A, B_labor, label='Labor Constraint') plt.axvline(x=20, color='r', linestyle='--', label='Demand A') plt.axhline(y=30, color='g', linestyle='--', label='Demand B') # 标记最优解 optimal_A = values[0] optimal_B = values[1] plt.scatter(optimal_A, optimal_B, color='red', s=100, label='Optimal Solution') plt.xlabel('Product A') plt.ylabel('Product B') plt.title('Feasible Region and Optimal Solution') plt.legend() plt.grid(True) plt.xlim(0, 25) plt.ylim(0, 45) plt.show()5. 高级功能与技巧
5.1 参数调优
Cplex提供了大量参数可以调整求解过程:
# 设置求解参数 problem.parameters.timelimit.set(60) # 限制求解时间为60秒 problem.parameters.mip.tolerances.mipgap.set(0.01) # 设置最优间隙为1% problem.parameters.threads.set(4) # 使用4个线程5.2 模型导出与导入
可以将模型导出为LP或SAV格式,便于分享或后续分析:
# 导出模型为LP文件 problem.write("production_model.lp") # 从文件加载模型 new_problem = cplex.Cplex() new_problem.read("production_model.lp")5.3 灵敏度分析
分析约束条件的影子价格和变量的缩减成本:
if problem.solution.is_primal_feasible(): # 获取影子价格(对偶变量) shadow_prices = problem.solution.get_dual_values() print(f"Material constraint shadow price: {shadow_prices[0]:.4f}") print(f"Labor constraint shadow price: {shadow_prices[1]:.4f}") # 获取缩减成本 reduced_costs = problem.solution.get_reduced_costs() print(f"Product A reduced cost: {reduced_costs[0]:.4f}") print(f"Product B reduced cost: {reduced_costs[1]:.4f}")6. 常见问题与调试技巧
6.1 典型错误与解决方案
变量添加格式错误:
# 错误写法 - 会报TypeError x = problem.variables.add(names='x', types='I') # 正确写法 - 所有参数都应该是列表 x = problem.variables.add(names=['x'], types=['I'])约束表达式括号层级错误:
# 错误写法 - 缺少一层括号 problem.linear_constraints.add( lin_expr=[[['x', 'y'], [1.0, 2.0]]], senses=['L'], rhs=[30] ) # 正确写法 - 注意三层括号结构 problem.linear_constraints.add( lin_expr=[[[['x', 'y'], [1.0, 2.0]]]], senses=['L'], rhs=[30] )内存不足问题:
- 对于大规模问题,可能需要增加内存限制:
problem.parameters.workmem.set(2048) # 设置工作内存为2GB
- 对于大规模问题,可能需要增加内存限制:
6.2 性能优化建议
模型预处理:
# 启用预处理 problem.parameters.preprocessing.presolve.set( problem.parameters.preprocessing.presolve.values.on)使用惰性约束:
# 对于某些复杂约束,可以设置为惰性约束 problem.parameters.mip.strategy.lazyconstraints.set(1)并行求解:
# 启用并行求解 problem.parameters.parallel.set(1) # 1=确定性并行,-1=自动
7. 实际应用案例扩展
7.1 运输问题建模
让我们考虑一个更复杂的例子:运输问题。假设有三个工厂和四个仓库,需要最小化运输成本。
# 创建运输问题实例 transport = cplex.Cplex() transport.set_problem_name("Transportation_Problem") # 添加变量:从工厂i到仓库j的运输量 transport.variables.add( names=[f"x_{i}_{j}" for i in range(3) for j in range(4)], lb=[0]*12, # 运输量不能为负 types=["C"]*12, # 连续变量 obj=[ # 运输成本 10, 8, 12, 6, # 工厂0到各仓库 9, 11, 13, 7, # 工厂1到各仓库 14, 10, 7, 8 # 工厂2到各仓库 ] ) # 添加工厂产能约束 for i in range(3): transport.linear_constraints.add( lin_expr=[[ [f"x_{i}_{j}" for j in range(4)], [1]*4 ]], senses=["L"], rhs=[100, 150, 200][i], # 各工厂产能 names=[f"Supply_{i}"] ) # 添加仓库需求约束 for j in range(4): transport.linear_constraints.add( lin_expr=[[ [f"x_{i}_{j}" for i in range(3)], [1]*3 ]], senses=["E"], # 必须满足需求 rhs=[80, 90, 110, 70][j], # 各仓库需求 names=[f"Demand_{j}"] ) # 设置最小化目标 transport.objective.set_sense(transport.objective.sense.minimize) # 求解问题 transport.solve() # 输出结果 if transport.solution.is_primal_feasible(): print(f"Total transportation cost: {transport.solution.get_objective_value():.2f}") for i in range(3): for j in range(4): val = transport.solution.get_values(f"x_{i}_{j}") if val > 1e-6: # 只显示有运输量的路径 print(f"Factory {i} -> Warehouse {j}: {val:.1f} units")7.2 混合整数规划示例
考虑一个固定成本生产问题,需要使用二进制变量表示是否生产某种产品:
# 创建混合整数规划实例 mip = cplex.Cplex() mip.set_problem_name("Fixed_Cost_Production") # 添加生产量变量(连续) mip.variables.add( names=["Prod_A", "Prod_B"], lb=[0, 0], ub=[100, 100], types=["C", "C"], obj=[5, 8] # 单位利润 ) # 添加是否生产的二进制变量 mip.variables.add( names=["Use_A", "Use_B"], types=["B", "B"] # 二进制变量 ) # 添加固定成本项到目标函数 mip.objective.set_linear([("Use_A", -20), ("Use_B", -30)]) # 固定成本 # 添加逻辑约束:如果生产量>0,则必须使用对应的二进制变量 mip.linear_constraints.add( lin_expr=[ [[["Prod_A"], [1]], [["Use_A"], [-100]]], # Prod_A <= 100*Use_A [[["Prod_B"], [1]], [["Use_B"], [-100]]] # Prod_B <= 100*Use_B ], senses=["L", "L"], rhs=[0, 0] ) # 添加资源约束 mip.linear_constraints.add( lin_expr=[[["Prod_A", "Prod_B"], [2, 3]]], senses=["L"], rhs=[120] # 总资源限制 ) # 设置最大化利润 mip.objective.set_sense(mip.objective.sense.maximize) # 求解问题 mip.solve() # 输出结果 if mip.solution.is_primal_feasible(): print(f"Total profit: {mip.solution.get_objective_value():.2f}") print(f"Produce A: {mip.solution.get_values('Prod_A'):.1f} units") print(f"Produce B: {mip.solution.get_values('Prod_B'):.1f} units") print(f"Use factory A: {'Yes' if mip.solution.get_values('Use_A') > 0.5 else 'No'}") print(f"Use factory B: {'Yes' if mip.solution.get_values('Use_B') > 0.5 else 'No'}")