跟随老师学习线性规划求解,老师推荐了cplex,简单了解了一下,顺便也使用了一下国产copt求解器。
cplex是ibm的,win linux mac平台都有,我以linux平台使用了一下
下载到手解压缩解压缩 ~/program/cplex/opl/oplide/oplide 这是它的图形界面
命令行交互终端是 ~/program/cplex/cplex/bin/x86-64_linux/cplex
ide里新建空opl项目后,可以再新建mod模型文件,数据可以另外存在dat文件里
使用著名diet模型数据跑了一下,初步理解,就是发现输出结果小数位数很多,想格式化输出没找到办法,网上建议也是其他语言调用时用另外语言功能自定义输出
现在做科研,技术选型必不可少的就是要考察一下国产替代。在这方面国产也有好几家,我就选了一家初步看了一下:https://guide.coap.online/copt/zh-doc/intro.html
也支持win linux mac平台,提供c语言java python等各语言接口调用,不知道是不是因为我linux平台缘故,只看到命令copt_cmd,没看到图形界面
与cplex这样成熟平台随意下载免费试用相比,copt需要填写申请(电脑用户名),然后通过邮件发回一个下载地址和授权license文件,将license文件放入适当文件夹,设置好环境变量,就可以试用了。教育邮箱可以1年,普通个人半年。到期可以重新申请。这些措施可能就阻挡了一部分热情不高的用户来了解copt.
看了解压缩后自带官方文档copt/copt80/docs/copt-userguide_cn.pdf ,初步试了几个小实例,用法容易学会,copt默认读业界常用mps模型文件,能够输出求解结果。和cplex里mod文件相比,mps文件理解起来难度稍大 https://developer.aliyun.com/article/785499
cplex也支持进入cplex终端后直接读mps文件求解,常用命令和copt一致,复杂高深功能2者肯定有细节区别,不过作为非专业用户,只是读个文件求个解,这2者软件都能完成,所以在写论文做一般学术研究方面都可以用
mps文件和mod文件的转化,ai了一个python代码,不能完美转化,但是也能加深对这2类文件的理解
import re
from collections import defaultdict
def parse_mps(mps_file):
"""解析MPS文件,提取核心信息"""
model_data = {
"name": "",
"rows": defaultdict(dict), # {行名: {type: 'E/L/G', rhs: 值}}
"columns": defaultdict(dict), # {变量名: {行名: 系数}}
"bounds": defaultdict(dict), # {变量名: {lb: 值, ub: 值}}
"rhs": defaultdict(float) # {行名: 右端值}
}
with open(mps_file, 'r') as f:
lines = [line.strip() for line in f if line.strip()]
# 解析状态机
state = None
rhs_label = ""
for line in lines:
# 1. 解析模型名
if line.startswith('NAME'):
model_data["name"] = line.split()[1]
continue
# 2. 解析行(约束)
elif line.startswith('ROWS'):
state = "ROWS"
continue
# 3. 解析列(变量+系数)
elif line.startswith('COLUMNS'):
state = "COLUMNS"
continue
# 4. 解析右端项
elif line.startswith('RHS'):
state = "RHS"
rhs_label = line.split()[1] if len(line.split())>1 else ""
continue
# 5. 解析边界
elif line.startswith('BOUNDS'):
state = "BOUNDS"
continue
# 6. 结束
elif line.startswith('ENDATA'):
break
# 按状态解析行内容
parts = re.split(r'\s+', line)
if state == "ROWS":
# ROWS行格式:[类型(E=等式/L=<=/G=>=)] [行名]
row_type, row_name = parts[0], parts[1]
model_data["rows"][row_name]["type"] = row_type
model_data["rows"][row_name]["rhs"] = 0.0 # 先初始化,后续RHS填充
elif state == "COLUMNS":
# COLUMNS行格式:[变量名] [行名1] [系数1] [行名2] [系数2]
var_name = parts[0]
for i in range(1, len(parts), 2):
if i+1 >= len(parts):
break
row_name = parts[i]
if "'INTORG'"!=parts[i+1] and "'INTEND'"!=parts[i+1] :
coeff = float(parts[i+1])
model_data["columns"][var_name][row_name] = coeff
elif state == "RHS":
# RHS行格式:[RHS标签] [行名1] [值1] [行名2] [值2]
for i in range(1, len(parts), 2):
if i+1 >= len(parts):
break
row_name = parts[i]
rhs_val = float(parts[i+1])
if row_name in model_data["rows"]:
model_data["rows"][row_name]["rhs"] = rhs_val
elif state == "BOUNDS":
# BOUNDS行格式:[边界类型] [空/标签] [变量名] [值]
# 边界类型:LO=下界, UP=上界, FX=固定值, FR=无界, MI=负无穷, PL=正无穷
bound_type = parts[0]
var_name = parts[2] if len(parts)>=3 else ""
bound_val = float(parts[3]) if len(parts)>=4 else 0.0
if bound_type == "LO": # 下界
model_data["bounds"][var_name]["lb"] = bound_val
elif bound_type == "UP": # 上界
model_data["bounds"][var_name]["ub"] = bound_val
elif bound_type == "FX": # 固定值(上下界相同)
model_data["bounds"][var_name]["lb"] = bound_val
model_data["bounds"][var_name]["ub"] = bound_val
elif bound_type == "FR": # 无界(默认)
model_data["bounds"][var_name]["lb"] = -float('inf')
model_data["bounds"][var_name]["ub"] = float('inf')
# 补全未定义边界的变量(默认下界0,上界无穷)
for var in model_data["columns"]:
if var not in model_data["bounds"]:
model_data["bounds"][var]["lb"] = 0.0
model_data["bounds"][var]["ub"] = float('inf')
return model_data
def generate_opl(model_data, mod_file):
"""生成CPLEX OPL的.mod文件"""
with open(mod_file, 'w') as f:
# 1. 模型头
f.write(f"// Auto-generated from {model_data['name']}.mps\n")
f.write("using CPLEX;\n\n")
f.write(f"model {model_data['name']} {{\n\n")
# 2. 定义变量
f.write(" // Variables\n")
for var_name, bounds in model_data["bounds"].items():
lb = bounds.get("lb", 0.0)
ub = bounds.get("ub", float('inf'))
# 处理无穷值
lb_str = "-inf" if lb == -float('inf') else str(lb)
ub_str = "inf" if ub == float('inf') else str(ub)
# 连续变量(如需整数/二进制,需手动调整或扩展脚本)
f.write(f" dvar float {var_name} in {lb_str}..{ub_str};\n")
f.write("\n")
# 3. 定义约束
f.write(" // Constraints\n")
for row_name, row_data in model_data["rows"].items():
row_type = row_data["type"]
rhs_val = row_data["rhs"]
# 构建约束左侧表达式
lhs_parts = []
for var_name, coeffs in model_data["columns"].items():
if row_name in coeffs:
coeff = coeffs[row_name]
if coeff == 1.0:
lhs_parts.append(f"+ {var_name}")
elif coeff == -1.0:
lhs_parts.append(f"- {var_name}")
elif coeff > 0:
lhs_parts.append(f"+ {coeff}*{var_name}")
else:
lhs_parts.append(f"- {abs(coeff)}*{var_name}")
# 拼接左侧表达式(去掉第一个+/-)
if lhs_parts:
lhs = " ".join(lhs_parts).lstrip("+ ")
else:
lhs = "0"
# 约束类型映射
if row_type == "E":
constraint = f" subject to {row_name}: {lhs} == {rhs_val};\n"
elif row_type == "L":
constraint = f" subject to {row_name}: {lhs} <= {rhs_val};\n"
elif row_type == "G":
constraint = f" subject to {row_name}: {lhs} >= {rhs_val};\n"
else:
constraint = f" // Unknown row type {row_type}: {row_name}\n"
f.write(constraint)
f.write("\n")
# 4. 目标函数(MPS默认第一个行是目标函数,类型为N)
f.write(" // Objective (default first row as objective)\n")
objective_row = next(iter(model_data["rows"].keys())) if model_data["rows"] else ""
if objective_row and model_data["rows"][objective_row].get("type") == "N":
# 构建目标函数表达式
obj_parts = []
for var_name, coeffs in model_data["columns"].items():
if objective_row in coeffs:
coeff = coeffs[objective_row]
if coeff == 1.0:
obj_parts.append(f"+ {var_name}")
elif coeff == -1.0:
obj_parts.append(f"- {var_name}")
elif coeff > 0:
obj_parts.append(f"+ {coeff}*{var_name}")
else:
obj_parts.append(f"- {abs(coeff)}*{var_name}")
obj_expr = " ".join(obj_parts).lstrip("+ ") if obj_parts else "0"
f.write(f" minimize obj: {obj_expr};\n") # 默认最小化,如需最大化改maximize
else:
f.write(" // No objective function defined\n")
# 5. 模型尾
f.write("\n}")
# 执行转换
if __name__ == "__main__":
mps_path = "x3.mps" # 输入MPS文件路径
mod_path = "x3.mod" # 输出MOD文件路径
# 解析MPS
model_data = parse_mps(mps_path)
# 生成OPL
generate_opl(model_data, mod_path)
print(f"转换完成!MOD文件已保存至: {mod_path}")
不同格式文件转化ai并不能完美达成,还是需要学会真正规则才能方便实际中纠正ai的失误。