Skip to content

Commit 93cb477

Browse files
committed
Implement Cradis method
1 parent ced4217 commit 93cb477

6 files changed

Lines changed: 350 additions & 39 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- Remove redundant code in `saw`.
55
- Add Julia v1.12 to GitHub workflows.
66
- Add Borda count method.
7+
- Add CRADIS method.
78

89

910
### 0.8.2

docs/src/mcdms.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,9 @@ JMcDM.bestworst
202202
## RAM
203203
```@docs
204204
JMcDM.ram
205+
```
206+
207+
## CRADIS
208+
```@docs
209+
JMcDM.cradis
205210
```

src/JMcDM.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ include("ram.jl")
134134

135135
include("copeland.jl")
136136
include("borda.jl")
137+
include("cradis.jl")
137138

138139
include("fuzzy/fuzzy.jl")
139140

@@ -174,6 +175,7 @@ import .TODIM: todim, TODIMResult, TODIMMethod
174175
import .CILOS: cilos, CILOSResult
175176
import .IDOCRIW: idocriw, IDOCRIWResult
176177
import .RAM: ram, RAMResult, RAMMethod
178+
import .CRADIS: cradis, CRADISMethod, CRADISResult
177179
import .Borda: borda
178180

179181
import .SCDM: LaplaceResult, MaximinResult, MaximaxResult, MinimaxResult, MiniminResult
@@ -219,6 +221,7 @@ export OCRAMethod
219221
export LMAWMethod
220222
export TODIMMethod
221223
export RAMMethod
224+
export CradisMethod
222225

223226

224227
export MCDMSetting
@@ -260,6 +263,7 @@ export TODIMResult
260263
export CILOSResult
261264
export IDOCRIWResult
262265
export RAMResult
266+
export CRADISResult
263267

264268
#  export SCDM types
265269
export SCDMResult
@@ -320,6 +324,7 @@ export cilos
320324
export idocriw
321325
export ram
322326
export borda
327+
export cradis
323328

324329
#  export SCDM tools
325330
export laplace

