遍历AMPL中的多个模型/数据对

时间:2019-01-04 21:41:36

标签: ampl

是否有很好的方法可以在.run文件中的AMPL中遍历模型/数据集对?

假设您有两个针对相同优化问题的不同模型和四个数据集。我到目前为止要做的是为每个模型/数据集对创建一个.run文件,并分别运行每个脚本,或者为每个模型保留一个脚本,并通过修改data (file);手动求解每个数据集命令。但这对于大型项目显然是乏味且不可行的。

那么有什么好办法吗?我尝试过的操作类似于以下内容(为清楚起见,只是一个框架):

# Some global options


for {model_ in {'1.mod', '2.mod'}} {
    reset;
    model (model_);

    for {dat in {'1.dat', '2.dat', '3.dat', '4.dat'}} {
        update data;
        data (dat);
        solve;

        # Do some post-processing
    }
}

但是AMPL抱怨在modeldata命令中使用for循环的虚拟变量。我尝试声明symbolic参数来存储模型和数据文件的名称,但也没有用。

当然,只有在模型足够相似的情况下,这才有意义,因为它们可以用相同的方式进行后处理。但是至少应该有 一种方法来遍历一个模型中的数据文件而不必像这样

update data;
data 1.dat;
solve;

update data;
data 2.dat;
solve;

update data;
data 3.dat;
solve;

[...]

update data;
data 427598.dat;
solve;

对吗?

1 个答案:

答案 0 :(得分:1)

您的示例代码在循环内有一个reset。这将重置循环参数,这将导致问题。例如,如果您运行以下命令:

for {modelname in {"itertest1.mod","itertest2.mod"}}{
    display (modelname);
    for {dataname in {"itertest_a.dat","itertest_b.dat"}}{
        display (dataname);
    }
}

它将打印出我们希望的文件名:

modelname = itertest1.mod
dataname = itertest_a.dat
dataname = itertest_b.dat
modelname = itertest2.mod
dataname = itertest_a.dat
dataname = itertest_b.dat

但是,如果我们添加一条reset语句:

for {modelname in {"itertest1.mod","itertest2.mod"}}{
    reset; ### this is the only line I changed ###
    display (modelname);
    for {dataname in {"itertest_a.dat","itertest_b.dat"}}{
        display (dataname);
    }
}

然后我们得到一个错误:modelname is not defined(因为我们只是重置了它)。

但是,即使没有这个问题,我们仍然会抱怨使用循环变量。

解决方案1:命令

根据所运行的内容,您可能会看到一条错误消息,建议您使用commands语句,如下所示:

reset;
for {scriptname in {"script1.run", "script2.run"}}{
    commands (scriptname);
}

然后它将运行列出的.run文件中的任何命令,并且您应该能够嵌套该命令以调用单独的脚本来定义模型和数据。由于您不能在不终止循环参数的情况下使用全部重置,因此您将需要使用其他选项来更新模型文件。 AMPL提供了定义多个“问题”并在它们之间进行切换的选项,这在这里可能有用也可能没有帮助;我没有探索过。

commands语句已记录在here中,尽管TBH我发现该文档有点难以理解。

解决方案2:代码生成

如果其他所有方法均失败,则可以编写一个迭代的AMPL脚本,该脚本将生成(并可选地运行)包含您要运行的所有模型/数据组合的第二个脚本,如下所示:

param outfile symbolic := "runme_temp.run";

for{i in 1..3}{
    for{j in 1..4}{
        printf "\nreset;" > (outfile);
        printf "\nmodel model%s;",i > (outfile);
        printf "\ndata data%s;",j > (outfile);
        printf "\nsolve;" > (outfile);
        # add post-processing here as desired
    }
}
close (outfile);
include runme_temp.run;

这将创建并调用如下所示的“ runme_temp.run”:

reset;
model model1;
data data1;
solve;
reset;
model model1;
data data2;
solve;

等等等。由于可以通过 使用毯子reset;,因此可以简化清理先前模型的过程。不是最尊严的解决方案,但它可以工作,并且可以适应各种情况。

这可以通过取消嵌套循环来改善:

param outfile symbolic := "runme_temp.run";

for{i in 1..3, j in 1..4}{
    printf "\nreset;" > (outfile);
    printf "\nmodel model%s;",i > (outfile);
    printf "\ndata data%s;",j > (outfile);
    printf "\nsolve;" > (outfile);
    # add post-processing here as desired
}
close (outfile);
include runme_temp.run;

在这个特定的例子中,它并没有太大的区别,但是多重嵌套循环在AMPL中可能会运行缓慢;使用单个多索引for可以对性能产生很大的影响,因此养成这种习惯可能会更好。