В некоторых случаях удобно объявить индексированную коллекцию таблиц или определить индексированную коллекцию столбцов данных в таблице.
Индексированные коллекции таблиц
Объявления таблиц AMPL могут быть проиндексированы почти так же, как наборы, параметры и другие компоненты модели. Необязательный {indexing-expr} следует за именем таблицы (table-name):
table table-name {indexing-expr} string-list : ...
param nCOL integer > 0; table COL {c in 1..nCOL} IN "ODBC" "colinput.xls" ("COL" & c): [DAYD,CROP,QUAL], COL;
или с помощью цикла в теле скрипта *.run файла:
for{c in 1..nCOL} код, который выполняет read table COL[c];
для чтения таблицы значений c в таблицу COL.
Одна таблица определена для каждого элемента набора, указанного в indexing-expr. Отдельные таблицы в этой коллекции обозначаются обычным способом, добавляя нижний индекс в скобках или нижний индекс к table-name. В качестве примера, следующее объявление определяет коллекцию таблиц AMPL, индексированных по набору продуктов в diet.mod, причем каждая таблица соответствует отдельной таблице базы данных в файле Access DietSens.mdb:
table DietSens {j in FOOD} OUT "ODBC" "DietSens.mdb" ("Sens" & j): [Food], f_min, Buy, f_max;
Следуя правилам стандартного обработчика таблицы ODBC, имена таблиц Access задаются третьим элементом в списке строк, строковым выражением («Sens» & j). Таким образом, таблица AMPL DietSens ["BEEF"] связана с таблицей доступа SensBEEF, таблица AMPL DietSens ["CHK"] связана с таблицей доступа SensCHK и так далее.
Доступ к базе данных с таблицами анализа чувствительности.
Альтернативный сценарий AMPL записывает оптимальный рацион питания при продаже двух продуктов по цене «два продукта по цене одного» в одну таблицу Access:
for {j in FOOD} { let cost[j] := cost[j] / 2; solve; write table DietSens[j]; let cost[j] := cost[j] * 2; }
Альтернативная таблица Access для анализа чувствительности
Для данных в diet2a.dat в наборе FOOD есть восемь элементов, поэтому в базе данных Access записано восемь таблиц, как показано на рисунке выше. Вместо объявления отдельных 8 таблиц в одном файле можно записать данные в отдельные 8 файлов изменив формулировку для второй записи, которая указывает имя файла:
table DietSens {j in FOOD} OUT "ODBC" ("DietSens" & j & ".mdb"): [Food], f_min, Buy, f_max; param nCOL integer > 0; table COL {c in 1..nCOL} IN "ODBC" ("COL" & c & ".xls"): [DAYD,CROP,QUAL], COL;
или с помощью цикла в теле скрипта *.run файла:
param sumSD {1..nCOL}; for {c в 1..nCOL} {read table COL[c]; solve; let sumSD[c] := sum {h in DAYH, t in DAYD, k in PROD, q in QUAL, d in DC, i in CUST, r in TRANS} SD[h,t,k,q,d,i,r];}
AMPL запишет восемь разных файлов базы данных Access с именами DietSensBEEF.mdb, DietSensCHK.mdb и т.д., каждый из которых содержит одну таблицу с именем (по умолчанию) DietSens. (Файлы должны быть созданы до выполнения команд записи таблицы). Строковое выражение можно использовать аналогичным образом, для назначения каждому элементу индексированной коллекции таблиц AMPL отдельного столбца datacol с персональным именем, в одной и той же таблице Access:
table DietSens {j in FOOD} "ODBC" "DietSens.mdb": [Food], Buy ˜ ("Buy" & j);
Вывод данных AMPL в двумерную таблицу Excel.
Таблицы AMPL в этом случае намеренно оставлены с состоянием чтения / записи по умолчанию, INOUT. Если состояние чтения / записи указано как OUT, тогда каждая таблица записи перезаписывает столбцы, созданные предыдущей записью.
Доступ к таблице можно получить следующим образом:
table Foods IN "ODBC" (filename): FOOD <- [FOOD], cost, f_min, f_max; param filename symbolic = "WarehouseData.xlsx"; table Foods IN "ODBC" (filename): FOOD <- [FOOD], cost, f_min, f_max;
Чтение данных с нескольких листов xlsx
Следующий скрипт позволяет прочитать несколько листов xlsx. Затем внутри цикла происходит чтение таблицы, соответствующей текущему значению j, с помощью команды read table a0[j];
param m := 9; param a0 {1..m}; set SIM := 1..2; table a0 {j in SIM} IN "amplxl" "N=9_2.xlsx" ("a0_" & j): [P], a0; load amplxl.dll; for {j in SIM} {read table a0[j]; display a0;};
Индексированные коллекции столбцов данных
Поскольку существует естественное соответствие между столбцами данных реляционной таблицы и индексированными коллекциями объектов в модели AMPL, каждая спецификация данных в объявлении таблицы обычно ссылается на отдельный параметр, переменную или выражение AMPL. Однако иногда значения для одного объекта AMPL распределяются между несколькими столбцами данных. Такой случай может быть обработан путем определения набора столбцов данных, по одному для каждого элемента указанного набора индексации. Чаще всего эта функция используется для чтения или записи двумерных таблиц. Например, данные для параметра
param amt {NUTR,FOOD} >= 0;
Файл diet.mod может быть представлен в электронной таблице Excel в виде двумерной таблицы. Где питательные вещества, представляют строки, а продукты - столбцы, как показано на рисунке ниже:
Двумерная таблица Excel
Чтобы читать подобную таблицу с использованием функций внешней базы данных AMPL, мы должны рассматривать ее как имеющую один ключевой столбец с заголовком NUTR и столбцы данных, озаглавленные наименованием отдельных продуктов. Таким образом, нам требуется объявление таблицы, у которой key-spec является одномерным, а data-specs индексируются по набору AMPL FOOD:
Конструкция <чтение 2D таблицы>
Концептуально существует симметрия между индексированием строк и столбцов двумерной таблицы. Но поскольку таблицы в этих примерах обрабатываются как реляционные таблицы, объявление таблицы должно обрабатывать индексацию строк и столбцов по-разному. В результате выражения, описывающие индексацию строк, существенно отличаются от выражений, описывающих индексацию столбцов. Как показывают эти примеры, общая форма для указания индексированной коллекции столбцов таблицы:
{indexing-expr} < data-spec, data-spec, data-spec, ... >
Где каждый data-spec имеет любую из ранее указанных форм. Для каждого элемента набора, указанного в indexing-expr, AMPL генерирует копию для каждой data-spec в угловых скобках <...>. Indexing-expr также определяет один или несколько фиктивных индексов, которые выполняются для всего набора индексов. Эти индексы используются в выражениях внутри data-specs, а также появляются в строковых выражениях, которые дают имена столбцов во внешней базе данных.
table dietAmts IN "ODBC" "Diet2D.xls": [i˜NUTR], {j in FOOD} <amt[i,j] ˜ (j)>;
Спецификация ключа [i˜NUTR], связывает первый столбец таблицы с набором AMPL NUTR. Спецификация данных {j в FOOD} <...>, заставляет AMPL генерировать индивидуальный data-spec для каждого элемента набора FOOD. В частности, для каждого j in FOOD, AMPL генерирует спецификацию данных amt[i,j]˜(j), где (j) - строковое выражение AMPL для заголовка столбца внешней таблицы продукта j. В параметр amt[i,j] должны быть записаны значения столбца (j). (В соответствии с соглашением, используемым в объявлениях и командах AMPL, использование круглых скобок (j) заставляют интерпретировать это выражение как строку. Без скобок это выражение будет обозначать имя столбца, состоящее из одного символа j.
Аналогичный подход работает для записи двумерных таблиц в электронные таблицы. Например, после того как SteelT.mod решен, результаты могут быть записаны в двумерную электронную таблицу, используя следующее объявление table:
table Results1 OUT "ODBC" "steel1out.xls": {p in PROD} -> [Product], Inv[p,0] ˜ Inv0, {t in 1..T} < Make[p,t] ˜ (’Make’ & t), Sell[p,t] ˜ (’Sell’ & t),Inv[p,t] ˜ (’Inv’ & t) >;
Или, же самое, с использованием индексации:
table Results2 OUT "ODBC" "steel2out.xls": [Product], {p in PROD} Inv[p,0] ˜ Inv0, {t in 1..T} < {p in PROD} (Make[p,t] ˜ (’Make’ & t), Sell[p,t] ˜ (’Sell’ & t), Inv[p,t] ˜ (’Inv’ & t) ) >;
Ключевой столбец помечает строки с названиями продуктов. Столбцы данных включают в себя один столбец для начальных запасов, а затем еще три, представляющие производство make, продажи Sell и запасы Inv, соответственно, для каждого периода, как показано на следующем рисунке: