Команда display использует несколько простых правил для выбора правильного расположения данных. Изменяя несколько параметров, можно управлять общим видом, обработкой нулевых значений и шириной зоны вывода. Эти параметры приведены в таблице. Значения по умолчанию указаны в скобках.
display_1col | максимальное количество элементов для таблицы в формате списка (20) |
display_transpose | транспонирование таблицы, если строки - столбцы <display_transpose (0) |
display_width | максимальная ширина линии (79) |
gutter_width | количество пробелов разделяющих столбцы таблицы (3) |
omit_zero_cols | если не 0, подавление всех нулевых столбцов (0) |
omit_zero_rows | если не 0, подавление всех нулевые строк (0) |
Расположение списков и таблиц
Отображение одномерного параметра или переменной может привести к очень длинному списку:
display required; required [*] := Fri1 100 Fri2 78 Fri3 52 Mon1 100 ... Wed3 52 ;
В этом случае, опция display_1col может использоваться для запроса более компактного формата:
option display_1col 0; display required; required [*] := Fri1 100 Mon1 100 Sat1 100 Thu2 78 Tue2 78 Wed2 78 Fri2 78 Mon2 78 Sat2 78 Thu3 52 Tue3 52 Wed3 52 Fri3 52 Mon3 52 Thu1 100 Tue1 100 Wed1 100 ;
По умолчанию AMPL использует формат списка в один столбец, когда число отображаемых значений меньше или равно display_1col. В противном случае используется табличный формат. Значение по умолчанию для display_1col - 20. Установление его значения в ноль приведет к принудительному табличному формату. Установление большого числа приведет к принудительному формату списка. Многомерное отображение зависит от опции display_1col. Формат списка в один столбец используется, когда число значений меньше или равно display_1col, а соответствующий формат таблицы - используется в противном случае. Ранее был представлен пример, в котором supply отображался в виде списка, потому что имел только 9 значений. В то время как отображение demand появилось в виде таблицы, потому что имелось 21 значение (превышает значение по умолчанию 20 для опции display_1col).
Поскольку параметр или переменная, индексированная для набора упорядоченных пар, считаются двумерными, значение display_1col также влияет на его отображение. Ниже представлена таблица значений для параметра cost, индексированная для набора LINKS :
option display_1col 0; display cost; cost [*,*] (tr) : CLEV GARY PITT := DET 9 14 . FRA 27 . 24 FRE . . 99 LAF 17 8 . LAN 12 11 . STL 26 16 28 WIN 9 . 13 ;
Точка (.) указывает на несуществующую комбинацию в наборе индексов. Таким образом, в столбце GARY таблицы есть точка в строке FRA, поскольку пара (GARY, FRA) не является элементом LINKS. Для этой проблемы не определена cost["GARY", "FRA"]. С другой стороны, LINKS содержит пару (GARY, LAF) и cost["GARY","LAF"].
При выборе ориентации для таблиц команда display по умолчанию предпочитает строки столбцам. То есть, если число столбцов превысит количество строк, таблица будет транспонирована. Транспонированная таблица обозначается записью (tr) в первой строке.
Статус транспонирования таблицы можно изменить, изменив значение display_transpose. Положительные значения приводят к транспонированию:
option display_transpose 5; display demand; demand [*,*] (tr) : DET FRA FRE LAF LAN STL WIN := bands 300 300 225 250 100 650 75 coils 750 500 850 500 400 950 250 plate 100 100 100 250 0 200 50 ;
в то время как отрицательные значения имеют тенденцию подавлять транспонирование:
option display_transpose -5; display cost; cost [*,*] : DET FRA FRE LAF LAN STL WIN := CLEV 9 27 . 17 12 26 9 GARY 14 . . 8 11 16 . PITT . 24 99 . . 28 13 ;
Правило следующее: таблица транспонируется только тогда, когда количество строк минус число столбцов будет меньше, чем display_transpose. При значении по умолчанию, равном нулю, display_transpose показывает ранее описанное поведение по умолчанию.
Контроль ширины вывода
Опция display_width обозначает максимальное количество символов в строке, сгенерированной командой display:
option display_width 50, display_1col 0; display required; required [*] := Fri1 100 Mon3 52 Thu3 52 Wed2 78 Fri2 78 Sat1 100 Tue1 100 Wed3 52 Fri3 52 Sat2 78 Tue2 78 Mon1 100 Thu1 100 Tue3 52 Mon2 78 Thu2 78 Wed1 100 ;
Когда таблица будет шире, чем display_width, она будет разрезана по вертикали на две или более таблиц. Имена строк в каждой таблице будут одинаковы, а столбцы разные:
option display_width 50; display cost; cost [*,*] : C118 C138 C140 C246 C250 C251 D237 D239 := Coullard 6 9 8 7 11 10 4 5 Daskin 11 8 7 6 9 10 1 5 Hazen 9 10 11 1 5 6 2 7 Hopp 11 9 8 10 6 5 1 7 Iravani 3 2 8 9 10 11 1 5 Linetsky 11 9 10 5 3 4 6 7 Mehrotra 6 11 10 9 8 7 1 2 Nelson 11 5 4 6 7 8 1 9 Smilowitz 11 9 10 8 6 5 7 3 Tamhane 5 6 9 8 4 3 7 10 White 11 9 8 4 6 5 3 10 ; : D241 M233 M239 := Coullard 3 2 1 Daskin 4 2 3 Hazen 8 3 4 Hopp 4 2 3 Iravani 4 6 7 Linetsky 8 1 2 Mehrotra 5 4 3 Nelson 10 2 3 Smilowitz 4 1 2 Tamhane 11 2 1 White 7 2 1 ;
Если заголовки столбцов таблицы намного шире, чем значения, display вводит сокращения, чтобы объединить все столбцы:
option display_width 40; display {p in PROD, t in 1..T} (revenue[p,t]*Sell[p,t], prodcost[p]*Make[p,t], invcost[p]*Inv[p,t]); # $1 = revenue[p,t]*Sell[p,t] # $2 = prodcost[p]*Make[p,t] # $3 = invcost[p]*Inv[p,t] : $1 $2 $3 := bands 1 150000 59900 0 bands 2 156000 60000 0 bands 3 37800 14000 0 bands 4 54000 20000 0 coils 1 9210 15477 3300 coils 2 87500 15400 0 coils 3 129500 38500 0 coils 4 163800 46200 0 ;
С другой стороны, если заголовки уже, чем значения, тогда можно сжать строки, уменьшив параметр gutter_width - количество пробелов между столбцами - со значения по умолчанию 3 до 2 или 1.
Подавление нулей
В некоторых видах линейных программ, которые имеют гораздо больше переменных, чем ограничений, большинство переменных имеют оптимальное значение равное нулю. В следующем примере оптимальные значения всех переменных отображаются в виде таблицы:
display Trans; Trans [*,*] : C118 C138 C140 C246 C250 C251 D237 D239 D241 M233 M239 := Coullard 1 0 0 0 0 0 0 0 0 0 0 Daskin 0 0 0 0 0 0 0 0 1 0 0 ... White 0 0 0 0 0 0 0 0 0 0 1 ; display Trans; Trans [*,*] : C118 C138 C140 C246 C250 C251 D237 D239 D241 M233 M239 := Coullard 1 0 0 0 0 0 0 0 0 0 0 Daskin 0 0 0 0 0 0 0 0 1 0 0 ... White 0 0 0 0 0 0 0 0 0 0 1 ;
Устанавливая для omit_zero_rows значение 1, все нулевые значения подавляются, и список сводится к интересующим нас элементам:
option omit_zero_rows 1; display Trans; Trans := Coullard C118 1 Daskin D241 1 ... White M239 1 ; option omit_zero_rows 1; display Trans; Trans := Coullard C118 1 Daskin D241 1 ... White M239 1 ;
Если число ненулевых записей меньше значения display_1col, данные выводятся в виде списка. Если число ненулевых значений больше display_1col, будет использоваться формат таблицы, а опция omit_zero_rows будет подавлять только те строки таблицы, которые содержат все нулевые записи. Например, отображение трехмерной переменной Trans, представленной ранее будет сжато следующим образом:
display Trans; Trans [CLEV,*,*]: bands coils plate := DET 0 750 0 LAF 0 500 0 LAN 0 400 0 STL 0 50 0 WIN 0 250 0 |
[GARY,*,*]: bands coils plate := FRE 225 850 100 LAF 250 0 0 STL 650 900 200 |
[PITT,*,*] : bands coils plate := DET 300 0 100 FRA 300 500 100 LAF 0 0 250 LAN 100 0 0 WIN 75 0 50 ; |
Соответствующая опция omit_zero_cols подавляет все нулевые столбцы, если установлено значение 1, и исключает два столбца из Trans[CLEV,*,*].
Дополнительные параметры display
Числа в таблице или списке, отображаемые командой display, являются результатом преобразования внутреннего числового представления компьютера в строку цифр и символов. Настройки AMPL для влияющие на отображение представлены в следующей таблице:
display_eps | наименьшая отображаемая величина (0) |
display_precision | цифры точности, до которых округляются отображаемые числа. Полная точность (0) |
display_round | цифры слева или (если отрицательное) справа от десятичного знака, до которого округляются отображаемые числа, переопределяя display_precision ("") |
solution_precision | цифры точности, до которых округляются значения решения. Полная точность, если 0 (0) |
solution_round | цифры слева или (если отрицательное) справа от десятичного знака, до которого округляются значения решения, переопределяя solution_precision ("") |
Внешний вид числовых значений
По умолчанию display отображает каждое числовое значение с одинаковым количеством значащих цифр:
display {p in PROD, t in 1..T} Make[p,t]/rate[p]; Make[p,t]/rate[p] [*,*] (tr) : bands coils := 1 29.95 10.05 2 30 10 3 20 12 4 32.1429 7.85714 ; display {p in PROD, t in 1..T} prodcost[p]*Make[p,t]; prodcost[p]*Make[p,t] [*,*] (tr) : bands coils := 1 59900 15477 2 60000 15400 3 40000 18480 4 64285.7 12100 ;
По умолчанию используется шесть значащих цифр, независимо от того, какой результат получится: 7,85714 или 64285,7. В некоторых случаях кажется, что некоторые числа имеют меньше цифр, но только потому, что конечные нули были удалены. Например, 29,95 представляет собой число, которое равно 29,9500 или шести цифрам, а 59900 представляет 59900,0.
Изменив параметр display_precision на значение, отличное от шести, можно изменить количество отображаемых значащих цифр:
option display_precision 3; display Make[’bands’,4] / rate[’bands’], prodcost[’bands’] * Make[’bands’,4]; Make[’bands’,4]/rate[’bands’] = 32.1 prodcost[’bands’]*Make[’bands’,4] = 64300 option display_precision 9; display Make[’bands’,4] / rate[’bands’], prodcost[’bands’] * Make[’bands’,4]; Make[’bands’,4]/rate[’bands’] = 32.1428571 prodcost[’bands’]*Make[’bands’,4] = 64285.7143 option display_precision 0; display Make[’bands’,4] / rate[’bands’], prodcost[’bands’] * Make[’bands’,4]; Make[’bands’,4]/rate[’bands’] = 32.14285714285713 prodcost[’bands’]*Make[’bands’,4] = 64285.71428571427
В последнем примере display_precision 0 отображение результата происходит с максимальной точностью. Для этого используется сколько необходимо цифр. (Точнее, отображаемое число является кратчайшим десятичным представлением, которое при правильном округлении до вывода на экран представлено в компьютере.)
Отображение с заданной точностью предоставляют одинаковую степень полезной информации о каждом числе, но они могут выглядеть неровными из-за разного количества цифр после десятичной точки. Чтобы указать округление до фиксированного числа десятичных разрядов, независимо от получаемой точности, нужно установить параметр display_round. Неотрицательное значение указывает количество цифр после десятичной точки:
option display_round 2; display {p in PROD, t in 1..T} Make[p,t]/rate[p]; Make[p,t]/rate[p] [*,*] (tr) : bands coils := 1 29.95 10.05 2 30.00 10.00 3 20.00 12.00 4 32.14 7.86 ;
Отрицательное значение указывает на округление до десятичной точки. Например, когда display_round равен –2, все числа округляются до сотен:
option display_round -2; display {p in PROD, t in 1..T} prodcost[p]*Make[p,t]; prodcost[p]*Make[p,t] [*,*] (tr) : bands coils := 1 59900 15500 2 60000 15400 3 40000 18500 4 64300 12100 ;
Любое целочисленное значение display_round переопределяет эффект display_precision.
Чтобы отключить display_round, необходимо установить для него некоторое нецелое число, например пустую строку ''. В зависимости от используемого решателя, можно обнаружить, что некоторые значения решения, которые должны быть равны нулю, не всегда оказываются такими. Для примера рассмотрим один из отчетов решателя назначения:
option omit_zero_rows 1; display{i in ORIG, j in DEST} cost[i,j] * Trans[i,j]; cost[i,j]*Trans[i,j] := Coullard C118 6 Coullard D241 2.05994e-17 Daskin D237 1 Hazen C246 1 Hopp D237 6.86647e-18 Hopp D241 4 ... 9 lines omitted White C246 2.74659e-17 White C251 -3.43323e-17 White M239 1 ;
Мельчайшие значения, такие как 6,86647e–18 и –3,43323e–17, не имеют значения представляя решение этой задачи. Они являются нулями в точном решении, но получаются немного отличными от нуля как артефакт способа взаимодействия алгоритма решателя с компьютерным представлением чисел. Чтобы избежать отображения этих бесконечно малых чисел с бессмысленной точностью, нужно задать разумное значение для настройки display_round - в данном случае 0, поскольку после десятичной точки нет интересующих цифр:
option display_round 0; display {i in ORIG, j in DEST} cost[i,j] * Trans[i,j];
cost[i,j]*Trans[i,j] := Coullard C118 6 Coullard D241 0 Daskin D237 1 Hazen C246 1 Hopp D237 0 Hopp D241 4 Iravani C118 0 Iravani C138 2 Linetsky C250 3 |
Mehrotra D239 2 Nelson C138 0 Nelson C140 4 Smilowitz M233 1 Tamhane C118 -0 Tamhane C251 3 White C246 0 White C251 -0 White M239 1 ; |
Маленькие числа теперь представлены в виде 0, если положительны, или -0, если отрицательны. Что бы полностью подавить появление в результатах предельно малых чисел и их округленных значений, нужно задать соответствующее значение для параметра display_eps:
option display_eps 1e-10; display {i in ORIG, j in DEST} cost[i,j] * Trans[i,j];
cost[i,j]*Trans[i,j] := Coullard C118 6 Coullard D241 0 Daskin D237 1 Hazen C246 1 Hopp D237 0 Hopp D241 4 Iravani C118 0 Iravani C138 2 Linetsky C250 3 |
Mehrotra D239 2 Nelson C138 0 Nelson C140 4 Smilowitz M233 1 Tamhane C118 -0 Tamhane C251 3 White C246 0 White C251 -0 White M239 1 ; |
Любое значение, величина которого меньше значения display_eps, считается точным нулем во всех отображениях данных команды display.
Округление значений решения
Параметры display_precision, display_round и display_eps влияют только на отображение чисел, а не на их фактические значения. Это можно увидеть, если отобразить набор всех пар i in ORIG и j in DEST, которые имеют положительное значение в предыдущей таблице, сравнивая cost[i,j] * Trans[i,j] с 0:
display {i in ORIG, j in DEST: cost[i,j]*Trans[i,j] > 0}; set {i in ORIG, j in DEST: cost[i,j]*Trans[i,j] > 0} := (Coullard,C118) (Iravani,C118) (Smilowitz,M233) (Coullard,D241) (Iravani,C138) (Tamhane,C251) (Daskin,D237) (Linetsky,C250) (White,C246) (Hazen,C246) (Mehrotra,D239) (White,M239) (Hopp,D237) (Nelson,C138) (Hopp,D241) (Nelson,C140);
Даже при том, что значение, например 2.05994e–17, рассматривается как ноль при отображении, его значение для модели больше нуля. Можно решить эту проблему, изменив условие с > 0, скажем, на >0.1. В качестве альтернативы можно установить опцию solution_round, чтобы AMPL округлял значения решения с разумной точностью, когда они получены от решателя:
option solution_round 10; solve; MINOS 5.5: optimal solution found. 40 iterations, objective 28 display {i in ORIG, j in DEST: cost[i,j]*Trans[i,j] > 0}; set {i in ORIG, j in DEST: cost[i,j]*Trans[i,j] > 0} := (Coullard,C118) (Iravani,C138) (Smilowitz,M233) (Daskin,D237) (Linetsky,C250) (Tamhane,C251) (Hazen,C246) (Mehrotra,D239) (White,M239) (Hopp,D241) (Nelson,C140) ;
Параметры solution_precision и solution_round работают так же, как display_precision и display_round, за исключением того, что они применяются только к значениям решения при возвращении его из решателя и постоянно изменяют значения, а не только их внешнее отображение.
Округленные значения могут иметь значение, даже если они не близки к нулю. В качестве примера, мы сначала используем несколько опций display, чтобы получить компактный список дробного решения для модели планирования:
model sched.mod; data sched.dat; solve; MINOS 5.5: optimal solution found. 19 iterations, objective 265.6 option display_width 60; option display_1col 5; option display_eps 1e-10; option omit_zero_rows 1; display Work; Work [*] := 10 28.8 30 14.4 71 35.6 106 23.2 123 35.6 18 7.6 35 6.8 73 28 109 14.4 24 6.8 66 35.6 87 14.4 113 14.4 ;
Каждое значение Work[j] представляет количество работников, назначенных для графика j. Можно получить быстрый практический график, округлив дробные значения до следующего наибольшего целого числа. Используя функцию ceil для округления, мы видим, что общее количество рабочих должно составлять:
display sum {j in SCHEDS} ceil(Work[j]); sum{j in SCHEDS} ceil(Work[j]) = 273
Если скопировать числа из предыдущей таблицы и округлить их вручную, тогда можно обнаружить, что результатом их суммирования является значение 271. Источник изменения значения можно увидеть, отобразив числа с полной точностью:
option display_eps 0; option display_precision 0; display Work; Work [*] := 10 28.799999999999997 73 28.000000000000018 18 7.599999999999998 87 14.399999999999995 24 6.79999999999999 95 -5.876671973951407e-15 30 14.40000000000001 106 23.200000000000006 35 6.799999999999995 108 4.685288280240683e-16 55 -4.939614313857677e-15 109 14.4 66 35.6 113 14.4 71 35.599999999999994 123 35.59999999999999 ;
Часть проблемы связана с крошечной положительной величиной Work[108], которая была округлена до 1. Другая чать связана с Work[73]. В точном решении его значение равно 28, однако оно возвращается из решателя со значением 28.000000000000018, поэтому округляется до 29. Самый простой способ убедиться, что арифметика работает правильно в этом случае, это снова установить параметр solution_round, прежде чем решить:
option solution_round 10; solve; MINOS 5.5: optimal solution found. 19 iterations, objective 265.6 display sum {j in SCHEDS} ceil(Work[j]); sum{j in SCHEDS} ceil(Work[j]) = 271
Мы выбрали значение 10 для solution_round, потому что было замечено, что небольшие неточности в значениях решателя произошли далеко за десятой цифрой после десятичного знака (запятой).
Эффект solution_round или solution_precision применяется ко всем значениям, возвращаемым решателем. Чтобы изменить только определенные значения, необходимо использовать команду присваивания let, вместе с функциями округления.