Итерация по нескольким парам модель / данные в AMPL

Есть ли хороший способ перебрать пары модель / набор данных в 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;

Правильно?


person Anakhand    schedule 04.01.2019    source источник


Ответы (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 задокументирован здесь, хотя, ТБХ, я нахожу документацию немного сложной следовать.

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

person Geoffrey Brent    schedule 05.01.2019
comment
Разделяю ваше мнение о том, что амплификатор немного замусоренный! Что касается вашего первого решения, нужно ли нам разделять команды модели и команды данных в разных сценариях? То есть, будет ли основной сценарий вызывать script1.run, а затем script1.run будет ссылаться на interest1.mod как на модель, а на interest_a.dat и interest_b.dat как на файлы данных? Или script1.run просто выберет модель, а затем главный скрипт выберет каждую из interest_a.dat и interest_b.dat перед переходом к следующей модели? Извините, если я не слишком ясен ... - person Anakhand; 05.01.2019
comment
Зависит от того, как вы хотите его зациклить. Если вы хотите протестировать декартово произведение моделей и данных, вам, вероятно, понадобится что-то структурированное с вложенными циклами, как ваш исходный код, но с использованием одной партии команд для вызова сценариев для моделей, а другой - для вызова сценариев для данных. . OTOH, если вы хотите протестировать только определенные комбинации модели / данных, я предполагаю, что каждая комбинация модели / данных будет одним скриптом, больше похожим на пример, который я опубликовал. - person Geoffrey Brent; 05.01.2019
comment
На самом деле, вместо вложенных циклов, по возможности используйте один for с несколькими индексными переменными - см. Мое недавнее изменение. - person Geoffrey Brent; 05.01.2019