Создайте начальную точку для оптимизации с именованными индексными переменными

В этом примере показано, как создать начальную точку для задачи оптимизации, которая имеет именованные индексные переменные. Для именованных индексных переменных часто самый легкий способ задать начальную точку - это использовать findindex функция.

Проблема заключается в многопериодной инвентаризационной проблеме, которая включает смешивание необработанных и рафинированных масел. Цель заключается в максимальном увеличении прибыли с учетом различных ограничений на производственные и товарно-материальные мощности и на «твердость» нефтяных смесей. Эта задача взята из Williams [1].

Описание задачи

Проблема связана с двумя видами необработанного растительного масла и тремя видами необработанного неведомого масла, которое производитель может перерабатывать в пищевое масло. Производитель может дорабатывать до 200 тонн растительных масел, и до 250 тонн нежестких масел в месяц. Производитель может хранить 1000 тонн каждого сырого масла, что выгодно, потому что стоимость приобретения сырого масла зависит как от месяца, так и от типа масла. Качество, называемое «твердость», связано с каждым маслом. Твердость смешанного масла является линейно взвешенной твердостью составных масел.

Из-за ограничений по переработке производитель ограничивает количество масел, очищенных за любой один месяц, не более чем тремя. Кроме того, если нефть очищается за месяц, то необходимо очистить не менее 20 тонн этой нефти. Наконец, если растительное масло рафинировано через месяц, то необходимо также рафинировать не вегетируемое масло 3.

Выручка является постоянной для каждой тонны проданной нефти. Затраты составляют стоимость приобретения масел, которая варьируется в зависимости от нефти и месяца, и существует фиксированная стоимость тонны хранения каждой нефти за каждый месяц. Затрат на переработку масла нет, но производитель не может хранить рафинированное масло (оно должно быть продано).

Введите данные задачи

Создайте именованные индексные переменные для периодов планирования и масел.

months = {'January','February','March','April','May','June'};
oils = {'veg1','veg2','non1','non2','non3'};
vegoils = {'veg1','veg2'};
nonveg = {'non1','non2','non3'};

Создайте переменные с параметрами хранения и использования.

maxstore = 1000; % Maximum storage of each type of oil
maxuseveg = 200; % Maximum vegetable oil use
maxusenon = 250; % Maximum nonvegetable oil use
minuseraw = 20; % Minimum raw oil use
maxnraw = 3; % Maximum number of raw oils in a blend
saleprice = 150; % Sale price of refined and blended oil
storecost = 5; % Storage cost per period per oil quantity
stockend = 500; % Stock at beginning and end of problem
hmin = 3; % Minimum hardness of refined oil
hmax = 6; % Maximum hardness of refined oil

Задайте твердость необработанных масел в качестве этого вектора.

h = [8.8,6.1,2,4.2,5.0];

Укажите стоимость необработанных масел в качестве этого массива. Каждая строка массива представляет стоимость сырого масла за месяц. Первая строка представляет затраты в январе, а последняя - затраты в июне.

costdata = [...
110 120 130 110 115
130 130 110  90 115
110 140 130 100  95
120 110 120 120 125
100 120 150 110 105
 90 100 140  80 135];

Создание переменных

Создайте следующие переменные задачи:

  • продать, количество каждой нефти продается каждый месяц

  • store, количество каждого масла, хранящегося в конце каждого месяца

  • buy, количество каждого масла, закупаемого каждый месяц

Кроме того, чтобы учесть ограничения на количество нефтей, очищенных и проданных каждый месяц, и минимальное количество произведенного, создайте вспомогательную двоичную переменную induse то есть 1 именно тогда, когда масло продается за месяц.

sell = optimvar('sell', months, oils, 'LowerBound', 0);
buy = optimvar('buy', months, oils, 'LowerBound', 0);
store = optimvar('store', months, oils, 'LowerBound', 0, 'UpperBound', maxstore);

induse = optimvar('induse', months, oils, 'Type', 'integer', ...
    'LowerBound', 0, 'UpperBound', 1);

Назовите общее количество нефти, проданной каждый месяц produce.

produce = sum(sell,2);

Создание цели

Чтобы создать целевую функцию для задачи, вычислите доход и вычитайте затраты на закупку и хранение масел.

Создайте задачу оптимизации для максимизации и включите целевую функцию в качестве Objective свойство.

prob = optimproblem('ObjectiveSense', 'maximize');
prob.Objective = sum(saleprice*produce) - sum(sum(costdata.*buy)) - sum(sum(storecost*store));

Выражение цели довольно длинное. Если вам нравится, вы можете увидеть это с помощью showexpr(prob.Objective) команда.

Создайте ограничения

Задача имеет несколько ограничений, которые вы должны задать.

Количество каждой нефти, хранившейся в июне, составляет 500. Установите это ограничение с помощью нижней и верхней границ.

store('June', :).LowerBound = 500;
store('June', :).UpperBound = 500;

Производитель не может дорабатывать более maxuseveg растительное масло в любой месяц. Установите это и все последующие ограничения с помощью Выражений для ограничений и уравнений.

