322 lines
9.4 KiB
Lua
322 lines
9.4 KiB
Lua
-------------------------------------------------------------------------------
|
|
---Description of the module.
|
|
---@class SolverMatrixSimplex : SolverMatrix
|
|
SolverMatrixSimplex = newclass(SolverMatrix, function(base, object)
|
|
SolverMatrix.init(base, object)
|
|
end)
|
|
|
|
-------------------------------------------------------------------------------
|
|
---Calcul pivot de gauss
|
|
---@param matrix Matrix
|
|
---@param xrow integer
|
|
---@param xcol integer
|
|
---@return Matrix
|
|
function SolverMatrixSimplex:pivot(matrix, xrow, xcol)
|
|
local rows = matrix.rows
|
|
|
|
local matrix_clone = self:clone(matrix)
|
|
matrix_clone.rows = {}
|
|
local rows_clone = matrix_clone.rows
|
|
local pivot_value = rows[xrow][xcol]
|
|
for irow, row in pairs(rows) do
|
|
local parameters = matrix_clone.parameters[xrow]
|
|
parameters.coefficient = parameters.coefficient / pivot_value
|
|
rows_clone[irow] = {}
|
|
for icol, column in pairs(matrix.columns) do
|
|
local cell_value = row[icol] or 0
|
|
if irow == xrow then
|
|
--Transformation de la ligne pivot : elle est divisee par l'element pivot
|
|
rows_clone[irow][icol] = cell_value / pivot_value
|
|
elseif icol == xcol then
|
|
--Transformation de la colonne pivot : toutes les cases sauf la case pivot deviennent zero.
|
|
rows_clone[irow][icol] = 0
|
|
else
|
|
local B = rows[irow][xcol] or 0
|
|
local D = rows[xrow][icol] or 0
|
|
local value = cell_value - (B * D) / pivot_value
|
|
if math.abs(value) < 1e-8 then
|
|
rows_clone[irow][icol] = 0
|
|
else
|
|
rows_clone[irow][icol] = value
|
|
end
|
|
end
|
|
end
|
|
end
|
|
-- swap base
|
|
matrix_clone.headers[xrow] = matrix.columns[xcol]
|
|
matrix_clone.columns[xcol] = matrix.headers[xrow]
|
|
return matrix_clone
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
---Retourne le pivot
|
|
---@param matrix Matrix
|
|
---@return boolean, integer, integer
|
|
function SolverMatrixSimplex:get_pivot(matrix)
|
|
local rows = matrix.rows
|
|
local zrow = matrix.rows[#matrix.rows]
|
|
|
|
local max_z_value = 0
|
|
local xcol = nil
|
|
local ratio_value = 0
|
|
local max_value = 0
|
|
local xrow = nil
|
|
-- boucle sur la derniere ligne nommee Z
|
|
for icol, column in pairs(matrix.columns) do
|
|
-- exclusion de la colonne coefficient
|
|
if icol > 1 then
|
|
local z_value = zrow[icol] or 0
|
|
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(rows) do
|
|
local parameters = matrix.parameters[irow]
|
|
local x_value = rows[irow][icol]
|
|
-- on n'utilise pas la derniere ligne
|
|
-- seule les cases positives sont prises en compte
|
|
if irow < #rows and x_value > 0 then
|
|
-- calcul du ratio base / x
|
|
local c_value = parameters.coefficient
|
|
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 matrix Matrix
|
|
---@return Matrix
|
|
function SolverMatrixSimplex:prepare(matrix)
|
|
---ajoute la ligne Z
|
|
local irow = 1
|
|
---prepare les headers
|
|
local matrix_clone = self:clone(matrix)
|
|
local rows = matrix_clone.rows
|
|
local headers = matrix_clone.headers
|
|
local columns = matrix_clone.columns
|
|
local parameters = matrix_clone.parameters
|
|
|
|
---ajoute les recettes d'ingredient
|
|
---initialise l'analyse
|
|
local ckeck_cols = {}
|
|
for icol, column in pairs(columns) do
|
|
ckeck_cols[icol] = true
|
|
end
|
|
for irow, row in pairs(rows) do
|
|
if irow <= #rows then
|
|
for icol, column in pairs(columns) do
|
|
local cell_value = row[icol] or 0
|
|
---si une colonne est un produit au moins une fois on l'exclus
|
|
if cell_value > 0 then
|
|
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
|
|
-- Add new header
|
|
local new_header = table.deepcopy(columns[xcol])
|
|
table.insert(headers, new_header)
|
|
-- Add coefficient value
|
|
local rowParameters = MatrixRowParameters()
|
|
rowParameters.base = new_header
|
|
rowParameters.contraint = nil
|
|
rowParameters.factory_count = 0
|
|
rowParameters.factory_speed = 0
|
|
rowParameters.recipe_count = 0
|
|
rowParameters.recipe_production = 1
|
|
rowParameters.recipe_energy = 1
|
|
rowParameters.coefficient = 1e4 * index --important ne pas changer
|
|
--rowParameters.coefficient = math.pow(10,index)*10
|
|
table.insert(parameters, rowParameters)
|
|
local new_row = {}
|
|
for icol, column in pairs(columns) do
|
|
if icol == xcol then
|
|
table.insert(new_row, 1)
|
|
else
|
|
table.insert(new_row, 0)
|
|
end
|
|
end
|
|
table.insert(rows, new_row)
|
|
index = index + 1
|
|
end
|
|
end
|
|
self:prepare_z_and_objectives(matrix_clone, true)
|
|
|
|
-- ajout colonne coefficient
|
|
local new_column = {type="none", name="C"}
|
|
table.insert(columns, 1, new_column)
|
|
for irow, row in pairs(rows) do
|
|
local parameters = matrix_clone.parameters[irow]
|
|
local C = 0
|
|
if parameters ~= nil and parameters.coefficient ~= nil then
|
|
C = parameters.coefficient or 0
|
|
end
|
|
table.insert(row, 1, C)
|
|
end
|
|
|
|
---ajoute les row en colonne
|
|
local num_row = rawlen(matrix.rows)
|
|
local num_col = rawlen(matrix_clone.columns)
|
|
for icol = 1, num_row do
|
|
-- Add new column
|
|
local new_column = table.deepcopy(matrix_clone.headers[icol])
|
|
table.insert(columns, new_column)
|
|
for irow, row in pairs(rows) do
|
|
---ajoute les valeurs
|
|
if irow == icol then
|
|
rows[irow][num_col + icol] = 1
|
|
else
|
|
rows[irow][num_col + icol] = 0
|
|
end
|
|
end
|
|
end
|
|
|
|
return matrix_clone
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
---Calcul de la ligne
|
|
---@param matrix Matrix
|
|
---@param xrow integer
|
|
---@return Matrix
|
|
function SolverMatrixSimplex:line_compute(matrix, xrow)
|
|
if matrix == nil or xrow == 0 then return matrix end
|
|
local row = matrix.rows[xrow]
|
|
local parameters = matrix.parameters[xrow]
|
|
local zrow = matrix.rows[#matrix.rows]
|
|
local R = parameters.recipe_count
|
|
local E = parameters.recipe_energy
|
|
|
|
for icol, column in pairs(matrix.columns) do
|
|
local cell_value = row[icol] or 0
|
|
if cell_value ~= 0 then
|
|
local Z = zrow[icol] ---valeur demandee Z
|
|
local X = cell_value
|
|
|
|
local C = -Z / X
|
|
if C > 0 and C > parameters.coefficient then
|
|
parameters.coefficient = C
|
|
parameters.recipe_production = R * E / C
|
|
end
|
|
end
|
|
end
|
|
|
|
local P = parameters.recipe_production
|
|
local C = parameters.coefficient
|
|
for icol, column in pairs(matrix.columns) do
|
|
local cell_value = row[icol] or 0
|
|
if cell_value ~= 0 then
|
|
local Z = zrow[icol] ---valeur demandee Z
|
|
local X = cell_value
|
|
---calcul du Z
|
|
zrow[icol] = Z + X * P * C
|
|
end
|
|
end
|
|
return matrix
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
---Calcul du tableau
|
|
---@param matrix Matrix --matrix finale
|
|
---@param matrix_result Matrix --matrix intermediaire
|
|
---@return Matrix
|
|
function SolverMatrixSimplex:table_compute(matrix, matrix_result)
|
|
if matrix == nil then return matrix end
|
|
local zrow = matrix_result.rows[#matrix_result.rows]
|
|
|
|
---preparation input
|
|
self:prepare_z_and_objectives(matrix, false)
|
|
|
|
---preparation du resultat
|
|
for irow, _ in pairs(matrix.rows) do
|
|
if irow < #matrix.rows then
|
|
---colonne correspondant a la recette
|
|
local icol = #matrix_result.columns - #matrix.headers + irow
|
|
|
|
local parameters = matrix.parameters[irow]
|
|
parameters.recipe_count = -zrow[icol] ---moins la valeur affichee dans Z
|
|
parameters.recipe_production = 0
|
|
end
|
|
end
|
|
|
|
---initialise les valeurs des produits par second
|
|
for irow, row in pairs(matrix.rows) do
|
|
if irow < #matrix.rows then
|
|
local parameters = matrix.parameters[irow]
|
|
local E = parameters.recipe_energy
|
|
for icol, column in pairs(matrix.columns) do
|
|
local cell_value = row[icol] or 0
|
|
row[icol] = cell_value / E
|
|
end
|
|
end
|
|
end
|
|
|
|
---calcul du resultat
|
|
for irow, _ in pairs(matrix.rows) do
|
|
if irow < #matrix.rows then
|
|
matrix = self:line_compute(matrix, irow)
|
|
end
|
|
end
|
|
return matrix
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
---Resoud la matrice
|
|
---@param Mbase table
|
|
---@param debug boolean
|
|
---@param by_factory boolean
|
|
---@param time number
|
|
---@return Matrix, {[integer] : Matrix}
|
|
function SolverMatrixSimplex:solve_matrix(Mbase, debug, by_factory, time)
|
|
if Mbase ~= nil then
|
|
local num_loop = 0
|
|
local runtime = {}
|
|
self:add_runtime(debug, runtime, "Initial", Mbase)
|
|
local Mstep = self:prepare(Mbase)
|
|
self:add_runtime(debug, runtime, "Prepare", Mstep)
|
|
local loop, xcol, xrow
|
|
loop = true
|
|
while loop do
|
|
loop, xcol, xrow = self:get_pivot(Mstep)
|
|
if loop then
|
|
self:add_runtime(debug, runtime, "Step " .. num_loop, Mstep, { x = xcol, y = xrow })
|
|
Mstep = self:pivot(Mstep, xrow, xcol)
|
|
else
|
|
self:add_runtime(debug, runtime, "Last", Mstep)
|
|
end
|
|
num_loop = num_loop + 1
|
|
end
|
|
---finalisation
|
|
local matrix_result = self:clone(Mbase)
|
|
matrix_result = self:table_compute(matrix_result, Mstep)
|
|
matrix_result = self:finalize(matrix_result)
|
|
matrix_result = self:apply_state(matrix_result)
|
|
self:add_runtime(debug, runtime, "final", matrix_result)
|
|
return matrix_result, runtime
|
|
end
|
|
end
|