Создайте модель инвентаризации многопериодов в основанной на проблеме среде

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

Удобрения и ингредиенты

Гранулированные удобрения содержат питательные вещества азот (N), фосфор (P) и калий (K). Можно смешать следующее сырье с получением смесей удобрений с необходимыми питательными веществами.

load fertilizer
blends = blendDemand.Properties.VariableNames % Fertilizers to produce
blends = 1x2 cell
    {'Balanced'}    {'HighN'}

nutrients = rawNutrients.Properties.RowNames
nutrients = 3x1 cell
    {'N'}
    {'P'}
    {'K'}

raws = rawNutrients.Properties.VariableNames % Raw materials
raws = 1x6 cell
    {'MAP'}    {'Potash'}    {'AN'}    {'AS'}    {'TSP'}    {'Sand'}

Две смеси удобрений имеют одинаковые потребности в питательных веществах (10% N, 10% P и 10% K по массе), за исключением того, что смесь «HighN» имеет дополнительное 10% N в общей сложности 20% N.

disp(blendNutrients) % Table is in percentage
         Balanced    HighN
         ________    _____

    N       10        20  
    P       10        10  
    K       10        10  

Сырье имеет следующие имена и процентное содержание питательных веществ по весу.

disp(rawNutrients) % Table is in percentage
         MAP    Potash    AN    AS    TSP    Sand
         ___    ______    __    __    ___    ____

    N    11        0      35    21     0      0  
    P    48        0       0     0    46      0  
    K     0       60       0     0     0      0  

Сырье Sand не имеет питательного содержимого. При необходимости песок разбавляет другие ингредиенты, получая требуемый процент питательных веществ по весу.

Сохраните номера каждой из этих величин в переменных.

nBlends = length(blends);
nRaws = length(raws);
nNutrients = length(nutrients);

Прогнозирование спроса и выручки

Предположим, что вы заранее знаете потребность в весе (тоннах) для двух смесей удобрений на периоды времени в задаче.

disp(blendDemand)
                 Balanced    HighN
                 ________    _____

    January        750        300 
    February       800        310 
    March          900        600 
    April          850        400 
    May            700        350 
    June           700        300 
    July           700        200 
    August         600        200 
    September      600        200 
    October        550        200 
    November       550        200 
    December       550        200 

Вы знаете цены за тонну, по которым вы продаете смеси удобрений. Эти цены за тонну не зависят от времени.

disp(blendPrice)
    Balanced    HighN
    ________    _____

      400        550 

Цены на сырье

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

disp(rawCost)
                 MAP    Potash    AN     AS     TSP    Sand
                 ___    ______    ___    ___    ___    ____

    January      350     610      300    135    250     80 
    February     360     630      300    140    275     80 
    March        350     630      300    135    275     80 
    April        350     610      300    125    250     80 
    May          320     600      300    125    250     80 
    June         320     600      300    125    250     80 
    July         320     600      300    125    250     80 
    August       320     600      300    125    240     80 
    September    320     600      300    125    240     80 
    October      310     600      300    125    240     80 
    November     310     600      300    125    240     80 
    December     340     600      300    125    240     80 

Стоимость хранения

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

disp(inventoryCost)
    10

Ограничения емкости

Хранить можно не более inventoryCapacity тонны суммарных смесей удобрений в любой временной период.

disp(inventoryCapacity)
        1000

Вы можете произвести в общей сложности не более productionCapacity тонн в любой временной период.

disp(productionCapacity)
        1200

Связь между производством, продажами и запасами

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

inventory(time,product) = inventory(time-1,product) + production(time,product) - sales(time,product)

Это уравнение подразумевает, что инвентаризация учитывается в конце временного периода. Периоды времени в задаче следующие.

months = blendDemand.Properties.RowNames;
nMonths = length(months);

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

inventory(1,product) = initialInventory(product) + production(1,product) - sales(1,product)

Начальный запас содержится в данных blendInventory{'Initial',:}. Окончательный запас указан в данных blendInventory{'Final',:}.

Предположим, что неудовлетворенное требование потеряно. Другими словами, если вы не можете заполнить все порядки за определенный период времени, избыточные порядки не переносятся в следующий период времени.

Формулировка задачи оптимизации

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

inventoryProblem = optimproblem('ObjectiveSense','maximize');

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

make = optimvar('make',months,blends,'LowerBound',0);
sell = optimvar('sell',months,blends,'LowerBound',0,'UpperBound',blendDemand{months,blends});
use  = optimvar('use',months,raws,blends,'LowerBound',0);

Кроме того, создайте переменную, которая представляет запасы в каждый момент времени.

inventory = optimvar('inventory',months,blends,'LowerBound',0,'UpperBound',inventoryCapacity);

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

revenue = sum(blendPrice{1,:}.*sum(sell(months,blends),1));

Стоимость ингредиентов является стоимостью для каждого ингредиента, используемого в каждый раз, добавляется в течение всех периодов времени. Поскольку количество, используемое в каждый момент времени, разделяется на количество, используемое для каждой смеси, также добавляют смеси.

