Итерация по нескольким парам модель / данные в AMPL - PullRequest
0 голосов
/ 05 января 2019

Есть ли хороший способ перебирать пары модель / набор данных в AMPL в файле .run?

Допустим, у вас две разные модели для одних и тех же задач оптимизации и четыре набора данных. До этого момента я создавал файл .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 жалуется на использование фиктивной переменной цикла for в командах model и data. Я пытался объявить 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 голосов
/ 05 января 2019

Ваш пример кода содержит 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 задокументирован здесь , хотя 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 может существенно повлиять на производительность, поэтому, возможно, лучше привыкнуть к этой привычке.

...