【问题标题】:Iterating over multiple model/data pairs in AMPL在 AMPL 中迭代多个模型/数据对
【发布时间】:2019-01-04 21:41:36
【问题描述】:

有没有一种好方法可以在 .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;

对吗?

【问题讨论】:

    标签: ampl


    【解决方案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
    

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

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

    【讨论】:

    • 我和你一样认为 Ampl 书有点乱!参考您的第一个解决方案,我们是否必须在不同的脚本中分离模型命令和数据命令?即,“主”脚本是否会调用script1.run,然后script1.run 将引用interest1.mod 作为模型并将interest_a.datinterest_b.dat 作为数据文件?还是script1.run 只是选择模型,然后“主”脚本选择interest_a.datinterest_b.dat 中的每一个,然后再递增到下一个模型?对不起,如果我不是太清楚......
    • 取决于你想如何循环它。如果您想在模型和数据的笛卡尔积上进行测试,那么您可能需要像原始代码一样使用嵌套循环构造的东西,但是使用大量命令来调用模型的脚本和另一个调用数据的脚本. OTOH,如果您只想测试特定的模型/数据组合,我猜每个模型/数据组合都是一个脚本,更像我发布的示例。
    • 实际上,如果可能的话,不要使用嵌套循环,而是使用带有多个索引变量的单个 for - 请参阅我最近的编辑。
    猜你喜欢
    • 2017-03-26
    • 2020-04-21
    • 1970-01-01
    • 2020-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-22
    相关资源
    最近更新 更多