Skip to content

Commit a5c13a7

Browse files
committed
implement copras
1 parent 7b72e74 commit a5c13a7

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

src/main/scala/copras.scala

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package org.expr.mcdm
2+
3+
case class CoprasResult(
4+
normalizedMat: Mat,
5+
weightedNormalizedMat: Mat,
6+
splus: Vec,
7+
sminus: Vec,
8+
Q: Vec,
9+
Z: Double,
10+
maxQ: Double,
11+
rankings: VecInt,
12+
bestIndex: Int,
13+
scores: Vec,
14+
) extends MCDMResult
15+
16+
def copras(
17+
decmat: Mat,
18+
weights: Vec,
19+
directions: Array[Direction],
20+
normalization: NormalizationFunction =
21+
Normalization.DivideByColumnnsSumNormalization,
22+
options: Map[String, Any] = Map.empty
23+
): CoprasResult =
24+
25+
val (nrows, ncols) = Matrix.size(decmat)
26+
27+
val normalizedMat = normalization(decmat, weights, directions)
28+
val weightedNormalizedMat = Matrix.weightizeColumns(normalizedMat, weights)
29+
30+
var sPlus = Matrix.zeros(nrows)
31+
var sMinus = Matrix.zeros(nrows)
32+
33+
for row <- 0 until nrows do
34+
for col <- 0 until ncols do
35+
if directions(col) == Direction.Maximize then
36+
sPlus(row) = sPlus(row) + Matrix.elementat(weightedNormalizedMat, row, col)
37+
else if directions(col) == Direction.Minimize then
38+
sMinus(row) = sMinus(row) + Matrix.elementat(weightedNormalizedMat, row, col)
39+
40+
41+
var Q = Matrix.zeros(nrows)
42+
var Z = sMinus.map(x => 1.0 / x).sum
43+
44+
for row <- 0 until nrows do
45+
Q(row) = sPlus(row) + (sMinus.sum / (sMinus(row) * Z))
46+
47+
val maxQ = Q.max
48+
val scores = Q.map(x => x / maxQ)
49+
50+
val rankings = scores.zipWithIndex.sortBy(-_._1).map(_._2)
51+
val bestIndex = rankings.head
52+
53+
CoprasResult(
54+
normalizedMat,
55+
weightedNormalizedMat,
56+
sPlus,
57+
sMinus,
58+
Q,
59+
Z,
60+
maxQ,
61+
rankings,
62+
bestIndex,
63+
scores
64+
)

src/test/scala/testcopras.scala

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import munit.Assertions as A
2+
3+
import org.expr.mcdm.copras
4+
import org.expr.mcdm.Direction.{Maximize, Minimize}
5+
import org.expr.mcdm.Matrix
6+
7+
class TestCopras extends munit.FunSuite {
8+
test("Copras Example - 1") {
9+
10+
val decmat = Array(
11+
Array(2.50, 240, 57, 45, 1.10, 0.333333),
12+
Array(2.50, 285, 60, 75, 4.00, 0.428571),
13+
Array(4.50, 320, 100, 65, 7.50, 1.111111),
14+
Array(4.50, 365, 100, 90, 7.50, 1.111111),
15+
Array(5.00, 400, 100, 90, 11.00, 1.111111),
16+
Array(2.50, 225, 60, 45, 1.10, 0.333333),
17+
Array(2.50, 270, 57, 60, 4.00, 0.428571),
18+
Array(4.50, 330, 100, 70, 7.50, 1.111111),
19+
Array(4.50, 365, 100, 80, 7.50, 1.111111),
20+
Array(5.00, 380, 110, 65, 8.00, 1.111111),
21+
Array(2.50, 285, 65, 80, 4.00, 0.400000),
22+
Array(4.00, 280, 75, 65, 4.00, 0.400000),
23+
Array(4.50, 365, 102, 95, 7.50, 1.111111),
24+
Array(4.50, 400, 102, 95, 7.50, 1.111111),
25+
Array(6.00, 450, 110, 95, 11.00, 1.176471),
26+
Array(6.00, 510, 110, 105, 11.00, 1.176471),
27+
Array(6.00, 330, 140, 110, 18.50, 1.395349),
28+
Array(2.50, 240, 65, 80, 4.00, 0.400000),
29+
Array(4.00, 280, 75, 75, 4.00, 0.400000),
30+
Array(4.50, 355, 102, 95, 7.50, 1.111111),
31+
Array(4.50, 385, 102, 90, 7.50, 1.111111),
32+
Array(5.00, 385, 114, 95, 7.50, 1.000000),
33+
Array(6.00, 400, 110, 90, 11.00, 1.000000),
34+
Array(6.00, 480, 110, 95, 15.00, 1.000000),
35+
Array(6.00, 440, 140, 100, 18.50, 1.200000),
36+
Array(6.00, 500, 140, 100, 18.50, 1.200000),
37+
Array(5.00, 450, 125, 100, 15.00, 1.714286),
38+
Array(6.00, 500, 150, 125, 18.50, 1.714286),
39+
Array(6.00, 515, 180, 140, 22.00, 2.307692),
40+
Array(7.00, 550, 200, 150, 30.00, 2.307692),
41+
Array(6.00, 500, 180, 140, 15.00, 2.307692),
42+
Array(6.00, 500, 180, 140, 18.50, 2.307692),
43+
Array(6.00, 500, 180, 140, 22.00, 2.307692),
44+
Array(7.00, 500, 180, 140, 30.00, 2.307692),
45+
Array(7.00, 500, 200, 140, 37.00, 2.307692),
46+
Array(7.00, 500, 200, 140, 45.00, 2.307692),
47+
Array(7.00, 500, 200, 140, 55.00, 2.307692),
48+
Array(7.00, 500, 200, 140, 75.00, 2.307692)
49+
)
50+
51+
val weights = Array(0.1667, 0.1667, 0.1667, 0.1667, 0.1667, 0.1667)
52+
53+
val fns = Array(Maximize, Maximize, Maximize, Maximize, Maximize, Minimize)
54+
55+
val result = copras(decmat, weights, fns)
56+
57+
val expectedScores = Array(0.44194, 0.44395, 0.41042, 0.44403, 0.48177,
58+
0.44074, 0.42430, 0.41737, 0.43474, 0.44382, 0.46625, 0.48602, 0.45019,
59+
0.45825, 0.51953, 0.54265, 0.56134, 0.45588, 0.49532, 0.44788, 0.45014,
60+
0.48126, 0.51586, 0.56243, 0.58709, 0.60091, 0.51850, 0.61085, 0.65888,
61+
0.75650, 0.61430, 0.63486, 0.65542, 0.72065, 0.77680, 0.82379, 0.88253,
62+
1.00000);
63+
64+
A.assert(Matrix.elementwise_equal(result.scores, expectedScores, 1e-5))
65+
66+
A.assert(result.bestIndex == 37)
67+
}
68+
}

0 commit comments

Comments
 (0)