Engee 文档
Notebook

基于优化肥料生产实例的多阶段库存管理模型

导言

此示例演示如何使用Engee创建智能生产和库存计划模型。 有必要为未来一年做出战略决策。

让我们想象一下,我们经营一家生产两种类型肥料的工厂。 我们有几种类型的原材料(成分)可供使用,其价格根据众所周知的时间表(例如季节性)每月变化。 与此同时,我们确切地知道每个月客户将订购多少吨每种类型的肥料。 该
目标是实现利润最大化。 要做到这一点,你需要找到之间的完美平衡:

*生产完全一样多,你需要(或者你可以出售它盈利);
*在最便宜的月份购买原材料;
*在经济上可行的情况下将成品存放在仓库中。

对于这样的长期策略,您可以使用金融工具(例如,期货合约)来固定未来的商品价格并保护自己免受风险。 我们的数学模型将帮助您计算最有利可图的计划,考虑到所有这些因素。

我们将连接必要的库。

In [ ]:
using JuMP, DataFrames, GLPK, NamedArrays, Measures

肥料及其组成

颗粒肥料含有三种关键营养素:氮,磷和钾。 在工厂,我们不是从头开始生产它们,而是混合现成的原料,以获得具有正确成分的商业品牌肥料。
我们有几种"成分"-每种都有自己独特的营养成分。 通过以正确的比例组合它们,我们创造了一个成品-一个平衡的肥料,我们的客户需要。

初始数据

我们将在此基础上确定优化生产的数据。:

*对肥料的需求量;
*一年中的月份列表;
*初始股票价值;
*每种肥料中养分的百分比;
*肥料订单的大小;
*肥料价格;
*储存容量;
*存储成本;
*生产能力;
*原材料成本;
*原料中营养成分的含量。

In [ ]:
对食物的需求=DataFrame(
    平衡的= [750, 800, 900, 850, 700, 700, 700, 600, 600, 550, 550, 550],
    高氮= [300, 310, 600, 400, 350, 300, 200, 200, 200, 200, 200, 200]
)
месяцы = ["一月", "二月", "三月", "四月", "五月", "六月", 
          "七月份", "八月份", "9月", "10月", "11月", "12月"]
初始储备=DataFrame(
    平衡=[200,200],
    高氮=[200,200]
)
肥料中的营养成分=DataFrame(
    平衡=[10,10,10],
    高氮=[20,10,10]
)
食品订单=DataFrame(
    平衡的= [200, 400, 400, 400, 400, 400, 200, 0, 0, 0, 0, 0],
    高氮= [0, 100, 200, 200, 200, 200, 200, 0, 0, 0, 0, 0]
)
肥料价格=DataFrame(
    平衡=[400],
    高氮=[550]
)
存储容量=1000
单位存储成本=10
生产功率=1200

原材料成本=DataFrame(
    磷酸一铵= [350, 360, 350, 350, 320, 320, 320, 320, 320, 310, 310, 340],
    氯化钾= [610, 630, 630, 610, 600, 600, 600, 600, 600, 600, 600, 600],
    Ammiachna_选择器= [300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300],
    硫酸铵_铵= [135, 140, 135, 125, 125, 125, 125, 125, 125, 125, 125, 125],
    三过磷酸钙= [250, 275, 275, 250, 250, 250, 250, 240, 240, 240, 240, 240],
    沙子= [80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80]
)

feed_containment_in原料=DataFrame(
    磷酸一铵=[11,48,0],
    氯化钾=[0,0,60],
    Ammoniacal_seliter=[35,0,0],
    硫酸铵=[21,0,0],
    Ternar_superphosphate=[0,46,0],
    =[0,0,0]
)
肥料类型=名称(肥料需求量)
вещества = ["氮", "磷", "钾"]
原料种类=名称(原料成本) 
肥料种类数=长度(肥料种类)
原料种类数=长度(原料种类)
营养素的数量=长度(物质)
月数=长度(月)
每月需求量=副本(肥料需求量)
插入!(需求以月为单位,1,:月=>)
以月为单位的原材料成本=副本(原材料成本)
插入!(以月为单位的原材料成本,1,:月=>月)
名称为=副本的肥料的含量(营养肥料的含量)
插入!(名称为,1,:Nutrition_material=>物质的肥料的含量)
名称=复制的原料内容(营养材料的内容)
插入!(名称为,1,:Nutrition_material=>substances的原料含量)

让我们定义一个以表格形式显示数据的函数。

