在 AMPL 中迭代多个 model/data 对

Iterating over multiple model/data pairs in AMPL

有没有一种在 .run 文件中迭代 AMPL 中的 model/dataset 对的好方法?

假设您有两个针对相同优化问题的不同模型和四个数据集。到目前为止,我要做的是为每个 model/dataset 对制作一个 .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 参数来存储模型和数据文件的名称,但也没有用。

当然,这只有在模型足够相似的情况下才有意义,只要它们可以 post 以相同的方式处理。但是应该 至少 有一种方法可以在一个模型中迭代数据文件,而不必像

update data;
data 1.dat;
solve;

update data;
data 2.dat;
solve;

update data;
data 3.dat;
solve;

[...]

update data;
data 427598.dat;
solve;

对吗?

您的示例代码在循环内有一个 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

但是如果我们添加一个重置语句:

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);
}

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

commands 声明已记录在案 here,尽管说实话,我发现文档有点难以理解。

方案二:代码生成

如果所有其他方法都失败了,您可以编写一个迭代的 AMPL 脚本,该脚本生成(以及可选的 运行s)第二个脚本,其中包含您想要 运行 的所有 model/data 组合,像这样:

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; 它可能会简化清理以前模型的过程。不是最有尊严的解决方案,但它有效并且可以适应广泛的 运行ge 事物。

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

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 会对性能产生很大影响,因此养成这种习惯可能会更好。