Списки одномерных наборов и параметров
Для параметра, индексированного по одномерному набору:
set PROD; param rate {PROD} > 0;
Чтение наборов
Спецификация набора может быть простым списком его элементов ():
data; set PROD:= bands coils plate ;
Чтение параметров
Спецификация параметра очень похожа на спецификацию набора, за исключением добавления значения после каждого элемента набора:
param rate:= bands 200 coils 140 plate 160 ;
Ввиду того, что лишние пробелы и разрывы строк игнорируются, спецификация параметра может быть записана в виде списка:
param rate := bands 200 coils 140 plate 160 ;
Если одномерный набор был объявлен упорядоченным или циклическим, тогда порядок его элементов наследуется из оператора данных, который его определяет:
set WEEKS ordered; data; set WEEKS:= 27sep 04oct 11oct 18oct ;
Списки двумерных наборов и параметров
В качестве примера рассмотрим следующие наборы:
set ORIG; # место происхождения set DEST; # место назначения set LINKS within {ORIG,DEST}; # транспортные дуги
Чтение двумерных наборов
Элементы ORIG и DEST являются одномерными наборами, поэтому запишем их спецификацию по ранее рассмотренному сценарию:
set ORIG:= GARY CLEV PITT ; set DEST:= FRA DET LAN WIN STL FRE LAF ;
Двумерный набор LINKS можно указать в виде списка кортежей:
set LINKS := (GARY,DET) (GARY,LAN) (GARY,STL) (GARY,LAF) (CLEV,FRA) (CLEV,DET) (CLEV,LAN) (CLEV,WIN) (CLEV,STL) (CLEV,LAF) (PITT,FRA) (PITT,WIN) (PITT,STL) (PITT,FRE) ;
Или в виде списка пар, без скобок и запятых:
set LINKS := GARY DET GARY LAN GARY STL GARY LAF CLEV FRA CLEV DET CLEV LAN CLEV WIN CLEV STL CLEV LAF PITT FRA PITT WIN PITT STL PITT FRE ;
Порядок элементов в каждой паре является существенным - первым элементом должен быть элемент набора ORIG, вторым DEST. Сами пары могут появляться в любом порядке.
Альтернативный и более краткий способ описать этот набор - применить вместо первого элемента шаблон данных:
set LINKS := (GARY,*) DET LAN STL LAF (CLEV,*) FRA DET LAN WIN STL LAF (PITT,*) FRA WIN STL FRE ;
Также можно перечислить все первые элементы, которые идут с шаблоном второго элемента:
set LINKS := (*,FRA) CLEV PITT (*,DET) GARY CLEV (*,LAN) GARY CLEV (*,WIN) CLEV PITT (*,LAF) GARY CLEV (*,FRE) PITT (*,STL) GARY CLEV PITT ;
Выражения (GARY, *) или (*, FRA) напоминают пару, один из элементов которой заменен на знак * шаблоном данных. Каждый шаблон сопровождается списком записей, к которым он применяется для генерации пар. Сгенерированная пара представляет собой срез размерности набора. Кортеж без каких-либо *, такой как (GARY, DET), по сути является шаблоном, который определяет только себя, поэтому за ним не следуют никакие значения. С другой стороны, для таблицы, которая содержит только пары:
set LINKS:= GARY DET GARY LAN GARY STL GARY LAF CLEV FRA CLEV DET CLEV LAN CLEV WIN CLEV STL CLEV LAF PITT FRA PITT WIN PITT STL PITT FRE ;
шаблоном по умолчанию является (*, *), который применяется ко всем записям.
Чтение двумерных параметров
Для параметра cost индексированного по двумерному набору LINKS:
param cost{LINKS} >= 0;
Данные имеют следующий вид в виде списка пар:
param cost := GARY DET 14 GARY LAN 11 GARY STL 16 GARY LAF 8 CLEV FRA 27 CLEV DET 9 CLEV LAN 12 CLEV WIN 9 CLEV STL 26 CLEV LAF 17 PITT FRA 24 PITT WIN 13 PITT STL 28 PITT FRE 99 ;
Для указанного случая применяется шаблон по умолчанию [*, *]
Применив шаблон данных к двумерному параметру получим:
param cost:= [GARY,*] DET 14 LAN 11 STL 16 LAF 8 [CLEV,*] FRA 27 DET 9 LAN 12 WIN 9 STL 26 LAF 17 [PITT,*] FRA 24 WIN 13 STL 28 FRE 99 ;
Шаблоны для параметров окружены скобками [], чтобы отличить их от шаблонов наборов (). Таким образом, шаблон [GARY, *] указывает, что будут указаны записи для значений параметра, для которых первым элементом набора выступает GARY, а запись DET 14, назначает параметру cost["GARY", "DET"] значение 14. Все вышеперечисленное также применимо к использованию шаблонов к каждому элементу:
param cost := [*,FRA] CLEV 27 PITT 24 [*,DET] GARY 14 CLEV 9 [*,LAN] GARY 11 CLEV 12 [*,WIN] CLEV 9 PITT 13 [*,STL] GARY 16 CLEV 26 PITT 28 [*,FRE] PITT 99 [*,LAF] GARY 8 CLEV 17
Списки многомерных множеств и параметров
Концепции, лежащие в основе списков данных для двумерных наборов и параметров, распространяются и для более высоких измерений. Единственное отличие состоит в том, что срезы делаются более чем в одном измерении.
Рассмотрим версию транспортной модели, которая определяет набор троек и затрат, индексированных по ним:
set ROUTES within {ORIG,DEST,PROD}; param cost {ROUTES} >= 0;
Чтение наборов троек
Предположим, что PROD имеет только элементы bands, и coils, а ROUTES имеет в качестве элементов тройки {ORIG, DEST, PROD}. Тогда членство в ROUTES может быть определено простым списком троек кортежей:
set ROUTES := (GARY,LAN,coils) (GARY,STL,coils) (GARY,LAF,coils) (CLEV,FRA,bands) (CLEV,FRA,coils) (CLEV,DET,bands) (CLEV,DET,coils) (CLEV,LAN,bands) (CLEV,LAN,coils) (CLEV,WIN,coils) (CLEV,STL,bands) (CLEV,STL,coils) (CLEV,LAF,bands) (PITT,FRA,bands) (PITT,WIN,bands) (PITT,STL,bands) (PITT,FRE,bands) (PITT,FRE,coils) ;
или
set ROUTES := GARY LAN coils GARY STL coils GARY LAF coils CLEV FRA bands CLEV FRA coils CLEV DET bands CLEV DET coils CLEV LAN bands CLEV LAN coils CLEV WIN coils CLEV STL bands CLEV STL coils CLEV LAF bands PITT FRA bands PITT WIN bands PITT STL bands PITT FRE bands PITT FRE coils ;
Используя шаблоны с тремя элементами, мы можем разбить спецификацию на части, поместив один символ * в каждом шаблоне. В следующем примере мы разрезаем набор по второму измерению:
set ROUTES := (CLEV,*,bands) FRA DET LAN STL LAF (PITT,*,bands) FRA WIN STL FRE (GARY,*,coils) LAN STL LAF (CLEV,*,coils) FRA DET LAN WIN STL (PITT,*,coils) FRE ;
Поскольку набор не содержит элементов с источником GARY и bands, шаблон (GARY, *, bands) отсутствует. Срезы могут проходить через несколько измерений. В частности, разрез двух измерений, естественно, предполагает размещение двух символов * в каждом шаблоне. Здесь мы разрезаем первое и третье измерения:
set ROUTES := (*,FRA,*) CLEV bands CLEV coils PITT bands (*,DET,*) CLEV bands CLEV coils (*,LAN,*) GARY coils CLEV bands CLEV coils (*,WIN,*) CLEV coils PITT bands (*,STL,*) GARY coils CLEV bands CLEV coils PITT bands (*,FRE,*) PITT bands PITT coils (*,LAF,*) GARY coils CLEV bands ;
Поскольку эти шаблоны имеют два символа *, за ними должны следовать пары компонентов, которые подставляются слева направо для создания элементов набора. Например, шаблон (*, FRA, *), за которым следуют CLEV bands, указывает, что (CLEV, FRA, bands) является элементом набора.
Чтение параметров для наборов троек
По аналогии с вышеперечисленной логикой построения шаблонов, для определения спецификации параметров можно записать:
param cost:= [CLEV,*,bands] FRA 27 DET 9 LAN 12 STL 26 LAF 17 [PITT,*,bands] FRA 24 WIN 13 STL 28 FRE 99 [GARY,*,coils] LAN 11 STL 16 LAF 8 [CLEV,*,coils] FRA 23 DET 8 LAN 10 WIN 9 STL 21 [PITT,*,coils] FRE 81 ;
или
param cost := [*,*,bands] CLEV FRA 27 CLEV DET 9 CLEV LAN 12 CLEV STL 26 CLEV LAF 17 PITT FRA 24 PITT WIN 13 PITT STL 28 PITT FRE 99 [*,*,coils] GARY LAN 11 GARY STL 16 GARY LAF 8 CLEV FRA 23 CLEV DET 8 CLEV LAN 10 CLEV WIN 9 CLEV STL 21 PITT FRE 81
или
param cost := CLEV DET bands 9 CLEV DET coils 8 CLEV FRA bands 27 CLEV FRA coils 23 CLEV LAF bands 17 CLEV LAN bands 12 CLEV LAN coils 10 CLEV STL bands 26 CLEV STL coils 21 CLEV WIN coils 9 GARY LAF coils 8 GARY LAN coils 11 GARY STL coils 16 PITT FRA bands 24 PITT FRE bands 99 PITT FRE coils 81 PITT STL bands 28 PITT WIN bands 13 ;
Размещая знак * в разных позициях в шаблонах, мы можем получить 3 одномерных среза, а также 3 двумерных среза. Шаблон [*,*,*] указывает трехмерный список:
param cost := CLEV DET bands 9 CLEV DET coils 8 CLEV FRA bands 27 ...
В более общем случае, шаблон для n-мерного набора или параметра в форме списка должен иметь n записей. Каждая запись является либо допустимым элементом набора, либо знаком *. Шаблоны для наборов заключены в круглые скобки (), а шаблоны для параметров заключены в квадратные скобки []. За шаблоном следует последовательность элементов, каждый из которых включает в себя один элемент набора для каждого знака * и дополнительно одно значение параметра в случае шаблона параметра. Шаблон применяется ко всем элементам между ним и следующим шаблоном (или окончанием оператора данных). Шаблоны с разным количеством знаков * могут использоваться вместе в одном и том же операторе данных, если каждому параметру присваивается значение только один раз. Если шаблон не отображается, предполагается, что шаблон *,*...
Объединение списков наборов и параметров
Когда необходимо задать данные для набора и параметра, индексированного по этому набору, мы указываем элементы набора дважды:
set PROD := bands coils plate ; param rate := bands 200 coils 140 plate 160 ;
AMPL позволяет избежать этого дублирования, включив имя набора в оператор param при определении данных:
param: PROD: rate := bands 200 coils 140 plate 160 ;
AMPL использует это утверждение для определения как элементов PROD, так и значений для rate. Другая распространенная избыточность возникает, когда необходимо определить данные для нескольких параметров, индексированных по одному набору. Такие как rate, profit и market, индексированных по PROD. Вместо того, чтобы написать отдельные операторы данных для каждого параметра:
param rate:= bands 200 coils 140 plate 160 ; param profit:= bands 25 coils 30 plate 29 ; param market:= bands 6000 coils 4000 plate 3500 ;
мы можем объединить эти операторы в один, перечислив все три имени параметра после ключевого слова param:
param: rate profit market:= bands 200 25 6000 coils 140 30 4000 plate 160 29 3500 ;
Поскольку AMPL игнорирует лишние пробелы и переносы строк, у нас есть возможность перегруппировать список в удобный для чтения вид:
param: rate profit market:= bands 200 25 6000 coils 140 30 4000 plate 160 29 3500 ;
В любом случае, у нас все еще есть возможность добавить имя набора индексации в оператор:
param: PROD: rate profit market := bands 200 25 6000 coils 140 30 4000 plate 160 29 3500 ;
Так происходит объединение спецификаций набора и параметров. Идентичные правила применяются к спискам любых многомерных множеств и индексированным по ним параметрам. Для нашего двумерного примера LINKS мы могли бы записать:
param: LINKS: cost := GARY DET 14 GARY LAN 11 GARY STL 16 GARY LAF 8 CLEV FRA 27 CLEV DET 9 CLEV LAN 12 CLEV WIN 9 CLEV STL 26 CLEV LAF 17 PITT FRA 24 PITT WIN 13 PITT STL 28 PITT FRE 99 ;
Либо указать членство в наборе LINKS и значения параметров cost и limit, проиндексированных по нему:
param: LINKS: cost limit := GARY DET 14 1000 GARY LAN 11 800 GARY STL 16 1200 GARY LAF 8 1100 CLEV FRA 27 1200 CLEV DET 9 600 CLEV LAN 12 900 CLEV WIN 9 950 CLEV STL 26 1000 CLEV LAF 17 800 PITT FRA 24 1500 PITT WIN 13 1400 PITT STL 28 1500 PITT FRE 99 1200 ;
При использовании шаблонов применяются те же подходы:
param: LINKS: cost := [GARY,*] DET 14 LAN 11 STL 16 LAF 8 [CLEV,*] FRA 27 DET 9 LAN 12 WIN 9 STL 26 LAF 17 [PITT,*] FRA 24 WIN 13 STL 28 FRE 99 ;
или
param: LINKS: cost limit := [GARY,*] DET 14 1000 LAN 11 800 STL 16 1200 LAF 8 1100 [CLEV,*] FRA 27 1200 DET 9 600 LAN 12 900 WIN 9 950 STL 26 1000 LAF 17 800 [PITT,*] FRA 24 1500 WIN 13 1400 STL 28 1500 FRE 99 1200 ;
Членство в наборе индексации указывается вместе со значениями двух параметров cost и limit. Шаблон [GARY, *], за которым следует заданный элемент DET, значения 14 и 1000 указывают, что (GARY, DET) необходимо добавить в набор LINKS, а cost и limit[GARY, DET] имеют значения 14 и 1000 соответственно.
Как показывают наши иллюстрации, ключ к интерпретации оператора param, который предоставляет значения для нескольких параметров или для набора из параметров, находится в первой строке. Она состоит из слова param, за которым следует двоеточие, а затем необязательные: имя набора индексации и двоеточие, затем список имен параметров, заканчивающихся оператором присваивания :=. Каждый последующий элемент в списке состоит из числа элементов набора, равного количеству знаков * и объявленных наборов в самом последнем шаблоне, числу значений параметров, равному количеству параметров, перечисленных в первой строке. Обычно параметры, перечисленные в первой строке оператора param, индексируются по одному и тому же набору. Однако это не обязательное правило. Для варианта модели диеты, мы определили следующие ограничения для питательных веществ:
set MINREQ; set MAXREQ; param n_min {MINREQ} >= 0; param n_max {MAXREQ} >= 0;
Так, что n_min и n_max индексируются по наборам питательных веществ, которые могут пересекаться, но вряд ли будут одинаковыми. Набор данных для этой модели:
set MINREQ := A B1 B2 C CAL ; set MAXREQ := A NA CAL ; param: n_min n_max := A 700 20000 C 700 . B1 0 . B2 0 . NA . 50000 CAL 16000 24000 ;
Error:…invalid subscript … discarded
Каждая точка «.» указывает AMPL, что для соответствующего параметра и индекса не дается никакого значения. Поскольку MINREQ не содержит элемента NA, параметр n_min[NA] не определен. Мы не можем просто оставить пробел для этой записи. AMPL примет его равным 50000: обработка в режиме данных игнорирует все лишние пробелы. Мы также не можем поставить ноль для этой записи, так как в этом случае мы получим сообщение об ошибке, когда AMPL впервые попытается получить доступ к n_min (обычно при первом решении).
error processing param n_min: invalid subscript n_min[’NA’] discarded.
Когда мы указываем набор в первой строке param, набор еще не может иметь значений. Если спецификация данных параметров нашего примера была бы задана как:
model; set NUTR = MINREQ union MAXREQ; data; param: NUTR: n_min n_max := A 700 20000 C 700 . B1 0 . B2 0 . NA . 50000 CAL 16000 24000 ;
AMPL сообщит об ошибке.
dietu.dat, line 16 (offset 366): NUTR was defined in the model context: param: NUTR >>> : <<< n_min n_max :=
потому, что объявление NUTR в модели, определило его как объединение MINREQ и MAXREQ.
set NUTR = MINREQ union MAXREQ;