vegoiluse = sell(:, vegoils);
vegused = sum(vegoiluse, 2) <= maxuseveg;

Производитель не может дорабатывать более maxusenon нежесткое масло в любой месяц.

nonvegoiluse = sell(:,nonveg);
nonvegused = sum(nonvegoiluse,2) <= maxusenon;

Твердость смешанного масла должна быть от hmin through hmax.

hardmin = sum(repmat(h, 6, 1).*sell, 2) >= hmin*produce;
hardmax = sum(repmat(h, 6, 1).*sell, 2) <= hmax*produce;

Количество каждой нефти, хранимой в конце месяца, равно сумме в начале месяца, плюс сумма купленной, минус сумма реализованной.

initstockbal = 500 + buy(1, :) == sell(1, :) + store(1, :);
stockbal = store(1:5, :) + buy(2:6, :) == sell(2:6, :) + store(2:6, :);

Если масло вообще очищается за месяц, по крайней мере minuseraw нефти должны быть переработаны и проданы.

minuse = sell >= minuseraw*induse;

Убедитесь, что induse переменная равна 1, когда соответствующее масло очищено.

maxusev = sell(:, vegoils) <= maxuseveg*induse(:, vegoils);
maxusenv = sell(:, nonveg) <= maxusenon*induse(:, nonveg);

Производитель может продать не более maxnraw масла каждый месяц.

maxnuse = sum(induse, 2) <= maxnraw;

Если растительное масло рафинировано, масло non3 также должны быть уточнены и проданы.

deflogic1 = sum(induse(:,vegoils), 2) <= induse(:,'non3')*numel(vegoils);

Включите в выражения в задаче ограничений.

prob.Constraints.vegused = vegused;
prob.Constraints.nonvegused = nonvegused;
prob.Constraints.hardmin = hardmin;
prob.Constraints.hardmax = hardmax;
prob.Constraints.initstockbal = initstockbal;
prob.Constraints.stockbal = stockbal;
prob.Constraints.minuse = minuse;
prob.Constraints.maxusev = maxusev;
prob.Constraints.maxusenv = maxusenv;
prob.Constraints.maxnuse = maxnuse;
prob.Constraints.deflogic1 = deflogic1;

Решите задачу

Чтобы показать возможное различие между использованием начальной точки и отказом от нее, установите опции, чтобы не использовать эвристику. Затем решите проблему.

opts = optimoptions('intlinprog','Heuristics','none');
[sol1,fval1,exitstatus1,output1] = solve(prob,'options',opts)
Solving problem using intlinprog.
LP:                Optimal objective value is -1.075130e+05.                                        

Cut Generation:    Applied 41 Gomory cuts, 2 cover cuts,                                            
                   1 mir cut, and 1 clique cut.                                                     
                   Lower bound is -1.047522e+05.                                                    

Branch and Bound:

   nodes     total   num int        integer       relative                                          
explored  time (s)  solution           fval        gap (%)                                         
      19      0.12         1  -7.190185e+04   4.535003e+01                                          
      25      0.14         2  -7.205000e+04   4.505117e+01                                          
      25      0.14         3  -8.290000e+04   2.606702e+01                                          
      29      0.15         4  -8.775370e+04   1.909426e+01                                          
      40      0.15         5  -9.937870e+04   5.163142e+00                                          
      91      0.19         6  -9.987222e+04   4.643483e+00                                          
     105      0.19         7  -1.002139e+05   4.286718e+00                                          
     630      0.38         8  -1.002787e+05   2.283810e+00                                          
    1112      0.51         8  -1.002787e+05   0.000000e+00                                          

Optimal solution found.

Intlinprog stopped because the objective value is within a gap tolerance of the
optimal value, options.AbsoluteGapTolerance = 0 (the default value). The intcon
variables are integer within tolerance, options.IntegerTolerance = 1e-05 (the
default value).
sol1 = struct with fields:
       buy: [6x5 double]
    induse: [6x5 double]
      sell: [6x5 double]
     store: [6x5 double]

fval1 = 1.0028e+05
exitstatus1 = 
    OptimalSolution

output1 = struct with fields:
        relativegap: 0
        absolutegap: 0
      numfeaspoints: 8
           numnodes: 1112
    constrviolation: 1.1867e-11
            message: 'Optimal solution found....'
             solver: 'intlinprog'

Использование начальной точки

Для этой задачи использование начальной точки может сохранить итерации ветвей и связей. Создайте начальную точку правильных размерностей.

x0.buy = zeros(size(buy));
x0.induse = zeros(size(induse));
x0.store = zeros(size(store));
x0.sell = zeros(size(sell));

Установите начальную точку, чтобы продавать только растительное масло veg2 и невегетируемое масло non3. Чтобы задать эту начальную точку соответствующим образом, используйте findindex функция.

numMonths = size(induse,1);
[idxMonths,idxOils] = findindex(induse,1:numMonths,{'veg2','non3'});
x0.induse(idxMonths,idxOils) = 1;

