From 911cf064c7fdd7a43542d91d2d46084360028e1d Mon Sep 17 00:00:00 2001 From: bahadirfyildirim Date: Thu, 16 Nov 2023 17:31:07 +0300 Subject: [PATCH] add LOPCOW method --- README.md | 4 +- src/JMcDM.jl | 22 ++------ src/lopcow.jl | 128 +++++++++++++++++++++++++++++++++++++++++++++++ src/mcdm.jl | 3 ++ test/runtests.jl | 5 -- test/testmcdm.jl | 63 ++++++++++++++++++----- 6 files changed, 187 insertions(+), 38 deletions(-) create mode 100644 src/lopcow.jl diff --git a/README.md b/README.md index b79a3a2..d5d6456 100644 --- a/README.md +++ b/README.md @@ -126,8 +126,7 @@ Please check out the reference manual [here](https://jbytecode.github.io/JMcDM/) - MEREC (MEthod based on the Removal Effects of Criteria) for determining weights - PIV (Proximity Indexed Value) method - SECA (Simultaneous Evaluation of Criteria and Alternatives) - - +- LOPCOW (LOgarithmic Percentage Change-driven Objective Weighting) ### SCDM Tools @@ -145,7 +144,6 @@ Please check out the reference manual [here](https://jbytecode.github.io/JMcDM/) - Game solver for zero sum games - ## Unimplemented methods - UTA - MAUT diff --git a/src/JMcDM.jl b/src/JMcDM.jl index b098dab..c6aa545 100755 --- a/src/JMcDM.jl +++ b/src/JMcDM.jl @@ -30,10 +30,6 @@ function __init__() end end - - - - # Abstract Types abstract type MCDMResult end abstract type SCDMResult end @@ -86,7 +82,6 @@ struct MCDMSetting fns::Array{F,1} where {F<:Function} end - # includes include("greynumber.jl") include("utilities.jl") @@ -117,6 +112,7 @@ include("codas.jl") include("psi.jl") include("moosra.jl") include("merec.jl") +include("lopcow.jl") include("summary.jl") @@ -128,9 +124,6 @@ include("piv.jl") include("copeland.jl") - - - # imports from modules import .Topsis: topsis, TopsisMethod, TopsisResult import .WPM: wpm, WPMResult, WPMMethod @@ -161,7 +154,7 @@ import .Entropy: entropy, EntropyResult import .AHP: ahp, ahp_consistency, ahp_RI, AHPResult, AHPConsistencyResult import .MEREC: merec, MERECResult, MERECMethod import .PIV: piv, PIVResult, PIVMethod - +import .LOPCOW: lopcow, LOPCOWResult, LOPCOWMethod import .SCDM: LaplaceResult, MaximinResult, MaximaxResult, MinimaxResult, MiniminResult import .SCDM: SavageResult, HurwiczResult, MLEResult, ExpectedRegretResult @@ -202,7 +195,7 @@ export MoosraMethod export ROVMethod export MERECMethod export PIVMethod - +export LOPCOWMethod export MCDMSetting @@ -236,8 +229,7 @@ export PSIResult export MoosraResult export MERECResult export PIVResult - - +export LOPCOWResult #  export SCDM types export SCDMResult @@ -252,8 +244,6 @@ export MLEResult export ExpectedRegretResult - - # export utility functions export euclidean export normalize @@ -292,7 +282,7 @@ export psi export moosra export merec export piv - +export lopcow #  export SCDM tools export laplace @@ -305,8 +295,6 @@ export hurwicz export mle export expectedregret - - #  export summary function export summary export mcdm diff --git a/src/lopcow.jl b/src/lopcow.jl new file mode 100644 index 0000000..b5f4941 --- /dev/null +++ b/src/lopcow.jl @@ -0,0 +1,128 @@ +# LOPCOW +module LOPCOW + +export lopcow, LOPCOWResult, LOPCOWMethod + +import ..MCDMMethod, ..MCDMResult, ..MCDMSetting +import ..Normalizations + +using ..Utilities + +struct LOPCOWMethod <: MCDMMethod + normalization::G where {G <: Function} +end + +LOPCOWMethod() = LOPCOWMethod(Normalizations.maxminrangenormalization) + +struct LOPCOWResult <: MCDMResult + decisionMatrix::Matrix + w::Vector +end + +""" + lopcow(decisionMat, fns; normalization) + +Apply LOPCOW (LOgarithmic Percentage Change-driven Objective Weighting) method for a given matrix and criteria types. + +# Arguments: + - `decisionMat::Matrix`: n × m matrix of objective values for n alternatives and m criteria + - `fns::Array{<:Function, 1}`: m-vector of functions to be applied on the columns. + - `normalization{<:Function}`: Optional normalization function. + +# Description +lopcow() applies the LOPCOW method to calculate objective weights using a decision matrix with +n alterntives subject to m criteria which are supposed to be either maximized or minimized. + +# Output +- `::LOPCOWResult`: LOPCOWResult object that holds multiple outputs including weighting and best index. + +# Examples +```julia-repl + +julia> decmat +9×17 Matrix{Float64}: + 21.8 14.1 10.7 1.6 1.8 770.0 12750.0 18.0 5100.0 1.5 9.1 1.054 4.196 29.407 7.03 15.08 9.705 + 16.4 8.5 13.9 1.2 1.3 524.0 12087.0 5.7 2941.0 2.208 15.2 1.123 3.86 5.228 14.724 32.103 19.0 + 14.5 7.0 2.3 0.2 0.2 238.0 3265.0 1.9 320.0 2.32 16.202 1.008 3.095 5.549 17.34 65.129 32.056 + 18.2 10.3 11.4 1.2 1.1 835.0 16037.0 21.3 4332.0 0.875 9.484 0.856 2.191 23.75 13.1 58.157 27.46 + 18.5 8.1 11.1 1.0 1.1 504.0 9464.0 1.4 1743.0 2.95 0.7 0.479 2.44 8.77 13.48 33.45 17.68 + 18.7 11.4 10.8 1.3 1.5 1227.0 24053.0 20.0 6521.0 0.733 1.6 0.857 2.377 4.985 11.743 26.732 24.485 + 18.5 12.6 10.8 1.4 1.8 912.0 18800.0 18.2 5300.0 1.29 8.27 0.558 0.635 5.22 13.829 31.914 7.515 + 16.4 6.7 12.6 0.9 0.9 951.0 16767.0 22.0 3917.0 2.46 3.9 0.724 0.568 4.491 14.357 28.869 7.313 + 15.2 6.3 6.9 0.5 0.5 1013.0 20170.0 10.97 4060.0 1.67 1.7 0.704 2.96 3.24 10.029 60.981 23.541 + +julia> fns = [maximum, maximum, maximum, maximum, maximum, maximum, maximum, maximum, maximum, maximum, minimum, minimum, minimum, minimum, minimum, minimum, minimum ]; + +julia> result = lopcow(decmat, fns); + +julia> result.w +17-element Vector{Float64}: + 0.04947396185854988 + 0.036623078374935315 + 0.08456624432002027 + 0.07055941647198624 + ⋮ + 0.07625360895444118 + 0.05507171491276535 + 0.05320727577078255 + 0.05340460620185558 + +``` +# References + +Ecer, F., & Pamucar, D. (2022). A novel LOPCOW‐DOBI multi‐criteria sustainability performance assessment methodology: An application in developing country banking sector. Omega, 112, 102690. https://doi.org/10.1016/j.omega.2022.102690 + +""" +function lopcow( + decisionMat::Matrix, + fns::Array{F,1}; + normalization::G = Normalizations.maxminrangenormalization + )::LOPCOWResult where {F<:Function, G<:Function} + + row, col = size(decisionMat) + colMax = colmaxs(decisionMat) + colMin = colmins(decisionMat) + + A = similar(decisionMat) + + zerotype = eltype(decisionMat[:, 1]) + + normalizedMat = normalization(decisionMat, fns) + + PV = zeros(zerotype, col) + + for i = 1:col + PV[i] = log(sqrt(sum(normalizedMat[:,i].*normalizedMat[:,i])/row)/std(normalizedMat[:,i]))*100 + end + + w = zeros(zerotype, col) + + for i = 1:col + w[i] = PV[i] ./ sum(PV) + end + + result = LOPCOWResult(decisionMat, w) + + return result +end + +""" + lopcow(setting) + +Apply LOPCOW (LOgarithmic Percentage Change-driven Objective Weighting) method for a given matrix and criteria types. + +# Arguments: + - `setting::MCDMSetting`: MCDMSetting object. + +# Description +lopcow() applies the LOPCOW method to rank n alterntives subject to m criteria which are supposed to be +either maximized or minimized. + +# Output +- `::LOPCOWResult`: LOPCOWResult object that holds multiple outputs including weighting and best index. +""" +function lopcow(setting::MCDMSetting)::LOPCOWResult + lopcow(setting.df, setting.fns) +end + +end # end of module LOPCOW \ No newline at end of file diff --git a/src/mcdm.jl b/src/mcdm.jl index d00a42d..7de2011 100644 --- a/src/mcdm.jl +++ b/src/mcdm.jl @@ -29,6 +29,7 @@ julia> subtypes(MCDMMethod) EdasMethod ElectreMethod GreyMethod + LOPCOWMethod MERECMethod MabacMethod MaircaMethod @@ -107,6 +108,8 @@ function mcdm( psi(df, fns) elseif method isa MERECMethod merec(df, fns) + elseif method isa LOPCOWMethod + merec(df, fns) elseif method isa PIVMethod piv(df, w, fns) else diff --git a/test/runtests.jl b/test/runtests.jl index 461e318..d907eef 100755 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,13 +1,11 @@ using Test - using JuMP, Ipopt, JMcDM import JMcDM.Game: game import JMcDM.DataEnvelop: dataenvelop import JMcDM.SECA: seca - const testGreyNumbers = true const testUtilityFunctions = true const testMCDMFunctions = true @@ -16,19 +14,16 @@ const testLPBasedFunctions = true const testGreyMCDMFunctions = true const testSummary = true - if testGreyNumbers @info "Grey Numbers tests ..." include("./testgreynumber.jl") end - if testUtilityFunctions @info "Utility tests ..." include("./testutility.jl") end - if testSCDMFunctions @info "SCDM tests ..." include("./testscdm.jl") diff --git a/test/testmcdm.jl b/test/testmcdm.jl index 224ea9d..c700248 100644 --- a/test/testmcdm.jl +++ b/test/testmcdm.jl @@ -227,8 +227,6 @@ @test result3.bestIndex == result.bestIndex end - - @testset "TOPSIS" begin tol = 0.00001 decmat = hcat( @@ -291,7 +289,6 @@ @test result.bestIndex == result3.bestIndex end - @testset "ELECTRE" begin tol = 0.00001 w = [0.110, 0.035, 0.379, 0.384, 0.002, 0.002, 0.010, 0.077] @@ -517,7 +514,6 @@ @test ahp_RI(16) == 1.59 end - @testset "AHP - Consistency" begin tol = 0.00001 @@ -562,7 +558,6 @@ ) end - @testset "AHP" begin tol = 0.00001 @@ -643,7 +638,6 @@ ) end - @testset "NDS" begin @testset "NDS - all maximum" begin cases = [ @@ -687,7 +681,6 @@ end end - @testset "SAW" begin @testset "Example 1: 4 criteria × 4 alternatives" begin @@ -759,8 +752,7 @@ end end - - @testset "ARAS Additive Ratio Assessment" begin + @testset "ARAS" begin tol = 0.0001 @@ -797,7 +789,6 @@ @test result3.bestIndex == result.bestIndex end - @testset "WPM" begin tol = 0.0001 decmat = [ @@ -993,8 +984,6 @@ @test result3.bestIndex == result.bestIndex end - - @testset "MAIRCA" begin tol = 0.0001 @@ -1434,5 +1423,53 @@ @test isa(result, SECAResult) @test result.bestIndex == 1 @test isapprox(result.scores, [0.86004480, 0.83316666, 0.83316666], atol = tol) - end + end + + @testset "LOPCOW" begin + tol = 0.0001 + decmat = [ + 21.8 14.1 10.7 1.6 1.8 770.0 12750.0 18.0 5100.0 1.5 9.1 1.054 4.196 29.407 7.03 15.08 9.705; + 16.4 8.5 13.9 1.2 1.3 524.0 12087.0 5.7 2941.0 2.208 15.2 1.123 3.86 5.228 14.724 32.103 19.0; + 14.5 7.0 2.3 0.2 0.2 238.0 3265.0 1.9 320.0 2.32 16.202 1.008 3.095 5.549 17.34 65.129 32.056; + 18.2 10.3 11.4 1.2 1.1 835.0 16037.0 21.3 4332.0 0.875 9.484 0.856 2.191 23.75 13.1 58.157 27.46; + 18.5 8.1 11.1 1.0 1.1 504.0 9464.0 1.4 1743.0 2.95 0.7 0.479 2.44 8.77 13.48 33.45 17.68; + 18.7 11.4 10.8 1.3 1.5 1227.0 24053.0 20.0 6521.0 0.733 1.6 0.857 2.377 4.985 11.743 26.732 24.485; + 18.5 12.6 10.8 1.4 1.8 912.0 18800.0 18.2 5300.0 1.29 8.27 0.558 0.635 5.22 13.829 31.914 7.515; + 16.4 6.7 12.6 0.9 0.9 951.0 16767.0 22.0 3917.0 2.46 3.9 0.724 0.568 4.491 14.357 28.869 7.313; + 15.2 6.3 6.9 0.5 0.5 1013.0 20170.0 10.97 4060.0 1.67 1.7 0.704 2.96 3.24 10.029 60.981 23.541 + ] + + fns = [maximum, maximum, maximum, maximum, maximum, maximum, maximum, maximum, maximum, maximum, minimum, minimum, minimum, minimum, minimum, minimum, minimum ] + + result = lopcow(decmat, fns) + @test result isa LOPCOWResult + @test isapprox( + result.w, + [ + 0.0494739618585499, + 0.0366230783749353, + 0.0845662443200203, + 0.0705594164719862, + 0.0637406420860843, + 0.0660734602415126, + 0.0699299682542297, + 0.0504004077191502, + 0.0689844249442893, + 0.0491618867560788, + 0.0555597725236513, + 0.0487457716462668, + 0.0482437589634006, + 0.0762536089544412, + 0.0550717149127653, + 0.0532072757707825, + 0.0534046062018556, + ], + atol = tol, + ) + + setting = MCDMSetting(decmat, zeros(17), fns) + result2 = lopcow(setting) + @test result2 isa LOPCOWResult + @test result2.w == result.w + end end