Многие проекты моделирования включают в себя решение ряда проблемных случаев, каждый из которых определяется своими данными. Мы опишем здесь возможности AMPL для сброса значений параметров, оставляя модель не тронутой. Они включают в себя команды для сброса ввода в режиме данных и повторной выборки случайных параметров, а также команду let для непосредственного назначения новых значений.
Сброс модели
Чтобы удалить текущие данные для нескольких компонентов модели, не изменяя саму текущую модель, используйте команду reset data с указанием имени наборов, параметров для которых необходимо удалить данные:
reset data MINREQ, MAXREQ, amt, n_min, n_max;
После удаления старых данных, можно использовать команды чтения новых значений для наборов и параметров. Чтобы удалить все данные, нужно ввести команду reset data без указания каких-либо имен.
Команда update data работает аналогично, за исключением того, что она не удаляет данные до тех пор, пока не будут назначены новые значения. Таким образом, если ввести:
update data MINREQ, MAXREQ, amt, n_min, n_max;
а после этого прочитать новые значения для MINREQ, amt и n_min, значения для MAXREQ и n_max останутся прежними. Если вместо этого, использовать reset data, пользователь получит сообщение об ошибке при попытке решить модель, т.к. все данные были удалены командой reset data.
Новая случайная выборка
Команда reset data также действует для повторной выборки случайно вычисленных параметров. Продолжая с вариантом модели steel4.mod, если значение параметра avail задается случайной функцией, мы получим:
param avail_mean {STAGE} >= 0; param avail_variance {STAGE} >= 0; param avail {s in STAGE} = Normal(avail_mean[s], avail_variance[s]);
с соответствующими данными:
param avail_mean := reheat 35 roll 40 ; param avail_variance := reheat 5 roll 2 ;
AMPL будет брать новые выборки из нормального распределения после каждого сброса данных. Разные начальные данные приводят к разным решениям и, следовательно, к различным оптимальным значениям целевой функции:
model steel4r.mod; data steel4r.dat; solve; MINOS 5.5: optimal solution found. 3 iterations, objective 187632.2489 display avail; reheat 32.3504 roll 43.038 ;
reset data avail; solve; MINOS 5.5: optimal solution found. 4 iterations, objective 158882.901 display avail; reheat 32.0306 roll 32.6855 ;
Только команда reset data имеет эффект сброса генератора случайных значений с назначением новой случайной выборки. Команда reset, приводит к сбросу генератора случайных чисел AMPL, однако новая случайная выборка не будет назначена. Поэтому, после использования reset новая выборка будет повторять прежний порядок генерации. Например:
param alpha = ceil(Uniform(1,5)); param beta = ceil(Uniform(0,5)); param deltaSOC = 0.4 + round(Uniform01()) * 0.07;
Когда мы отображаем значения этих параметров, мы видим случайно сгенерированные значения:
display alpha, beta, deltaSOC; alpha = 4 beta = 1 deltaSOC = 0.47
После каждого reset data значения обновляются новой случайной выборкой:
reset data alpha, beta, deltaSOC; display alpha, beta, deltaSOC; alpha = 5 beta = 1 deltaSOC = 0.47 reset data alpha, beta, deltaSOC; display alpha, beta, deltaSOC; alpha = 4 beta = 2 deltaSOC = 0.4
Команда let
Команда let также позволяет изменять значения данных, оставляя модель неизменной. Команду let удобно использовать для небольших или легко описываемых изменений. Например, с помощью let можно провести анализ чувствительности решения к изменению значений верхней границы f_max["CHK"] при решении модели диеты:
model dietu.mod; data dietu.dat; solve; MINOS 5.5: optimal solution found. 5 iterations, objective 74.27382022 let f_max["CHK"] := 11; solve; MINOS 5.5: optimal solution found. 1 iterations, objective 73.43818182 let f_max["CHK"] := 12; solve; MINOS 5.5: optimal solution found. 0 iterations, objective 73.43818182
Данные показывают, что ослабление границы f_max["CHK"], приводит к небольшому улучшению результата и снижает итоговую стоимость. Однако, дальнейшее ослабление границ, не приносит пользы. Индексное выражение может быть дано после ключевого слова let, и в этом случае изменение вносится для каждого члена указанного набора индексации. Можно использовать эту функцию, чтобы изменить верхнюю границу f_max для всех j на значение 8:
let{j in FOOD} f_max[j] := 8;
Или увеличить все значения верхних границ на 10 процентов:
let{j in FOOD} f_max[j] := 1.1 * f_max[j];
Обычно, эта команда состоит из ключевого слова let, индексированного выражения, если это необходимо, и оператора присваивания :=. Любой набор или параметр, объявление которых не определяет его с помощью фразы =, может быть указан слева от оператора присваивания :=, а справа может появиться любое подходящее выражение, которое в данный момент может быть оценено.
Например, если определить ARCS с помощью фразы «=»:
param n; set X = 1..n ordered; set DONORS = 1..2*n ordered; param ARCS {DONORS, X} = floor(Uniform(0,2));
… has an = assignment in the model.
если в последствии появится необходимость изменить значение параметра ARCS и пользователь попытается выполнить инструкцию let ARCS[i,j]:=0;, AMPL сгенерирует ошибку, подобную этой:
ARCS has an = assignment in the model.
Что бы избежать появления ошибки при изменении данных, необходимо объявить ARCS следующим образом:
param ARCS {DONORS, X};
а позже использовать let для инициализации значений ARCS:
let {j in X, i in DONORS} ARCS[i,j] := floor(Uniform(0,2)); for {j in X, i in DONORS} {...изменение значений ARCS...} { if (ord(i) mod 2 = 1 and ord(j) = ceil(ord(i)/2)) then if (ARCS[i, j] = ARCS[i+1, j] and ARCS[i, j] = 1) then { if Uniform(0,1) <= 0.5 then let ARCS[i, j] := 0; else let ARCS[i+1, j] := 0; }; };
AMPL не накладывает никаких ограничений на то, что можно изменить с помощью let. Однако, пользователю необходимо аккуратно изменять наборы или параметры, которые влияют на индексирование других данных. Например, после решения задачи многопериодного производства:
set PROD; # продукты param T > 0; # количество недель param rate {PROD} > 0; # тонн в час. param inv0 {PROD} >= 0; # начальные запасы param avail {1..T} >= 0; # доступное количество времени на неделе param market {PROD,1..T} >= 0; # обязательства по продажам каждую недулю param prodcost {PROD} >= 0; # стоимость 1 тонны продукции param invcost {PROD} >= 0; # стоимость содержания запасов param revenue {PROD,1..T} >= 0; # доход от продажи 1 тонны продукции var Make {PROD,1..T} >= 0; # произведено, тонн var Inv {PROD,0..T} >= 0; # тонн на складах var Sell {p in PROD, t in 1..T} >= 0, <= market[p,t]; # tons sold maximize Total_Profit: sum {p in PROD, t in 1..T} (revenue[p,t]*Sell[p,t] - prodcost[p]*Make[p,t] - invcost[p]*Inv[p,t]); # Total revenue less costs in all weeks subject to Time {t in 1..T}: sum {p in PROD} (1/rate[p]) * Make[p,t] <= avail[t]; # Total of hours used by all products # may not exceed hours available, in each week subject to Init_Inv {p in PROD}: Inv[p,0] = inv0[p]; # Initial inventory must equal given value subject to Balance {p in PROD, t in 1..T}: Make[p,t] + Inv[p,t-1] = Sell[p,t] + Inv[p,t]; param T := 4; set PROD := bands coils; param avail := 1 40 2 40 3 32 4 40 ; param rate := bands 200 coils 140 ; param inv0 := bands 10 coils 0 ; param prodcost := bands 10 coils 11 ; param invcost := bands 2.5 coils 3 ; param revenue: 1 2 3 4 := bands 25 26 27 27 coils 30 35 37 39 ; param market: 1 2 3 4 := bands 6000 6000 4000 6500 coils 4000 2500 3500 4200 ;
Динамический набор данных
Может возникнуть идея, изменить количество недель T с 4 (как указано в исходных данных) на 3:
let T:= 3; solve; Error executing "solve" command: error processing param avail: invalid subscript avail[4] discarded. error processing param market: 2 invalid subscripts discarded: market[’bands’,4] market[’coils’,4] error processing param revenue: 2 invalid subscripts discarded: revenue[’bands’,4] revenue[’coils’,4] error processing var Sell[’coils’,1]: invalid subscript market[’bands’,4]
Однако, нужно понимать, что AMPL по-прежнему имеет текущие данные для параметров 4-й недели, таких как avail[4], которые стали недействительными при изменении T на 3. Что бы правильно уменьшить количество недель в линейной программе, при использовании одних и те же данных, необходимо объявить два параметра:
param Tdata integer > 0; param T integer <= Tdata;
Использовать 1..Tdata для индексации в объявлениях параметров, и сохранение при этом 1..T для переменных, цели и ограничений, позволяют использовать let для изменения T.
Также можно использовать команду let для изменения текущих значений переменных. Это удобно при изучении альтернативного решения. Например, вот что происходит, когда мы выбираем оптимальную диету, как описано выше, затем округляем оптимальное решение до следующего наименьшего целого числа пакетов:
model dietu.mod; data dietu.dat; solve; MINOS 5.5: optimal solution found. 5 iterations, objective 74.27382022
let {j in FOOD} Buy[j] := floor(Buy[j]); display Total_Cost, n_min, Diet_Min.slack;; Total_Cost = 70.8 : n_min Diet_Min.slack := A 700 231 B1 0 580 B2 0 475 C 700 -40 CAL 16000 -640 ;
Поскольку мы использовали let для изменения значений переменных, цель и слабина автоматически вычисляются из новых округленных значений. Стоимость снизилась примерно на 3,5 долл. Однако, полученное решение не соответствует требованиям для C почти на 6%, а для CAL - на 4%.
AMPL предоставляет множество функций округления, которые можно использовать таким образом.
Таблица 1. Функции округления
Наименование |
English |
Описание |
ceil(x) | ceiling of x (next higher integer) | следующее более высокое целое число |
floor(x) | floor of x (next lower integer) | следующее нижнее целое |
precision(x,n) | x rounded to n significant digits | х округляется до n значащих цифр |
round(x, n) | x rounded to n digits past the decimal point | х округляется до n цифр после десятичной точки |
round(x) | x rounded to the nearest integer | х округляется до ближайшего целого |
trunc(x, n) | x truncated to n digits past the decimal point | x усекается до n цифр после десятичной точки |
trunc(x) | x truncated to an integer (fractional part dropped) | х усечено до целого числа (дробная часть отброшена) |