blendsUsed = sum(use(months,raws,blends),3);
ingredientCost = sum(sum(rawCost{months,raws}.*blendsUsed));

Затраты на хранение - это затраты на хранение запасов в течение каждого временного периода, добавляемые с течением времени и смешения.

storageCost = inventoryCost*sum(inventory(:));

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

inventoryProblem.Objective = revenue - ingredientCost - storageCost;

Ограничения задачи

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

materialBalance = optimconstr(months,blends);
timeAbove1 = months(2:end);
previousTime = months(1:end-1);
materialBalance(timeAbove1,:) = inventory(timeAbove1,:) == inventory(previousTime,:) +...
    make(timeAbove1,:) - sell(timeAbove1,:);
materialBalance(1,:) = inventory(1,:) == blendInventory{'Initial',:} +...
    make(1,:) - sell(1,:);

Выразите ограничение, что окончательный запас также фиксирован.

finalC = inventory(end,:) == blendInventory{'Final',:};

Общий запас в каждый момент времени ограничен.

boundedInv = sum(inventory,2) <= inventoryCapacity;

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

processLimit = sum(make,2) <= productionCapacity;

Количество, которое вы производите каждый месяц каждой смеси, это количество сырья, которое вы используете. The squeeze функция преобразует сумму из nmonths-by-1-by- nblends массив в nmonths-by- nblends массив.

rawMaterialUse = squeeze(sum(use(months,raws,blends),2)) == make(months,blends);

Питательные вещества в каждой смеси должны иметь необходимые значения. В следующем внутреннем операторе умножение rawNutrients{n,raws}*use(m,raws,b)' добавляет значения питательных веществ в каждый момент времени по сравнению с используемым сырьем.

blendNutrientsQuality = optimconstr(months,nutrients,blends);
for m = 1:nMonths
    for b = 1:nBlends
        for n = 1:nNutrients
            blendNutrientsQuality(m,n,b) = rawNutrients{n,raws}*use(m,raws,b)' == blendNutrients{n,b}*make(m,b);
        end
    end
end

Поместите ограничения в задачу.

inventoryProblem.Constraints.materialBalance = materialBalance;
inventoryProblem.Constraints.finalC = finalC;
inventoryProblem.Constraints.boundedInv = boundedInv;
inventoryProblem.Constraints.processLimit = processLimit;
inventoryProblem.Constraints.rawMaterialUse = rawMaterialUse;
inventoryProblem.Constraints.blendNutrientsQuality = blendNutrientsQuality;

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

Формулировка задачи завершена. Решите проблему.

[sol,fval,exitflag,output] = solve(inventoryProblem)
Solving problem using linprog.

Optimal solution found.
sol = struct with fields:
    inventory: [12x2 double]
         make: [12x2 double]
         sell: [12x2 double]
          use: [12x6x2 double]

fval = 2.2474e+06
exitflag = 
    OptimalSolution

output = struct with fields:
         iterations: 162
    constrviolation: 5.4570e-12
            message: 'Optimal solution found.'
          algorithm: 'dual-simplex'
      firstorderopt: 6.5235e-12
             solver: 'linprog'

Отображение результатов в табличной и графической форме.

if exitflag > 0
    fprintf('Profit: %g\n',fval);
    makeT = array2table(sol.make,'RowNames',months,'VariableNames',strcat('make',blends));
    sellT = array2table(sol.sell,'RowNames',months,'VariableNames',strcat('sell',blends));
    storeT = array2table(sol.inventory,'RowNames',months,'VariableNames',strcat('store',blends));
    productionPlanT = [makeT sellT storeT]
    figure
    subplot(3,1,1)
    bar(sol.make)
    legend('Balanced','HighN','Location','eastoutside')
    title('Amount Made')
    subplot(3,1,2)
    bar(sol.sell)
    legend('Balanced','HighN','Location','eastoutside')
    title('Amount Sold')
    subplot(3,1,3)
    bar(sol.inventory)
    legend('Balanced','HighN','Location','eastoutside')
    title('Amount Stored')
    xlabel('Time')
end
Profit: 2.24739e+06
productionPlanT=12×6 table
                 makeBalanced    makeHighN    sellBalanced    sellHighN    storeBalanced    storeHighN
                 ____________    _________    ____________    _________    _____________    __________

    January          1100           100           750            300            550              0    
    February          600           310           800            310            350              0    
    March             550           650           900            600              0             50    
    April             850           350           850            400              0              0    
    May               700           350           700            350              0              0    
    June              700           300           700            300              0              0    
    July              700           200           700            200              0              0    
    August            600           200           600            200              0              0    
    September         600           200           600            200              0              0    
    October           550           200           550            200              0              0    
    November          550           200           550            200              0              0    
    December          750           400           550            200            200            200    

Figure contains 3 axes. Axes 1 with title Amount Made contains 2 objects of type bar. These objects represent Balanced, HighN. Axes 2 with title Amount Sold contains 2 objects of type bar. These objects represent Balanced, HighN. Axes 3 with title Amount Stored contains 2 objects of type bar. These objects represent Balanced, HighN.

Похожие темы