301 lines
8.6 KiB
Lua
301 lines
8.6 KiB
Lua
-------------------------------------------------------------------------------
|
|
---Description of the module.
|
|
---@class SolverSimplex
|
|
SolverSimplex = newclass(Solver,function(base, object)
|
|
Solver.init(base, object)
|
|
end)
|
|
|
|
-------------------------------------------------------------------------------
|
|
---Calcul pivot de gauss
|
|
---@param M table
|
|
---@param xrow number
|
|
---@param xcol number
|
|
---@return table
|
|
function SolverSimplex:pivot(M, xrow, xcol)
|
|
local Mx = {}
|
|
local pivot_value = M[xrow][xcol]
|
|
for irow,row in pairs(M) do
|
|
Mx[irow]={}
|
|
if irow > self.row_input then
|
|
for icol,cell_value in pairs(row) do
|
|
if icol >= self.col_start then
|
|
if irow == xrow then
|
|
--Transformation de la ligne pivot : elle est divisee par l'element pivot
|
|
Mx[irow][icol] = cell_value/pivot_value
|
|
elseif icol == xcol then
|
|
--Transformation de la colonne pivot : toutes les cases sauf la case pivot deviennent zero.
|
|
Mx[irow][icol] = 0
|
|
else
|
|
local B = M[irow][xcol]
|
|
local D = M[xrow][icol]
|
|
local value = cell_value - ( B * D ) / pivot_value
|
|
if math.abs(value) < 1e-8 then
|
|
Mx[irow][icol] = 0
|
|
else
|
|
Mx[irow][icol] = value
|
|
end
|
|
end
|
|
else
|
|
Mx[irow][icol] = cell_value
|
|
end
|
|
end
|
|
else
|
|
for icol,cell_value in pairs(row) do
|
|
Mx[irow][icol] = cell_value
|
|
end
|
|
end
|
|
end
|
|
Mx[xrow][1] = M[1][xcol]
|
|
Mx[1][xcol] = M[xrow][1]
|
|
return Mx
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
---Retourne le pivot
|
|
---@param M table
|
|
---@return table
|
|
function SolverSimplex:getPivot(M)
|
|
local max_z_value = 0
|
|
local xcol = nil
|
|
local ratio_value = 0
|
|
local max_value = 0
|
|
local xrow = nil
|
|
local last_row = M[#M]
|
|
-- boucle sur la derniere ligne nommee Z
|
|
for icol,z_value in pairs(last_row) do
|
|
-- on exclus les premieres colonnes
|
|
if icol > self.col_start then
|
|
if z_value > max_z_value then
|
|
-- la valeur repond au critere, la colonne est eligible
|
|
-- on recherche le ligne
|
|
ratio_value = nil
|
|
for irow, current_row in pairs(M) do
|
|
local x_value = M[irow][icol]
|
|
-- on n'utilise pas la derniere ligne
|
|
-- seule les cases positives sont prises en compte
|
|
if irow > self.row_input and irow < #M and x_value > 0 then
|
|
-- calcul du ratio base / x
|
|
local c_value = M[irow][self.col_start]
|
|
local bx_ratio = c_value/x_value
|
|
-- prend la premier valeur ou le plus grand ratio sinon la plus grande valeur
|
|
if ratio_value == nil or bx_ratio > ratio_value or c_value > max_value then
|
|
ratio_value = bx_ratio
|
|
max_value = c_value
|
|
xrow = irow
|
|
end
|
|
end
|
|
end
|
|
if ratio_value ~= nil then
|
|
-- le pivot est possible
|
|
max_z_value = z_value
|
|
xcol = icol
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if max_z_value == 0 then
|
|
-- il n'y a plus d'amelioration possible fin du programmme
|
|
return false, xcol, xrow
|
|
end
|
|
return true, xcol, xrow
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
---Prepare la matrice
|
|
---@param M table
|
|
---@return table
|
|
function SolverSimplex:prepare(M)
|
|
---ajoute la ligne Z
|
|
local irow = 1
|
|
---prepare les headers
|
|
local Mx = self:clone(M)
|
|
|
|
---ajoute les recettes d'ingredient
|
|
---initialise l'analyse
|
|
local ckeck_cols = {}
|
|
for icol,_ in pairs(Mx[1]) do
|
|
ckeck_cols[icol] = true
|
|
end
|
|
for irow,row in pairs(Mx) do
|
|
if irow > self.row_input and irow < #Mx then
|
|
for icol,cell in pairs(row) do
|
|
if icol > self.col_start then
|
|
---si une colonne est un produit au moins une fois on l'exclus
|
|
if cell > 0 then
|
|
ckeck_cols[icol] = false
|
|
end
|
|
else
|
|
ckeck_cols[icol] = false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
---ajout des faux recipe
|
|
local index = 1
|
|
for xcol,check in pairs(ckeck_cols) do
|
|
if check then
|
|
local row = {}
|
|
for icol,header in pairs(Mx[1]) do
|
|
if header.name == "B" then
|
|
table.insert(row, Mx[1][xcol])
|
|
else
|
|
if icol == self.col_start then
|
|
--table.insert(row,math.pow(10,index)*10) ---important ne pas changer
|
|
table.insert(row,1e4*index) ---important ne pas changer
|
|
elseif icol == xcol then
|
|
table.insert(row,1)
|
|
else
|
|
table.insert(row,0)
|
|
end
|
|
end
|
|
end
|
|
table.insert(Mx, #Mx,row)
|
|
index = index + 1
|
|
end
|
|
end
|
|
---ajoute les row en colonne
|
|
local num_row = rawlen(M)-self.row_input-1
|
|
local num_col = rawlen(Mx[1])
|
|
for xrow=1, num_row do
|
|
for irow,row in pairs(Mx) do
|
|
if irow == 1 then
|
|
---ajoute le header
|
|
Mx[irow][num_col+xrow] = Mx[xrow+self.row_input][1];
|
|
else
|
|
---ajoute les valeurs
|
|
if irow == xrow + self.row_input then
|
|
Mx[irow][num_col+xrow] = 1
|
|
else
|
|
Mx[irow][num_col+xrow] = 0
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
---initialise la ligne Z avec Z=input
|
|
for icol,cell in pairs(Mx[self.row_input]) do
|
|
if icol > self.col_start then
|
|
Mx[#Mx][icol] = cell
|
|
end
|
|
end
|
|
|
|
return Mx
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
---Calcul de la ligne
|
|
---@param Mx table
|
|
---@param xrow number
|
|
---@return table
|
|
function SolverSimplex:lineCompute(Mx, xrow)
|
|
if Mx == nil or xrow == 0 then return Mx end
|
|
local row = Mx[xrow]
|
|
local R = row[self.col_R]
|
|
local E = row[self.col_E]
|
|
|
|
for icol,cell_value in pairs(row) do
|
|
if cell_value ~= 0 and icol > self.col_start then
|
|
local Z = Mx[#Mx][icol] ---valeur demandee Z
|
|
local X = cell_value
|
|
|
|
local C = -Z/X
|
|
if C > 0 and C > Mx[xrow][self.col_C] then
|
|
Mx[xrow][self.col_C] = C
|
|
Mx[xrow][self.col_P] = R * E / C
|
|
end
|
|
end
|
|
end
|
|
|
|
local P = Mx[xrow][self.col_P]
|
|
local C = Mx[xrow][self.col_start]
|
|
for icol,cell_value in pairs(row) do
|
|
if cell_value ~= 0 and icol > self.col_start then
|
|
local Z = Mx[#Mx][icol] ---valeur demandee Z
|
|
local X = cell_value
|
|
---calcul du Z
|
|
Mx[#Mx][icol] = Z + X * P * C
|
|
end
|
|
end
|
|
return Mx
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
---Calcul du tableau
|
|
---@param Mx table --matrix finale
|
|
---@param Mi table --matrix intermediaire
|
|
---@return table
|
|
function SolverSimplex:tableCompute(Mx, Mi)
|
|
if Mx == nil then return Mx end
|
|
---preparation de la colonne R et P
|
|
for irow,_ in pairs(Mx) do
|
|
if irow > self.row_input and irow < #Mx then
|
|
---colonne correspondant a la recette
|
|
local icol = #Mx[1] + irow - self.row_input
|
|
Mx[irow][self.col_R] = - Mi[#Mi][icol] ---moins la valeur affichee dans Z
|
|
Mx[irow][self.col_P] = 0
|
|
end
|
|
end
|
|
---preparation input
|
|
---ajoute la ligne Z avec Z=-input
|
|
for icol,cell in pairs(Mx[self.row_input]) do
|
|
if icol > self.col_start then
|
|
Mx[#Mx][icol] = 0-cell
|
|
end
|
|
end
|
|
|
|
---initialise les valeurs des produits par second
|
|
for irow,row in pairs(Mx) do
|
|
if irow > self.row_input and irow < #Mx then
|
|
local E = Mx[irow][self.col_E]
|
|
for icol,cell in pairs(row) do
|
|
if icol > self.col_start then
|
|
Mx[irow][icol] = cell / E
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
---calcul du resultat
|
|
for irow,_ in pairs(Mx) do
|
|
if irow > self.row_input and irow < #Mx then
|
|
Mx = self:lineCompute(Mx, irow)
|
|
end
|
|
end
|
|
return Mx
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
---Resoud la matrice
|
|
---@param Mbase table
|
|
---@param debug boolean
|
|
---@param by_factory boolean
|
|
---@param time number
|
|
---@return table, table
|
|
function SolverSimplex:solveMatrix(Mbase, debug, by_factory, time)
|
|
if Mbase ~= nil then
|
|
local num_loop = 0
|
|
local runtime = {}
|
|
self:addRuntime(debug, runtime, "Initial", Mbase)
|
|
local Mstep = self:prepare(Mbase)
|
|
self:addRuntime(debug, runtime, "Prepare", Mstep)
|
|
local loop, xcol, xrow
|
|
loop = true
|
|
while loop do
|
|
loop, xcol, xrow = self:getPivot(Mstep)
|
|
if loop then
|
|
self:addRuntime(debug, runtime, "Step "..num_loop, Mstep, {x=xcol,y=xrow})
|
|
Mstep = self:pivot(Mstep, xrow, xcol)
|
|
else
|
|
self:addRuntime(debug, runtime, "Last", Mstep)
|
|
end
|
|
num_loop = num_loop + 1
|
|
end
|
|
---finalisation
|
|
local Mr = self:clone(Mbase)
|
|
Mr = self:tableCompute(Mr, Mstep)
|
|
Mr = self:finalize(Mr)
|
|
Mr = self:appendState(Mr)
|
|
self:addRuntime(debug, runtime, "final", Mr)
|
|
return Mr, runtime
|
|
end
|
|
end |