|
|
|
GUSS 求解器文档
Gather-Update-Solve-Scatter (GUSS)
目录
引言
GUSS (Gather-Update-Solve-Scatter) 是 GAMS 的一个特殊求解器,专门用于高效地批量求解多个相关的优化问题(场景)。它通过在求解过程中执行 Gather(收集数据)→ Update(更新参数)→ Solve(求解模型)→ Scatter(分散结果)四个步骤,避免了重复的模型生成和求解开销,大大提高了多场景分析的效率。
GUSS 的核心价值在于:当需要求解大量相似但参数不同的优化问题时,传统方法需要为每个场景重新生成并求解模型,而 GUSS 只需生成模型一次,之后通过内存中的快速数据交换来切换场景,可将求解效率提升数十倍甚至数百倍。
GUSS 概念
2.1 工作流程
GUSS 将问题求解过程分为四个步骤:
- Gather(收集):从 GAMS 符号中收集模型输入数据,包括目标函数的系数、约束矩阵的元素、变量的边界等。
- Update(更新):根据当前场景的定义,更新模型中的参数。GUSS 支持对任何模型输入数据的修改,包括目标系数、约束系数、右侧值(RHS)、变量边界等。
- Solve(求解):调用底层求解器(如 CPLEX、Gurobi、CONOPT 等)求解当前场景的优化模型。
- Scatter(分散):将求解结果(变量水平、对偶变量、约简成本等)写回 GAMS 符号中,供后续分析使用。
2.2 主要优势
- 极高的求解效率:避免重复的模型生成和加载,场景切换仅在内存中完成,速度提升可达 10-100 倍。
- 灵活的场景定义:支持任意数量的场景和参数变化,可以通过 GAMS 集合和参数动态生成场景。
- 完整的数据集成:与 GAMS 的数据处理和报告生成功能无缝集成,结果可直接用于后续分析。
- 支持多种求解器:GUSS 作为"wrapper"求解器,可以调用 GAMS 支持的任何底层求解器,包括 LP、MIP、NLP、MCP 等。
- 内存高效:只加载模型一次,所有场景共享同一个模型实例,大幅降低内存使用。
2.3 适用场景
- 敏感性分析:改变目标系数或约束右侧值,观察最优解的变化。
- 多周期优化:同一模型在不同时间段或不同数据切面上的求解。
- 随机规划:处理多种随机情景(如需求不确定、价格波动等)的优化问题。
- 参数扫描:系统地变化一组参数,生成解空间的热力图或趋势图。
- 鲁棒优化:在各种参数组合下验证解的可行性和质量。
- 投资组合优化:在不同风险偏好或市场假设下求解最优资产配置。
架构与工作原理
3.1 内部架构
GUSS 作为 GAMS 求解器体系中的"元求解器"(meta-solver),其架构分为三层:
- GUSS 控制层:负责场景调度、数据传递和结果收集。该层根据用户定义的场景数据文件(以 GAMS 参数形式提供)确定场景数量和每个场景的参数变化。
- GAMS 模型层:包含实际的优化数学模型。模型只需编译一次,之后 GUSS 通过修改内存中的模型数据来切换场景。
- 底层求解器层:GUSS 调用用户指定的底层求解器(如 LP 用 CPLEX、NLP 用 CONOPT 等)来实际求解每个场景的优化问题。
3.2 场景执行流程
GUSS 在执行场景时的具体流程如下:
- 读取场景数据定义,确定场景数量和每个场景的索引。
- 编译并加载模型到内存(仅一次)。
- 对每个场景:
- 根据场景定义更新模型中的参数值。
- 调用底层求解器求解当前模型。
- 收集求解结果,按照 GUSS Dictionary 的映射关系,将结果写入对应的 GAMS 符号。
- 所有场景求解完毕后,将累积的结果数据传递回 GAMS 环境。
3.3 性能特征
GUSS 的性能优势随着场景数量和模型规模的增加而更加显著:
- 对于小型模型(数百个变量)和少量场景(10-100 个),加速比约为 5-10 倍。
- 对于中型模型(数千个变量)和中等数量场景(100-1000 个),加速比约为 10-50 倍。
- 对于大型模型(数万个变量)和大量场景(1000+ 个),加速比可达 50-100 倍以上。
- 性能提升主要来源于:节省模型编译时间、节省模型生成时间、更好的内存局部性。
场景数据定义
4.1 场景集合(Scenario Set)
场景集合是 GUSS 场景定义的基础。用户需要定义一个集合来索引所有场景:
Set s / s1*s10 /; * 定义 10 个场景
Set ds(s) / s2, s5, s8 /; * 仅对部分场景求解
4.2 场景参数(Scenario Parameters)
场景参数用于定义每个场景中模型输入数据的值。GUSS 使用特殊的 GAMS 参数来传递场景数据:
Parameter
scen_data(s, *) 场景数据表
/
s1, demand 100
s1, cost 2.5
s2, demand 120
s2, cost 2.8
s3, demand 150
s3, cost 3.0
/;
4.3 场景字典(GUSS Dictionary)
GUSS Dictionary 是连接场景数据和模型之间的桥梁。它定义了哪些模型数据将被更新、哪些结果将被收集,以及数据在场景参数中的位置:
Parameter
dict(*) / 'demand' . 'scen_data'
'scen_data'. 'demand'
'cost' . 'scen_data'
'scen_data'. 'cost'
'x' . 'result_data'
'result_data'. 'x'
/;
GUSS Dictionary 的每个条目遵循 <符号名称> . <数据表名称> 的格式:
- 左侧为模型中的符号名称(参数、变量或方程)
- 右侧为场景数据或结果数据的参数名称
- 输入数据:
<模型参数> . <场景数据参数>
- 输出数据:
<场景数据参数> . <模型变量/方程>
使用方法
5.1 基本用法
在 GAMS 中使用 GUSS 求解器,首先需要设置相应的模型类型:
OPTION LP = GUSS;
OPTION NLP = GUSS;
OPTION MIP = GUSS;
OPTION RMIP = GUSS;
OPTION RMINLP = GUSS;
然后定义场景数据、字典,并执行求解:
Set s / s1*s5 /;
Parameter
scen_data(s, *) 场景数据
/
s1, demand, 100
s2, demand, 120
s3, demand, 150
s4, demand, 200
s5, demand, 250
/;
Parameter
dict(*) / 'demand' . 'scen_data'
'scen_data' . 'demand' /;
Parameter
result_data(s, *); * 存放结果
Solve simple_model using LP minimizing obj;
5.2 完整调用流程
* 步骤 1:定义模型(和平常一样)
Variables x, obj;
Equations eq1, eq2;
...
Model mymodel /all/;
* 步骤 2:定义场景集合和数据
Set s / s1*s100 /;
Parameter input_data(s, *) 场景输入数据;
* 步骤 3:定义 GUSS Dictionary
Parameter guss_dict(*) / ... /;
* 步骤 4:准备结果数据容器
Parameter output_data(s, *);
* 步骤 5:设置 GUSS 为求解器
OPTION LP = GUSS;
* 步骤 6:求解
Solve mymodel using LP minimizing obj;
* 步骤 7:分析结果
Display output_data;
选项与参数
6.1 关键选项
GUSS 通过 GAMS 选项和自身特有的参数控制行为。以下是最常用的选项:
| 选项名称 |
默认值 |
描述 |
GUSS dictionary |
"" |
指定 GUSS Dictionary 参数的名称,定义场景数据和模型数据之间的映射关系。 |
GUSS scenario set |
"" |
指定场景集合的名称。如果未指定,则默认使用字典中第一个数据参数的索引集合。 |
GUSS update data |
"*" |
控制在每个场景中更新的数据类型:"*" 更新所有,"input" 仅更新输入参数,"output" 仅更新输出结果。 |
GUSS keep unsolved |
0 |
当某个场景求解失败时,是否保留该场景的结果(0=丢弃,1=保留)。 |
GUSS skip invalid |
0 |
是否跳过无效的场景定义(0=报错,1=跳过并继续)。 |
GUSS log frequency |
1 |
控制日志输出频率,每隔 N 个场景输出一次求解状态。0 表示不输出。 |
GUSS solve link |
0 |
是否启用求解链接模式(0=标准模式,1=链接模式)。链接模式下 GUSS 会在场景之间保持求解器的状态,加速收敛。 |
6.2 在 GAMS 中设置选项
* 通过 Option 语句设置
Option gussDict = "dict";
Option gussSSet = "s";
Option gussLogFrequency = 10;
* 或通过 Solve 语句的选项字符串设置
Solve mymodel using LP minimizing obj
option gussDict = "dict", option gussLogFrequency = 10;
6.3 底层求解器选项
GUSS 将底层求解器的选项传递给实际的求解器。可以通过标准 GAMS 选项或求解器选项文件来控制底层求解器的行为:
* 设置底层求解器选项(以 CPLEX 为例)
Option LP = GUSS;
Option reslim = 100;
Option optcr = 0.05;
$onecho > cplex.opt
threads 4
mipgap 0.01
timelimit 60
$offecho
Option cplex = "cplex.opt";
GUSS Dictionary 详解
7.1 字典结构
GUSS Dictionary 是 GUSS 求解器的核心配置。它是一个 GAMS 参数,定义了模型符号和场景数据/结果数据之间的映射关系。字典的每个条目为一个两元组 (key, value):
Parameter dict(*) /
'demand' . 'scen_input' * 模型参数 -> 输入数据表
'scen_input' . 'demand' * 输入数据表 -> 模型参数
'x' . 'scen_output' * 模型变量 -> 输出数据表
'scen_output' . 'x' * 输出数据表 -> 模型变量
/;
7.2 字典条目类型
字典条目根据数据类型分为以下几类:
- 输入参数映射:
<模型参数> . <数据参数> 和 <数据参数> . <模型参数>。将场景数据表中的值更新到模型参数中。
- 变量映射:
<模型变量> . <数据参数> 和 <数据参数> . <模型变量>。将求解后的变量水平值写入结果数据表。
- 方程映射:
<模型方程> . <数据参数> 和 <数据参数> . <模型方程>。将求解后的方程对偶值、slack 值等写入结果数据表。
- 特殊值映射:
<特殊属性> . <数据参数>。用于将模型的整体属性(如目标函数值、求解状态等)写入结果数据表。特殊属性包括:
modelstat:模型状态
solvestat:求解状态
objval:目标函数值
resusd:资源使用量
iterusd:迭代次数
nodusd:节点使用量
7.3 完整的字典示例
Parameter dict(*) /
* --- 输入映射 ---
'demand' . 'scen_data'
'scen_data' . 'demand'
'cost_coef' . 'scen_data'
'scen_data' . 'cost_coef'
* --- 变量映射 ---
'x' . 'scen_result'
'scen_result' . 'x'
'y' . 'scen_result'
'scen_result' . 'y'
* --- 方程映射 ---
'supply' . 'scen_result'
'scen_result' . 'supply'
* --- 特殊值映射 ---
'objval' . 'scen_result'
'scen_result' . 'objval'
'modelstat' . 'scen_result'
'scen_result' . 'modelstat'
'solvestat' . 'scen_result'
'scen_result' . 'solvestat'
/;
完整示例
8.1 传感器布局优化(参数敏感性分析)
以下是一个完整的 GUSS 示例,解决一个传感器覆盖问题,分析不同预算对最优传感器布局的影响:
* ===== 传感器布局优化 - GUSS 场景分析示例 =====
Sets
i 传感器候选位置 / location1*location20 /
j 目标区域 / area1*area50 /
s 场景 / s1*s10 /;
Parameters
cov(i,j) 覆盖矩阵(1=可覆盖,0=不可覆盖)
area_val(j) 区域价值系数
cost(i) 传感器安装成本
budget(s) 各场景的预算限制
/
s1 500
s2 600
s3 700
s4 800
s5 900
s6 1000
s7 1200
s8 1400
s9 1600
s10 2000
/;
* 生成覆盖矩阵(实际应用中使用真实数据)
cov(i,j) = uniform(0,1) > 0.7;
area_val(j) = uniform(10, 100);
cost(i) = uniform(20, 100);
Variables
x(i) 是否在位置 i 安装传感器(0-1 变量)
covered(j) 区域 j 是否被覆盖
z 总覆盖价值;
Binary Variable x;
Integer Variable covered;
Equations
budget_eq 预算约束
cover_eq(j) 覆盖约束
obj 目标函数;
obj .. z =e= sum(j, area_val(j) * covered(j));
cover_eq(j) .. sum(i, cov(i,j) * x(i)) =g= covered(j);
budget_eq .. sum(i, cost(i) * x(i)) =l= budget("s1");
Model sensor_placement /all/;
* --- 定义 GUSS 场景数据和字典 ---
Parameter
scen_data(s, *) 场景数据
scen_result(s, *) 场景结果;
* 将预算数据加载到场景数据参数中
scen_data(s, 'budget') = budget(s);
Parameter dict(*) /
'budget' . 'scen_data'
'scen_data' . 'budget'
'z' . 'scen_result'
'scen_result' . 'z'
'modelstat' . 'scen_result'
'scen_result' . 'modelstat'
'solvestat' . 'scen_result'
'scen_result' . 'solvestat'
/;
* --- 使用 GUSS 批量求解所有场景 ---
Option MIP = GUSS;
Option gussDict = "dict";
Option gussLogFrequency = 1;
Solve sensor_placement using MIP maximizing z;
* --- 输出结果 ---
Display scen_result;
* 按预算排序显示覆盖价值
Parameter results(s, *) 分析结果;
results(s, 'budget') = budget(s);
results(s, 'total_value') = scen_result(s, 'z');
results(s, 'status') = scen_result(s, 'modelstat');
Display results;
8.2 运输问题(多场景需求变化)
以下是一个运输问题的 GUSS 示例,分析不同需求情景下的最优运输方案:
* ===== 运输问题 - GUSS 多场景分析 =====
Sets
i 工厂 / plant1*plant3 /
j 市场 / market1*market5 /
s 场景 / base, high_demand, low_demand, peak /;
Parameters
sup(i) 供应能力
dem(j,s) 各场景下的市场需求
dist(i,j) 运输距离
cost_tr(i,j) 单位运输成本;
* 基本数据
sup(i) = [500, 800, 600];
dem(j, 'base') = [200, 250, 150, 180, 220];
dem(j, 'high_demand') = [300, 350, 200, 280, 320];
dem(j, 'low_demand') = [150, 180, 120, 140, 160];
dem(j, 'peak') = [400, 450, 250, 350, 400];
dist(i,j) = uniform(100, 1000);
cost_tr(i,j) = 0.05 * dist(i,j);
Variables
ship(i,j) 运输量
total_cost 总运输成本;
Positive Variable ship;
Equations
supply_eq(i) 供应约束
demand_eq(j,s) 需求约束(注意场景索引)
cost_obj 成本目标;
supply_eq(i) .. sum(j, ship(i,j)) =l= sup(i);
demand_eq(j,s) .. sum(i, ship(i,j)) =g= dem(j,s);
cost_obj .. total_cost =e= sum((i,j), cost_tr(i,j) * ship(i,j));
Model transport /all/;
* --- GUSS 场景配置 ---
Parameter
scen_demand(j, s) 场景需求数据;
scen_demand(j, s) = dem(j, s);
Parameter
guss_dict(*) /
'demand_eq' . 'scen_demand'
'scen_demand' . 'demand_eq'
'total_cost' . 'scen_result'
'scen_result' . 'total_cost'
'ship' . 'scen_result'
'scen_result' . 'ship'
'modelstat' . 'scen_result'
'scen_result' . 'modelstat'
'objval' . 'scen_result'
'scen_result' . 'objval'
/;
Parameter scen_result(s, *);
Option LP = GUSS;
Option gussDict = "guss_dict";
Option gussSset = "s";
Solve transport using LP minimizing total_cost;
* 结果分析
Parameter summary(s, *);
summary(s, 'obj') = scen_result(s, 'objval');
summary(s, 'status') = scen_result(s, 'modelstat');
Display summary;
* 查看基准场景的最优运输方案
Parameter ship_base(i,j);
ship_base(i,j) = scen_result('base', 'ship');
Display ship_base;
高级功能
9.1 求解链接模式(Solve Link)
求解链接模式允许 GUSS 在场景之间保持底层求解器的内部状态。对于连续的场景(如时间序列优化),这种模式可以显著加速求解过程,因为求解器可以利用前一个场景的最优解作为当前场景的初始解:
Option gussSolveLink = 1;
使用求解链接模式的注意事项:
- 仅适用于连续的 LP 和 NLP 模型。
- 场景之间的参数变化不宜过大,否则热启动效果会降低。
- 对于 MIP 模型,链接模式可以保留之前的可行解作为上界参考。
9.2 部分场景求解
当场景定义中包含大量场景时,可以选择仅求解其中的子集。这在开发和调试阶段特别有用:
Set s 全体场景 / s1*s1000 /;
Set ds(s)调试场景 / s1, s50, s100, s500 /;
Option gussSset = "ds"; * 只求解 ds 中定义的场景
9.3 多维度场景分析
GUSS 支持多维度参数变化的场景定义。例如,同时改变供应和需求参数:
Set param_dim / supply, demand /;
Parameter multi_scen(s, param_dim);
multi_scen(s, 'supply') = ... ; * 各场景的供应参数
multi_scen(s, 'demand') = ... ; * 各场景的需求参数
* 在字典中分别映射
Parameter dict(*) /
'sup' . 'multi_scen'
'multi_scen' . 'sup'
'dem' . 'multi_scen'
'multi_scen' . 'dem'
...
9.4 结果后处理与聚合
GUSS 求解完成后,可以利用 GAMS 强大的数据处理能力对结果进行聚合分析和可视化输出:
* 计算统计摘要
Parameter stats(*, *);
stats('min', s) = smin(s, scen_result(s, 'z'));
stats('max', s) = smax(s, scen_result(s, 'z'));
stats('avg', s) = sum(s, scen_result(s, 'z')) / card(s);
stats('std', s) = sqrt(sum(s, sqr(scen_result(s, 'z') - stats('avg', s))) / (card(s)-1));
* 导出到 GDX 文件便于外部工具分析
Execute_unload 'scenario_results.gdx', scen_result;
9.5 使用条件场景
有时需要根据前一个场景的结果决定是否求解后续场景。GUSS 支持通过条件判断来控制场景执行:
* 如果某个场景的目标值超过阈值,跳过后续场景
Parameter skip_scenario(s);
skip_scenario(s) = no;
* 在 GAMS 中使用循环和 GUSS 结合实现条件场景求解
Option LP = GUSS;
* 第一次求解:基准场景
s_active(s) = sameas(s, 'base');
Option gussSset = "s_active";
Solve mymodel using LP minimizing obj;
* 根据基准结果决定是否求解其他场景
If (scen_result('base', 'modelstat') = 1,
s_active(s) = yes;
Option gussSset = "s_active";
Solve mymodel using LP minimizing obj;
);
常见问题与排错
10.1 常见错误及解决方法
| 错误信息 |
可能原因 |
解决方法 |
| Dictionary not found |
GUSS Dictionary 参数未正确定义或名称错误。 |
检查 Option gussDict 指定的参数名称是否正确,并确认该参数已在模型中定义。 |
| Scenario set is empty |
场景集合中没有任何元素。 |
检查场景集合的定义,确保至少包含一个场景。 |
| Symbol mapping error |
字典中的符号名称与模型中的符号不匹配。 |
验证字典中使用的所有符号名称(参数、变量、方程)与模型定义一致。 |
| Dimension mismatch |
场景数据参数与模型符号的维度不一致。 |
确保场景数据参数包含了模型符号所需的所有索引维度。 |
| No solver available |
未为模型类型指定有效的底层求解器。 |
检查是否有适合当前模型类型的底层求解器许可证及安装。 |
| Out of memory |
场景数据量过大,超出内存限制。 |
减少场景数量或使用更紧凑的数据表示方式。 |
10.2 性能优化建议
- 减少不必要的输出:通过
Option gussLogFrequency = 0 关闭日志输出,减少 I/O 开销。
- 合理组织场景顺序:将相似参数值的场景放在一起求解,利用求解链接模式的加速效果。
- 使用紧凑的数据结构:避免在场景数据参数中使用稀疏度极高的数据格式。
- 分批处理:对于超大规模的场景集(10,000+),考虑分成多个批次处理。
- 选择合适的底层求解器:对于 LP 模型,使用 CPLEX 或 Gurobi;对于 NLP 模型,使用 CONOPT 或 SNOPT。
10.3 调试技巧
- 先用 2-3 个场景测试 GUSS 配置,确认字典和场景数据正确后再扩展到全部场景。
- 使用
Display 语句输出场景数据参数,验证输入数据是否正确加载。
- 使用
Option gussLogFrequency = 1 查看每个场景的求解状态,快速定位出问题的场景。
- 对于复杂的模型,可以先在非 GUSS 模式下求解一个场景,验证模型和求解器设置的正确性。
- 使用
Option sysout = on 查看底层求解器的详细求解过程信息。
在线留言
尊敬的客户朋友,如您有任何意见建议,请通过下表反馈给我们,我们会尽快与您联系。
|
|
|