In [ ]:
function вывести_таблицу(df::DataFrame, заголовок::String="")
    如果!isempty(标头)
        println("\n$标题:")
    end
    列名=名称(df)
    列width_=[]
    对于列名中的列_
        宽度=长度(字符串(列))
        for i in 1:nrow(df)
            =df[i,]
            如果isa(值,数字)
                string_values=string(round(Int,value))
            else
                string_values=字符串(值)
            end
            宽度=最大(宽度,长度(string_values))
        end
        推!(列width_,宽度+2 
    end
    заголовок_таблицы = ""
    for(i,column)in enumerate(column name_)
        表heading_=rpad(列,列width_[i]
    end
    println(表头)
    разделитель = ""
    用于列宽中的宽度_
        разделитель *= repeat("-", ширина)
    end
    println(分隔符)
    for i in 1:nrow(df)
        строка = ""
        for(j,column)in enumerate(column name_)
            =df[i,]
            如果isa(值,数字)
                string_values=string(round(Int,value))
            else
                string_values=字符串(值)
            end
            *=rpad(row_values,列width_[j])
        end
        println(字符串)
    end
    println()
end

有两种类型的肥料可用:

*平衡-标准成分是10%氮,10%磷,10%钾。

*高氮-氮-增强版:20%氮,10%磷,10%钾。

唯一的区别是"高氮"中氮的比例增加了一倍,这使得生产更昂贵。

In [ ]:
println("肥料中的养分含量(以百分比计):")
查看表(包含名称的改进内容)
Содержание питательных веществ в удобрениях (в процентах):

Питательное_вещество  Сбалансированное  Высокоазотное  
-------------------------------------------------------
Азот                  10                20             
Фосфор                10                10             
Калий                 10                10             

原料及其营养成分(以重量百分比计):

In [ ]:
println("原料中营养物质的含量(以百分比计):")
查看表(带名称的原料内容)
Содержание питательных веществ в сырье (в процентах):

Питательное_вещество  Моноаммонийфосфат  Хлорид_калия  Аммиачная_селитра  Сульфат_аммония  Тройной_суперфосфат  Песок  
-----------------------------------------------------------------------------------------------------------------------
Азот                  11                 0             35                 21               0                    0      
Фосфор                48                 0             0                  0                46                   0      
Калий                 0                  60            0                  0                0                    0      

砂是中性填料。 里面没有营养素。 它用作"稀释剂",以在活性成分过于浓缩时准确地保持成品混合物中所需的百分比。

化肥订单量和价格

在整个计划期间,我们知道每个品牌肥料的每月订单的确切数量。

In [ ]:
println("订购量:")
检查表(需求以月为单位)
Объём заказов:
Месяц     Сбалансированное  Высокоазотное  
-------------------------------------------
Январь    750               300            
Февраль   800               310            
Март      900               600            
Апрель    850               400            
Май       700               350            
Июнь      700               300            
Июль      700               200            
Август    600               200            
Сентябрь  600               200            
Октябрь   550               200            
Ноябрь    550               200            
Декабрь   550               200            

价格是固定的全年。 这意味着我们的收入只取决于销售量,而不是时间。 这种稳定性简化了规划,使您能够专注于成本优化。

In [ ]:
println("化肥价格:")
查看表(肥料价格)
Цены на удобрения:
Сбалансированное  Высокоазотное  
---------------------------------
400               550            

原材料的成本

原材料价格是我们模型中的一个关键变量。 与成品的稳定价格不同,成分成本每个月都会发生变化(例如,由于季节性或市场条件)。

In [ ]:
println("原材料的成本:")
查看表(以月为单位的原材料成本)
Стоимость сырья:
Месяц     Моноаммонийфосфат  Хлорид_калия  Аммиачная_селитра  Сульфат_аммония  Тройной_суперфосфат  Песок  
-----------------------------------------------------------------------------------------------------------
Январь    350                610           300                135              250                  80     
Февраль   360                630           300                140              275                  80     
Март      350                630           300                135              275                  80     
Апрель    350                610           300                125              250                  80     
Май       320                600           300                125              250                  80     
Июнь      320                600           300                125              250                  80     
Июль      320                600           300                125              250                  80     
Август    320                600           300                125              240                  80     
Сентябрь  320                600           300                125              240                  80     
Октябрь   310                600           300                125              240                  80     
Ноябрь    310                600           300                125              240                  80     
Декабрь   340                600           300                125              240                  80     

生产及储存

我们将显示存储生产单位的成本,仓库容量和生产能力。

In [ ]:
println("单位存储成本:$单位存储成本")
println("仓库容量:▪仓库容量")
println("生产能力:$production_power")
Стоимость хранения единицы: 10
Вместимость склада: 1000
Производственная мощность: 1200

如果我们无法在一个月内生产和运送全部订单,余额将不会转移到下个月。 这迫使模型始终在需求之前计划生产。 因此,该模型正在寻找一个平衡点:生产足够的订单并达到目标余额,但不能生产太多,以免产生存储成本。

设置任务

该模型基于需要最大化的目标函数。 在我们的情况下,这是一个利润。

In [ ]:
模型=模型(GLPK。优化器)
Out[0]:
A JuMP Model
├ solver: GLPK
├ objective_sense: FEASIBILITY_SENSE
├ num_variables: 0
├ num_constraints: 0
└ Names registered in the model: none

变量

任务中的变量是我们每个月生产和销售的肥料混合物的数量,以及用于制造它们的原材料。

In [ ]:
@variable(model,produce[1:月数,1:改进类型数]>=0)
@variable(model,sell[1:月数,1:改进类型数]>=0)
@变量(型号,用途[1:月数,1:原料种类数,1:肥料种类数]>=0

此外,我们将创建一个变量,表示每个时间点的库存量。

In [ ]:
@变量(型号,0<=库存[1:月数,1:肥料种类数]<=仓库容量)

销售上限是每个时间段和每个品牌肥料的需求量。

In [ ]:
对于i in1:月数,b in1:肥料类型的数量
    set_upper_bound(sell[i,b],demand for fertilities[i,b])
end

表达方式

要使用任务变量计算目标函数,您需要计算收入和成本。 收入是每种类型肥料的销售量乘以其价格,在所有时间段和所有类型的肥料中总结。 让我们定义一个收入表达式。

In [ ]:
@expression(model,revenue,sum(prices for fertilizers[1,j]*sum(sell[i,j]for i in1:number of months)for j in1:肥料种类的数量))

让我们定义一个原材料成本的表达式。 原材料的成本是在每个时间点使用的每种成分的成本,总结了所有时期。 由于每个时刻使用的原料体积除以每种肥料的使用量,因此求和也按肥料的类型进行。

In [ ]:
@expression(model,raw materials used[i=1:月数,r=1:原料种类数],sum(use[i,r,b]for b in1:肥料种类数))
@expression(model,total cost of raw materials,sum(cost of raw materials[i,r]*raw materials used[i,r]for i in1:number of months for r in1:number of types of raw materials))

让我们定义一个存储成本的表达式。 储存成本是在每个时间段内维持库存的成本,由时间和肥料类型总结。

In [ ]:
@表达式(模型、总存储成本、单位存储成本*总和(股票))

目标功能

让我们定义目标函数。

In [ ]:
@objective(模型,最大值,收入-原材料的一般价值-存储的一般价值)

限制

模型必须符合关键生产规则。:

*库存余额:仓库中的余额是上个月余额与生产和销售的产品数量差异之和;
*目标股票:到年底,有必要达到设定的平衡水平;
*限制:仓库容量和工厂容量有限;
*物料平衡:成品量等于消耗的原料量;
*质量:每批次的化学成分必须严格符合配方。

所有这些规则都形式化为数学约束并加载到模型中以找到最优计划。

In [ ]:
@constraint(model,material_balance1[i=2:月数,b=1:肥料种类数], 
    stocks[i,b]==stocks[i-1,b]+produce[i,b]-sell[i,b])
@constraint(model,material_balance2[b=1:改进类型数],
    stocks[1,b]==初始股票[1,b]+produce[1,b]-sell[1,b]
@constraint(model,final stocks[b=1:肥料种类数],stocks[月数,b]==initial stocks[2,b])
@constraint(model,storage limite_[i=1:月数],sum(stocks[i,:])<=存储容量)
@constraint(model,capacity limite_[i=1:月数],sum(produce[i,:])<=生产能力)
@约束(型号,原料用量[i=1:月数,b=1:肥料种类数], 
    sum(use[i,r,b]for r in1:quantity of raw materials)==produce[i,b])
@约束(模型,肥料的质量[i=1:月数,n=1:养分数量,b=1:肥料种类数量],
    sum(原料中的营养含量[n,r]*use[i,r,b]for r in1:quantity of raw materials)==肥料中的营养含量[n,b]*produce[i,b])

解决和可视化结果

让我们开始解决优化问题。

In [ ]:
优化!(型号)

让我们将解决方案显示为表格(如果表格的宽度不适合,请在浏览器中缩小)。

In [ ]:
状态=termination_status(模型);
如果状态==MOI。最佳
    利润=圆(Int,objective_value(模型))
    println("\找到最优解")
    println("利润:$利润")
    value_product=值。(生产)
    value_sell=值。(卖出)
    stock_values=值。(股票)
    产生圆形=圆形。(Int,要产生的值)
    sell_rounded=圆形。(Int,卖出值)
    stock_四舍五入=圆形。(Int,stock_values)
    生产table_=DataFrame()
    对于1中的b:肥料类型的数量
        таблица_производства[!, Symbol("生产_" * replace(виды_удобрений[b], " " => "_"))] = производить_округленные[:, b]
    end
    销售表_=DataFrame()
    对于1中的b:肥料类型的数量
        таблица_продаж[!, Symbol("出售_" * replace(виды_удобрений[b], " " => "_"))] = продавать_округленные[:, b]
    end
    库存表_=DataFrame()
    对于1中的b:肥料类型的数量
        таблица_запасов[!, Symbol("股票_" * replace(виды_удобрений[b], " " => "_"))] = запасы_округленные[:, b]
    end
    生产计划=hcat(生产表,销售表,库存表)
    生产计划[,:月]=
    生产计划=生产计划[:,[end,1:end-1...]]
    println("\生产计划:")
    查看表(生产计划)
end
产生平衡=产生圆角[:,1];
产生高氮=产生圆角[:,2];
卖出平衡=卖出四舍五入[:,1];
卖出高氮=卖出四舍五入[:,2];
stock_balanced=stock_四舍五入[:,1];
高氮股=圆形股[:,2];
Оптимальное решение найдено
Прибыль: 2247394

Производственный план:
Месяц     произвести_Сбалансированное  произвести_Высокоазотное  продать_Сбалансированное  продать_Высокоазотное  запасы_Сбалансированное  запасы_Высокоазотное  
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Январь    1100                         100                       750                       300                    550                      0                     
Февраль   600                          310                       800                       310                    350                      0                     
Март      550                          650                       900                       600                    0                        50                    
Апрель    850                          350                       850                       400                    0                        0                     
Май       700                          350                       700                       350                    0                        0                     
Июнь      700                          300                       700                       300                    0                        0                     
Июль      700                          200                       700                       200                    0                        0                     
Август    600                          200                       600                       200                    0                        0                     
Сентябрь  600                          200                       600                       200                    0                        0                     
Октябрь   550                          200                       550                       200                    0                        0                     
Ноябрь    550                          200                       550                       200                    0                        0                     
Декабрь   750                          400                       550                       200                    200                      200                   

我们将显示a平衡肥料的最佳生产,销售和库存的直方图。

In [ ]:
p1 = bar(месяцы, производить_сбалансированное, title="制作人", xlabel="", color=:green, ylabel="数量")
p2 = bar(месяцы, продавать_сбалансированное, title="已售出", xlabel="", color=:green, ylabel="数量")
p3 = bar(месяцы, запасы_сбалансированное, title="股票", color=:green, ylabel="数量")
graph1=plot(p1,p2,p3,layout=(3,1),size=(1000,900),margin=10mm,legend=false)
显示(图1)

我们将显示高氮肥料的最佳生产,销售和库存的直方图。

In [ ]:
p1 = bar(месяцы, производить_высокоазотное, title="制作人", xlabel="", color=:orange, ylabel="数量")
p2 = bar(месяцы, продавать_высокоазотное, title="已售出", xlabel="", color=:orange, ylabel="数量")
p3 = bar(месяцы, запасы_высокоазотное, title="股票", color=:orange, ylabel="数量")
graph2=plot(p1,p2,p3,layout=(3,1),size=(1000,900),margin=10mm,legend=false)
显示(图2)

结论

多阶段生产任务的模拟演示了优化方法在动态市场中的战略规划的有效性。 该解决方案提供了一个准确和实用的计划。

主要调查结果:

  1. 发布策略:该模型确定了在需求高峰期生产高利润产品的优先级,而基础产品支持整体产能利用率。 这与利润最大化的目标直接一致。

  2. 储备的动态作用:分析表明,储备不是作为储备,而是作为一个积极的缓冲,平滑可变需求和固定能力之间的不平衡。 采用计划积累和随后减少库存的策略来最大限度地减少总成本。

  3. 约束的影响:对产能和目标端平衡的严格约束形成计划的特定结构,导致生产尖峰满足边界条件。 这证实了系统对控制参数的高灵敏度。

因此,该模型充分地将复杂的规划任务形式化,并将其转化为优化任务。 其结果是一个战略地图,确定利润增长点和系统瓶颈,这使得这种方法成为证明管理决策合理的有价值的工具。