src/cradis.jl

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
module CRADIS
2+
3+
4+
import ..MCDMMethod, ..MCDMResult, ..MCDMSetting
5+
import ..Normalizations
6+
7+
using ..Utilities: weightise, colmins, colmaxs, rowsums
8+
9+
struct CradisMethod <: MCDMMethod
10+
normalization::G where {G<:Function}
11+
end
12+
13+
CradisMethod() = CradisMethod(Normalizations.dividebycolumnmaxminnormalization)
14+
15+
const CRADISMethod = CradisMethod
16+
17+
struct CRADISResult <: MCDMResult
18+
decisionMat::Matrix
19+
normalizedDecisionMat::Matrix
20+
weights::Vector{Float64}
21+
weightedNormalizedDecisionMat::Matrix{Float64}
22+
tideal::Float64
23+
tantiideal::Float64
24+
idealAlternative::Vector{Float64}
25+
antiIdealAlternative::Vector{Float64}
26+
dplus::Matrix{Float64}
27+
dminus::Matrix{Float64}
28+
splus::Vector{Float64}
29+
sminus::Vector{Float64}
30+
splus0::Float64
31+
sminus0::Float64
32+
kplus::Vector{Float64}
33+
kminus::Vector{Float64}
34+
q::Vector{Float64}
35+
scores::Vector{Float64}
36+
ranking::Vector{Int64}
37+
bestIndex::Int64
38+
end
39+
40+
export cradis, CRADISMethod, CRADISResult
41+
42+
43+
44+
"""
45+
46+
cradis(decisionMat::Matrix, weights::Array{Float64,1}, fs::Array{F,1}; normalization::G=Normalizations.dividebycolumnmaxminnormalization) where {F<:Function,G<:Function}
47+
48+
49+
# Description
50+
51+
The CRADIS (Criteria Ranking and Decision Support) method is a multi-criteria decision-making
52+
technique that evaluates alternatives based on multiple criteria. It normalizes the decision
53+
matrix, applies weights to the criteria, and calculates scores for each alternative to determine
54+
their ranking.
55+
56+
# Arguments
57+
58+
- `decisionMat::Matrix`: A matrix representing the decision matrix, where rows correspond to
59+
alternatives and columns correspond to criteria.
60+
- `weights::Array{Float64,1}`: An array of weights for each criterion, indicating their relative
61+
importance.
62+
- `fs::Array{F,1}`: An array of functions that define the direction of optimization for each criterion
63+
(e.g., `maximum` for benefit criteria, `minimum` for cost criteria).
64+
- `normalization::G`: An optional normalization function to be applied to the decision matrix.
65+
Default is `Normalizations.dividebycolumnmaxminnormalization`.
66+
67+
# References
68+
69+
- Puška, A., I. Hodžić, and A. Štilić. "Evaluating the knowledge economies within the European
70+
Union: A global knowledge index ranking via entropy and CRADIS methodologies." International
71+
Journal of Knowledge and Innovation Studies 1.2 (2023): 103-115.
72+
"""
73+
function cradis(
74+
decisionMat::Matrix,
75+
weights::Array{Float64,1},
76+
fs::Array{F,1};
77+
normalization::G=Normalizations.dividebycolumnmaxminnormalization)::CRADISResult where {F<:Function,G<:Function}
78+
79+
n, p = size(decisionMat)
80+
81+
normalizedDecisionMat = normalization(decisionMat, fs)
82+
83+
weightedNormalizedDecisionMat = weightise(normalizedDecisionMat, weights)
84+
85+
t_ideal = maximum(weightedNormalizedDecisionMat)
86+
t_anti_ideal = minimum(weightedNormalizedDecisionMat)
87+
88+
idealAlternative = colmaxs(weightedNormalizedDecisionMat)
89+
antiIdealAlternative = colmins(weightedNormalizedDecisionMat)
90+
91+
dplus = Array{Float64,2}(undef, n, p)
92+
dminus = Array{Float64,2}(undef, n, p)
93+
for i in 1:n
94+
for j in 1:p
95+
dplus[i, j] = t_ideal - weightedNormalizedDecisionMat[i, j]
96+
dminus[i, j] = weightedNormalizedDecisionMat[i, j] - t_anti_ideal
97+
end
98+
end
99+
100+
splus = rowsums(dplus)
101+
sminus = rowsums(dminus)
102+
103+
splus0 = sum(t_ideal .- idealAlternative)
104+
sminus0 = sum(idealAlternative .- t_anti_ideal)
105+
106+
kplus = splus0 ./ splus
107+
kminus = sminus ./ sminus0
108+
109+
q = (kplus .+ kminus) ./ 2
110+
ranking = sortperm(q)
111+
bestIndex = ranking |> last
112+
113+
return CRADISResult(
114+
decisionMat,
115+
normalizedDecisionMat,
116+
weights,
117+
weightedNormalizedDecisionMat,
118+
t_ideal,
119+
t_anti_ideal,
120+
idealAlternative,
121+
antiIdealAlternative,
122+
dplus,
123+
dminus,
124+
splus,
125+
sminus,
126+
splus0,
127+
sminus0,
128+
kplus,
129+
kminus,
130+
q,
131+
q,
132+
ranking,
133+
bestIndex)
134+
135+
end
136+
137+
function cradis(setting::MCDMSetting; normalization::G=Normalizations.dividebycolumnmaxminnormalization)::CRADISResult where {G<:Function}
138+
cradis(setting.df, setting.weights, setting.fns, normalization=normalization)
139+
end
140+
141+
end # end of module CRADIS

