Общие сведения
В моделях часто возникает необходимость вычисления значений некоторых параметров. Например, для производственной модели, мы записали следующее ограничение:
sum{p in PROD} (1/rate[p]) * Make[p] <= avail;
В связи с тем, что производительность оборудования по условиям задачи была задана в тонн/час, а коэффициент Make[p] измеряется в часах на тонну, мы использовали выражение перевода 1/rate[p]. Любое выражение параметра может использоваться в ограничениях и целях. Однако для ясности формулировок, выражения лучше сохранять простыми. Когда требуются более сложные выражения, вычисляемые параметры определяются в терминах параметров данных.
Объявление вычисляемого параметра имеет фразу присваивания с использованием оператора «=» для указания того, что параметр устанавливается равным определенному выражению.
В качестве первого примера предположим, что значения данных, представленные для модели транспортировки, состоят из общего спроса на каждый продукт вместе с долей спроса каждого пункта назначения share{DEST}. Доли мест назначения представляют собой проценты от 0 до 100. Их сумма по всем пунктам назначения tot_sh, может не совпадать с 100% из-за округления и приближения.
param share {DEST}>= 0, <= 100; # данные о долях param tot_sh = sum {j in DEST} share[j];
Затем мы можем объявить параметр для представления общих требований и вычисляемый параметр, равный спросу в каждом пункте назначения:
param tot_dem {PROD} >= 0; # данные о сумарной потребности param demand {j in DEST, p in PROD} = share[j] * tot_dem[p] / tot_sh;
Деление на tot_sh действует как поправочный коэффициент для суммы, не равной 100%. Когда спрос определен таким образом, модель может использовать его.
subject to Demand {j in DEST, p in PROD}: sum {i in ORIG} Trans[i,j,p] = demand[j,p];
Мы можем избежать вычисляемых параметров, подставляя формулы для tot_sh и demand[j,p] непосредственно в ограничение:
subject to Demand {j in DEST, p in PROD}: sum {i in ORIG} Trans[i,j,p] = share[j] * tot_dem[p] / sum{k in DEST}share[k];
Эта альтернатива делает модель немного короче, но вычислить спрос и понять структуру ограничения намного сложнее.
В качестве другого примера рассмотрим сценарий для многопериодной модели производства, в которой рассчитываются минимальные запасы. В частности, предположим, что запас продукта p за неделю t должен составлять как минимум определенную долю рынка [p, t+1], максимум, который может быть продан на следующей неделе. Таким образом, мы используем следующие объявления для предоставляемых данных:
param frac > 0; param market{PROD,1..T+1} >= 0; param mininv{p in PROD, t in 0..T} = frac * market[p,t+1]; var Inv{p in PROD, t in 0..T} >= mininv[p,t];
Параметр mininv [p,t] представляет собой минимальный запас продукта p за неделю t. AMPL постоянно обновляет все определения = параметров в течение сеанса. Например, если мы изменим значение frac, значения всех параметров mininv автоматически изменятся соответственно. Если мы определим параметр через вычисление, тогда мы не сможем указать для него значения данных. Попытка сделать это приведет к сообщению об ошибке:
mininv was defined in the model context: param >>> mininv <<< := bands 2 3000
Имеется альтернативный способ определения начального значения параметра, с сохранением возможности его изменения. Если определить параметр с помощью оператора default вместо =, тогда параметр инициализируется, а не определяется. Его начальное значение принимается равным значению выражения справа от оператора default. Начальные значения могут быть переопределены операторами данных, а также они могут быть изменены последующими операторами присваивания.
Если определить параметр с помощью оператора default вместо =, тогда можно указать значения в операторах данных, чтобы переопределить те из них, которые в противном случае были бы вычислены. Например, объявив:
param mininv {p in PROD, t in 0..T} default frac * market[p,t+1];
Мы можем указать минимальные запасы:
в качестве части данных в форме списка:
param mininv := bands 2 3000 coils 2 2000 coils 3 2000 ; |
либо в форме таблицы:
param market: 1 2 3 4 := bands . 3000 . . coils . 2000 2000 . ; |
AMPL использует «.» в выражении данных, чтобы указать несуществующую запись.
Выражение, которое задает значение параметра default (по умолчанию), оценивается только тогда, когда значение параметра требуется в первый раз. Например, когда задача или ограничение, использующее параметр, обрабатываются командой solve.
В большинстве = и фраз default за оператором следует арифметическое выражение, использующее ранее определенные наборы и параметры (но не переменные) с использованием действующих фиктивных индексов. Например, можно сгладить некоторые вариации в минимальных запасах, определив параметр mininv как скользящее среднее:
param mininv{p in PROD, t in 0..T} = if t = 0 then inv0[p] else 0.5 * (mininv[p,t-1] + frac * market[p,t+1]);
Значения mininv для недели 0 устанавливаются явно для начальных запасов, в то время как значения для каждой последующей недели t определяются на основании значений предыдущей недели. AMPL допускает любое «рекурсивное» определение подобного рода, но сообщит об ошибке, если обнаружит циклическую ссылку, которая приводит к тому, что значение параметра прямо или косвенно зависит от самого себя.
Можно использовать фразы, определенные в этом разделе, вместе с фразами раздела «Ограничения параметров», чтобы дополнительно проверить вычисляемые значения. Например объявление:
param mininv{p in PROD, t in 0..T} = frac * market[p,t+1], >= 0;
приведет к сообщению об ошибке, если вычисленное значение любого из параметров mininv будет отрицательным. Эта проверка запускается всякий раз, когда сеанс AMPL использует mininv для каких-либо целей.
If-then-Else
Следующий пример показывает как можно использовать формулу для расчета параметра с учетом конструкции If-then-else:
param M1 {i in N, j in N: i <> j} = if ??? then 999999 else S1[i] - S2[j] + travel_time[i,j];
??? означает любое условие, которое зависит только от ранее определенных наборов и параметров.
Изменение значений параметров. Команда let
for{a in 1..A}{ for{m in 1..Meses}{ let CMg[a,m]:= 0;} }
Можно использовать оператор присваивания вместо цикла для присвоения значения CMg.
let {a in 1..A, b in 1..B,g in 1..G,m in 1..Meses} CMg[a,m]:= if(<condition>) then CV[g] else 0;
Когда AMPL находит цикл for в файле данных, он автоматически переключается в режим model для обработки команд for и let. И если после конструкции let указать BETA:= 0.2; тогда AMPL выдаст сообщение об ошибке, потому что параметр BETA был ранее определен в модели.
Чтобы решить эту проблему, необходимо снова переключить AMPL в режим данных, поместив data; перед param BETA:= 0.2;. В качестве альтернативы циклы можно переместить в конец файла данных после всех операторов данных.