Удовлетворите максимальным растительным и неведомым ограничениям масла.

x0.sell(:,idxOils) = repmat([200,250],numMonths,1)
x0 = struct with fields:
       buy: [6x5 double]
    induse: [6x5 double]
     store: [6x5 double]
      sell: [6x5 double]

Установите начальную точку, чтобы не покупать нефть в первый месяц.

x0.buy(1,:) = 0;

Удовлетворите initstockbal ограничение для первого месяца, основанное на начальном хранении 500 для каждого типа масла, и отсутствие покупки в первый месяц, и постоянное использование veg2 и non3.

x0.store(1,:) = [500 300 500 500 250];

Удовлетворите оставшимся ограничениям запаса запаса при помощи findindex функция.

[idxMonths,idxOils] = findindex(store,2:6,{'veg2'});
x0.store(idxMonths,idxOils) = [100;0;0;0;500];

[idxMonths,idxOils] = findindex(store,2:6,{'veg1','non1','non2'});
x0.store(idxMonths,idxOils) =  500;

[idxMonths,idxOils] = findindex(store,2:6,{'non3'});
x0.store(idxMonths,idxOils) = [0;0;0;0;500];

[idxMonths,idxOils] = findindex(buy,2:6,{'veg2'});
x0.buy(idxMonths,idxOils) = [0;100;200;200;700];

[idxMonths,idxOils] = findindex(buy,2:6,{'non3'});
x0.buy(idxMonths,idxOils) = [0;250;250;250;750];

Проверяйте, что начальная точка является допустимой. Поскольку ограничения имеют различные размерности, установите cellfun UniformOutput Пара "имя-значение" с false при проверке несоответствий.

inf{1} = infeasibility(vegused,x0);
inf{2} = infeasibility(nonvegused,x0);
inf{3} = infeasibility(hardmin,x0);
inf{4} = infeasibility(hardmax,x0);
inf{5} = infeasibility(initstockbal,x0);
inf{6} = infeasibility(stockbal,x0);
inf{7} = infeasibility(minuse,x0);
inf{8} = infeasibility(maxusev,x0);
inf{9} = infeasibility(maxusenv,x0);
inf{10} = infeasibility(maxnuse,x0);
inf{11} = infeasibility(deflogic1,x0);
allinfeas = cellfun(@max,inf,'UniformOutput',false);
anyinfeas = cellfun(@max,allinfeas);
disp(anyinfeas)
     0     0     0     0     0     0     0     0     0     0     0

Все недопустимости равны нулю, что показывает, что начальная точка допустима.

Повторите задачу с помощью начальной точки.

[sol2,fval2,exitstatus2,output2] = solve(prob,x0,'options',opts)
Solving problem using intlinprog.
LP:                Optimal objective value is -1.075130e+05.                                        

Cut Generation:    Applied 41 Gomory cuts, 2 cover cuts,                                            
                   1 mir cut, and 1 clique cut.                                                     
                   Lower bound is -1.047522e+05.                                                    
                   Relative gap is 166.88%.                                                        

Branch and Bound:

   nodes     total   num int        integer       relative                                          
explored  time (s)  solution           fval        gap (%)                                         
      15      0.12         2  -7.190185e+04   4.535003e+01                                          
      21      0.14         3  -8.290000e+04   2.606702e+01                                          
      25      0.14         4  -8.775370e+04   1.909426e+01                                          
      30      0.14         5  -9.953889e+04   4.993907e+00                                          
      56      0.16         6  -9.982870e+04   4.689100e+00                                          
     138      0.20         7  -1.002787e+05   3.851839e+00                                          
    1114      0.49         7  -1.002787e+05   0.000000e+00                                          

Optimal solution found.

Intlinprog stopped because the objective value is within a gap tolerance of the
optimal value, options.AbsoluteGapTolerance = 0 (the default value). The intcon
variables are integer within tolerance, options.IntegerTolerance = 1e-05 (the
default value).
sol2 = struct with fields:
       buy: [6x5 double]
    induse: [6x5 double]
      sell: [6x5 double]
     store: [6x5 double]

fval2 = 1.0028e+05
exitstatus2 = 
    OptimalSolution

output2 = struct with fields:
        relativegap: 0
        absolutegap: 0
      numfeaspoints: 7
           numnodes: 1114
    constrviolation: 1.7580e-12
            message: 'Optimal solution found....'
             solver: 'intlinprog'

На этот раз, solve предпринял меньше шагов для поиска решения.

fprintf(['Using the initial point took %d branch-and-bound steps,\nbut ',...
    'using no initial point took %d steps.'],output2.numnodes,output1.numnodes)
Using the initial point took 1114 branch-and-bound steps,
but using no initial point took 1112 steps.

Ссылка

[1] Уильямс, Х. Пол. Построение моделей в математическом программировании. Четвертое издание. Дж. Уайли, Чичестер, Англия. Проблема 12.1, «Продовольственная Manufacture1.» 1999 года.

См. также

|

Похожие темы