test/mcdm/testcradis.jl

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
@testset "Cradis" begin
2+
3+
# This test data is taken from the paper
4+
# Puška, A., I. Hodžić, and A. Štilić. "Evaluating the knowledge economies within the European
5+
# Union: A global knowledge index ranking via entropy and CRADIS methodologies." International
6+
# Journal of Knowledge and Innovation Studies 1.2 (2023): 103-115.
7+
#
8+
# The implementation is also based on the same paper.
9+
10+
eps = 0.01
11+
12+
decmat = [
13+
79.60 67.40 64.70 43.50 66.10 67.80 79.40;
14+
84.00 64.90 63.20 43.70 59.80 67.60 78.00;
15+
69.80 58.70 58.60 34.10 56.50 55.80 61.60;
16+
77.50 50.60 67.10 41.70 65.10 55.30 68.90;
17+
74.00 60.70 59.50 32.10 57.60 58.30 68.40;
18+
83.10 63.40 59.10 39.10 55.80 65.70 73.90;
19+
79.80 57.50 63.50 50.30 70.80 73.60 83.40;
20+
79.90 64.20 62.50 43.30 71.10 63.40 76.50;
21+
84.30 68.20 61.30 50.70 71.80 66.70 85.80;
22+
78.50 55.20 53.50 45.70 65.20 65.50 75.30;
23+
75.70 63.70 61.10 47.30 61.90 66.50 79.40;
24+
72.80 47.00 47.20 34.80 53.00 52.50 63.90;
25+
70.60 67.10 47.80 34.90 56.70 66.20 67.00;
26+
70.40 60.30 55.30 42.90 62.40 72.30 81.80;
27+
75.40 62.40 52.40 42.30 55.50 59.70 68.70;
28+
80.50 62.80 59.30 32.60 63.10 63.30 71.70;
29+
79.30 58.20 56.90 32.60 62.20 62.40 73.90;
30+
77.20 64.10 66.40 45.90 72.60 65.40 82.40;
31+
78.20 52.70 58.00 46.70 71.50 68.50 73.80;
32+
83.80 68.10 63.00 48.80 71.60 65.60 80.60;
33+
83.40 55.30 54.80 31.60 57.90 57.80 69.30;
34+
85.70 62.10 63.30 34.90 57.30 59.50 77.20;
35+
60.20 57.70 55.00 32.00 54.20 58.90 64.60;
36+
80.10 69.10 58.30 30.50 54.20 56.60 69.00;
37+
82.40 65.50 61.40 39.80 63.70 62.60 75.50;
38+
79.10 59.70 55.40 37.40 62.00 60.40 73.10;
39+
82.30 61.20 62.40 54.70 72.40 68.10 85.70
40+
]
41+
42+
normalized_expected = [
43+
0.929 0.975 0.964 0.795 0.910 0.921 0.925;
44+
0.980 0.939 0.942 0.799 0.824 0.918 0.909;
45+
0.814 0.849 0.873 0.623 0.778 0.758 0.718;
46+
0.904 0.732 1.000 0.762 0.897 0.751 0.803;
47+
0.863 0.878 0.887 0.587 0.793 0.792 0.797;
48+
0.970 0.918 0.881 0.715 0.769 0.893 0.861;
49+
0.931 0.832 0.946 0.920 0.975 1.000 0.972;
50+
0.932 0.929 0.931 0.792 0.979 0.861 0.892;
51+
0.984 0.987 0.914 0.927 0.989 0.906 1.000;
52+
0.916 0.799 0.797 0.835 0.898 0.890 0.878;
53+
0.883 0.922 0.911 0.865 0.853 0.904 0.925;
54+
0.849 0.680 0.703 0.636 0.730 0.713 0.745;
55+
0.824 0.971 0.712 0.638 0.781 0.899 0.781;
56+
0.821 0.873 0.824 0.784 0.860 0.982 0.953;
57+
0.880 0.903 0.781 0.773 0.764 0.811 0.801;
58+
0.939 0.909 0.884 0.596 0.869 0.860 0.836;
59+
0.925 0.842 0.848 0.596 0.857 0.848 0.861;
60+
0.901 0.928 0.990 0.839 1.000 0.889 0.960;
61+
0.912 0.763 0.864 0.854 0.985 0.931 0.860;
62+
0.978 0.986 0.939 0.892 0.986 0.891 0.939;
63+
0.973 0.800 0.817 0.578 0.798 0.785 0.808;
64+
1.000 0.899 0.943 0.638 0.789 0.808 0.900;
65+
0.702 0.835 0.820 0.585 0.747 0.800 0.753;
66+
0.935 1.000 0.869 0.558 0.747 0.769 0.804;
67+
0.961 0.948 0.915 0.728 0.877 0.851 0.880;
68+
0.923 0.864 0.826 0.684 0.854 0.821 0.852;
69+
0.960 0.886 0.930 1.000 0.997 0.925 0.999
70+
]
71+
72+
weighted_normalized_expected = [
73+
0.0260 0.0878 0.0990 0.2943 0.1216 0.1365 0.1179;
74+
0.0275 0.0845 0.0967 0.2957 0.1100 0.1361 0.1158;
75+
0.0228 0.0765 0.0896 0.2307 0.1040 0.1124 0.0915;
76+
0.0253 0.0659 0.1027 0.2821 0.1198 0.1114 0.1023;
77+
0.0242 0.0791 0.0910 0.2172 0.1060 0.1174 0.1016;
78+
0.0272 0.0826 0.0904 0.2645 0.1027 0.1323 0.1097;
79+
0.0261 0.0749 0.0971 0.3403 0.1303 0.1482 0.1239;
80+
0.0261 0.0836 0.0956 0.2930 0.1308 0.1277 0.1136;
81+
0.0276 0.0888 0.0938 0.3430 0.1321 0.1343 0.1274;
82+
0.0257 0.0719 0.0818 0.3092 0.1200 0.1319 0.1118;
83+
0.0247 0.0830 0.0935 0.3200 0.1139 0.1339 0.1179;
84+
0.0238 0.0612 0.0722 0.2354 0.0975 0.1057 0.0949;
85+
0.0231 0.0874 0.0731 0.2361 0.1043 0.1333 0.0995;
86+
0.0230 0.0785 0.0846 0.2902 0.1148 0.1456 0.1215;
87+
0.0246 0.0813 0.0802 0.2862 0.1021 0.1202 0.1020;
88+
0.0263 0.0818 0.0907 0.2206 0.1161 0.1275 0.1065;
89+
0.0259 0.0758 0.0870 0.2206 0.1145 0.1257 0.1097;
90+
0.0252 0.0835 0.1016 0.3105 0.1336 0.1317 0.1224;
91+
0.0256 0.0686 0.0887 0.3160 0.1316 0.1380 0.1096;
92+
0.0274 0.0887 0.0964 0.3302 0.1318 0.1321 0.1197;
93+
0.0273 0.0720 0.0838 0.2138 0.1066 0.1164 0.1029;
94+
0.0280 0.0809 0.0968 0.2361 0.1054 0.1198 0.1146;
95+
0.0197 0.0752 0.0841 0.2165 0.0997 0.1186 0.0959;
96+
0.0262 0.0900 0.0892 0.2064 0.0997 0.1140 0.1025;
97+
0.0269 0.0853 0.0939 0.2693 0.1172 0.1261 0.1121;
98+
0.0259 0.0778 0.0848 0.2530 0.1141 0.1216 0.1086;
99+
0.0269 0.0797 0.0955 0.3701 0.1332 0.1371 0.1273
100+
]
101+
102+
s_plus_expected = [
103+
1.7074, 1.7242, 1.8631, 1.7811, 1.8541, 1.7811, 1.6498, 1.7201, 1.6435, 1.7383,
104+
1.7036, 1.8997, 1.8337, 1.7323, 1.7939, 1.8211, 1.8314, 1.6820, 1.7126, 1.6644,
105+
1.8678, 1.8088, 1.8808, 1.8626, 1.7597, 1.8049, 1.6208
106+
]
107+
108+
s_minus_expected = [
109+
0.7454, 0.7286, 0.5897, 0.6718, 0.5987, 0.6717, 0.8031, 0.7327, 0.8093, 0.7146,
110+
0.7492, 0.5531, 0.6191, 0.7206, 0.6589, 0.6317, 0.6215, 0.7708, 0.7403, 0.7885,
111+
0.5850, 0.6440, 0.5720, 0.5902, 0.6931, 0.6479, 0.8321
112+
]
113+
114+
k_plus_expected = [
115+
0.9316, 0.9225, 0.8537, 0.8930, 0.8579, 0.8930, 0.9641, 0.9247, 0.9678, 0.9150,
116+
0.9336, 0.8373, 0.8674, 0.9182, 0.8867, 0.8734, 0.8685, 0.9456, 0.9288, 0.9557,
117+
0.8516, 0.8794, 0.8457, 0.8539, 0.9039, 0.8813, 0.9814
118+
]
119+
120+
k_minus_expected = [
121+
0.8645, 0.8450, 0.6839, 0.7791, 0.6943, 0.7790, 0.9314, 0.8497, 0.9386, 0.8287,
122+
0.8689, 0.6414, 0.7181, 0.8357, 0.7642, 0.7326, 0.7207, 0.8939, 0.8585, 0.9144,
123+
0.6785, 0.7469, 0.6634, 0.6845, 0.8039, 0.7515, 0.9650
124+
]
125+
126+
q_expected = [
127+
0.8981, 0.8838, 0.7688, 0.8361, 0.7761, 0.8360, 0.9477, 0.8872, 0.9532, 0.8719,
128+
0.9013, 0.7393, 0.7927, 0.8769, 0.8254, 0.8030, 0.7946, 0.9198, 0.8937, 0.9350,
129+
0.7650, 0.8131, 0.7545, 0.7692, 0.8539, 0.8164, 0.9732
130+
]
131+
132+
rank_expected = [
133+
7, 10, 24, 14, 22, 15, 3, 9, 2, 12, 6, 27, 21, 11, 16, 19, 20, 5, 8, 4, 25, 18, 26, 23, 13, 17, 1
134+
]
135+
136+
137+
weights = [0.0280, 0.0900, 0.1027, 0.3701, 0.1336, 0.1482, 0.1274]
138+
139+
dirs = [maximum for i in 1:7]
140+
141+
result = cradis(decmat, weights, dirs)
142+
143+
@test isapprox(result.normalizedDecisionMat, normalized_expected, atol=eps)
144+
@test isapprox(result.weightedNormalizedDecisionMat, weighted_normalized_expected, atol=eps)
145+
@test isapprox(result.splus, s_plus_expected, atol=eps)
146+
@test isapprox(result.sminus, s_minus_expected, atol=eps)
147+
@test isapprox(result.kplus, k_plus_expected, atol=eps)
148+
@test isapprox(result.kminus, k_minus_expected, atol=eps)
149+
@test isapprox(result.q, q_expected, atol=eps)
150+
@test result.scores == result.q
151+
152+
@test result.bestIndex == 27
153+
154+
ranks = similar(result.ranking)
155+
for (r, idx) in enumerate(reverse(result.ranking))
156+
ranks[idx] = r
157+
end
158+
@test ranks == rank_expected
159+
end

0 commit comments

Comments
 (0)