Команда read обеспечивает особенно простой способ получения значений в AMPL. Необходимые значения перечислены в обычном порядке в файле. Файл данных не должен содержать ничего кроме значений, которые нужно прочитать. Ни наборов, ни имен параметров, ни двоеточий, ни операторов :=. В простейшем виде read читает список значений для параметра и файла. Значения присваиваются элементам параметра в порядке их появления. Например, если нужно прочитать количество недель и количество часов, доступных каждую неделю:
param T > 0; param avail {1..T} >= 0;
*.txt
из файла week_data.txt, содержащего значения:
4 40 40 32 40
тогда необходимо записать следующую команду:
read T, avail[1], avail[2], avail[3], avail[4] < week_data.txt;
Чтобы сказать то же самое более кратко, - можно использовать индексное выражение:
read T, {t in 1..T} avail[t] < week_data.txt;
Если имеется файл *.txt вида:
Example.txt 3 10 2 6 7 9 4 5 3 1
И для него необходимо прочитать данные вида:
Set W[3]:= 10,2,6; Set W[7]:=9,4; Set W[5]:=3,1;
Это можно сделать следующим образом:
- Оператор read AMPL должен иметь какой-то способ узнать, когда он достиг конца строки или конца файла. Например, можно добавить 0 для обозначения окончания, чтобы файл txt выглядел так:
3 10 2 6 0 7 9 4 0 5 3 1 0 0
- Затем можно использовать следующий скрипт для чтения данных из файла:
repeat { read W_index < Example.txt; if W_index = 0 then break; let W[W_index] := {}; repeat { read W_val < Example.txt; if W_val = 0 then break; let W[W_index] := W[W_index] union {W_val}; } }
Обозначение < имя файла, определяет имя файла для чтения. Аналогично > указывает на запись в файл.
В общем, команда read имеет следующий вид:
read item-list < filename;
При этом, item-list является разделенным запятыми списком элементов, каждый элемент которого, может иметь следующие типы:
parameter
{indexing} parameter
{indexing} (item-list)
Первые два типа используются в наших примерах выше. Третий тип item-list позволяет применять одну индексацию к нескольким элементам:
param prodcost{PROD} >= 0; param invcost{PROD} >= 0; param revenue{PROD,1..T} >= 0;
Из файла, можно прочитать каждый параметр отдельно:
read {p in PROD} prodcost[p] < cost_data; read {p in PROD} invcost[p] < cost_data; read {p in PROD, t in 1..T} revenue[p,t] < cost_data;
Чтение из файла cost_data сначала всех затрат на производство, затем всех затрат на хранение, а затем всех доходов. Если данные организованы по продуктам, тогда можно записать:
read {p in PROD}(prodcost[p], invcost[p], {t in 1..T} revenue[p,t]) < cost_data;
Читать затраты на производство и хранение, а также доходы по первому продукту, затем по второму и т. д. Список элементов может содержать вложенные списки элементов, так что если нужно прочитать:
param market {PROD,1..T} >= 0;
Из этого же файла, можно записать:
read {p in PROD}(prodcost[p], invcost[p], {t in 1..T} (revenue[p,t], market[p,t])) <cost_data;
В этом случае, для каждого продукта читается две стоимости. Затем для каждой недели читается доход от продажи продуктов и размер спроса. Как показывают описания, форма списка элементов оператора read зависит от того, как значения данных упорядочены в файле. Когда читаются данные, индексированные по наборам строк, которые, как и PROD, не упорядочены по своей природе, тогда порядок, в котором считываются значения, является порядком, в котором AMPL их внутренне представляет. Если элементы набора были получены непосредственно из оператора набора данных, то порядок будет таким же, как в операторе данных. В противном случае лучше всего указывать ordered или ordered by в объявлении набора модели, чтобы гарантировать, что порядок всегда будет соответствовать ожидаемому.
Альтернативой, позволяющей избежать определения порядка элементов в наборе, является их явное указание в файле, который читается. В качестве примера рассмотрим, как можно использовать оператор read вместо оператора данных, чтобы получить значения из параметра cost, который определен как:
param cost{ORIG,DEST,PROD} >= 0, default 9999;
Можно настроить оператор read следующим образом:
param ntriples integer; param ic symbolic in ORIG; param jc symbolic in DEST; param kc symbolic in PROD; read ntriples, {1..ntriples}(ic, jc, kc, cost[ic,jc,kc]) <cost_data;
Соответствующий файл cost_dat должен начинаться примерно так:
18 CLEV FRA bands 27 PITT FRA bands 24 CLEV FRA coils 23 ... и т.д. еще 15 записей
Строки в файле для команды read, которые содержат любые символы, кроме букв, цифр, подчеркиваний, точки, + и -, должны заключаться в кавычки, как и в режиме данных. Однако сам оператор read интерпретируется в режиме модели. Поэтому, если оператор ссылается на какую-либо конкретную строку. Например:
read {t in 1..T} revenue ["bands",t];
тогда эта строка должна быть заключена в кавычки. Имя файла после < не должно быть заключено в кавычки, если оно не содержит пробелов, точек с запятой или непечатаемых символов.
Если оператор read не содержит < filename, значения считываются из текущего входного потока. Таким образом, если ввести команду read в приглашении AMPL, можно вводить значения в последующих подсказках, до тех пор, пока всем перечисленным элементам не будут присвоены значения. Например:
read T, {t in 1..T} avail[t]; 4 40 40 32 40 display avail; avail [*]:= 1 40 2 40 3 32 4 40 ;
Приглашение с ampl? возвращается в ampl: когда все необходимые данные прочитаны. Имя файла - (литеральный знак минуса) принимается в качестве стандартного ввода для процесса AMPL. Это полезно для интерактивного ввода информации. Во всех примерах предполагается, что базовым наборам, таким как ORIG и PROD, уже были присвоены значения с помощью оператора данных. Таким образом, оператор read обычно дополняет, а не заменяет другие команды ввода. Это особенно полезно при обработке длинных файлов данных, которые генерируются внешними приложениями и программами.
Чтение данных из нескольких двоичных файлов
Следующий пример производит чтение данных из нескольких файлов:
set filenames = {"file1.txt","hypercube43","input.txt"}; param n integer > 0; param a {1..n} >= 0; for {f in filenames} {reset data; read n <(f); read {j in 1..n} a[j] <(f); display a; }
Если имена файлов часто меняются, тогда вместо того, чтобы указывать их имена в модели, их можно прочитать как элементы набора filenames из файла - либо в формате текстового файла AMPL (с использованием data), либо в неформатированном виде текст (с использованием команды read). https://groups.google.com/g/ampl/c/qCSkXDmmsyo
*.CSV в *.dat
Структура файла *.CSV:
IndexSetName, Column1, Column2 index1, 1, 2 index2, 3, 4 index3, 5, 6 |
Структура *.dat файла:
param: IndexSetName: Column1, Column2 := index1 1, 2 index2 3, 4 index3 5, 6 |
*.MPS в *.dat
Преобразование MPS в *.dat в основном предназначено для ситуаций, когда имеются файлы в формате MPS, и пользователь желает увидеть насколько хорошо данные задачи решаются с помощью решателей подключенных к AMPL. Большинство решателей способны читать файлы MPS напрямую, однако сделав преобразование MPS в *.dat один раз, а затем используя AMPL для решения сформулированных задач, не нужно будет разбираться в MPS-интерфейсах каждого решателя.
Существуют сценарии awk для преобразования файлов MPS в файлы данных AMPL (.dat), который можно использовать с некоторыми предопределенными моделями AMPL. На странице www.netlib.org/ampl/models см. файлы awk: m2a и m2ai, а также модель, файлы mps.mod, mps1.mod и mpsi.mod. Краткие комментарии в файлах объясняют как их использовать. Бесплатные конверторы awk доступны для скачивания для всех популярных платформ.