-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMatrixArithmetic.scala
132 lines (119 loc) · 5.59 KB
/
MatrixArithmetic.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package competitivescala.numbers
object MatrixArithmetic {
def dimensions[T](matrix: IndexedSeq[IndexedSeq[T]]): (Int, Int) = {
require(matrix.nonEmpty && matrix.head.nonEmpty)
val (m, n) = (matrix.size, matrix.head.size)
require(matrix.forall(_.size == n))
(m, n)
}
def elementwise[T](op: (T, T) => T)(matrix1: IndexedSeq[IndexedSeq[T]], matrix2: IndexedSeq[IndexedSeq[T]]): IndexedSeq[IndexedSeq[T]] = {
val ((m1, n1), (m2, n2)) = (dimensions(matrix1), dimensions(matrix2))
require(m1 == m2 && n1 == n2)
IndexedSeq.tabulate(m1, n1)((i, j) => op(matrix1(i)(j), matrix2(i)(j)))
}
def multiply[N](matrix1: IndexedSeq[IndexedSeq[N]], matrix2: IndexedSeq[IndexedSeq[N]])(implicit ev: Numeric[N]): IndexedSeq[IndexedSeq[N]] = {
import ev._
val ((m1, n1), (m2, n2)) = (dimensions(matrix1), dimensions(matrix2))
require(n1 == m2)
IndexedSeq.tabulate(m1, n2)((i, j) => (0 until n1).foldLeft(zero)((acc, k) => acc + matrix1(i)(k) * matrix2(k)(j)))
}
def gaussianElimination[N](matrix: IndexedSeq[IndexedSeq[N]], precision: N)(implicit ev: Fractional[N]): IndexedSeq[IndexedSeq[N]] = {
import ev._
val (m, n) = dimensions(matrix)
val (reducedMatrix, _) = (0 until m).foldLeft((matrix, -1)) { case ((matrix1, r), j) =>
val k = (r + 1 until m).maxBy(i => abs(matrix1(i)(j)))
val pivot = matrix1(k)(j)
if(abs(pivot) > precision) {
val r1 = r + 1
val matrix2 = (0 until n).foldLeft(matrix1)((current, l) => current.updated(k, current(k).updated(l, current(k)(l) / pivot)))
val matrix3 = (0 until n).foldLeft(matrix2)((current, l) => current.updated(k, current(k).updated(l, current(r1)(l))).updated(r1, current(r1).updated(l, current(k)(l))))
val matrix4 = (0 until m).foldLeft(matrix3) { (current, i) =>
val v = current(i)(j)
if(i != r1) {
(0 until n).foldLeft(current)((current, l) => current.updated(i, current(i).updated(l, current(i)(l) - current(r1)(l) * v)))
} else {
current
}
}
(matrix4, r1)
} else {
(matrix1, r)
}
}
reducedMatrix
}
def identity[N](n: Int)(implicit ev: Numeric[N]): IndexedSeq[IndexedSeq[N]] = IndexedSeq.tabulate(n, n)((i, j) => if(i == j) ev.one else ev.zero)
def inverse[N](matrix: IndexedSeq[IndexedSeq[N]], precision: N)(implicit ev: Fractional[N]): Option[IndexedSeq[IndexedSeq[N]]] = {
import ev._
val (m, n) = dimensions(matrix)
require(m == n)
val matrices = IndexedSeq(matrix, identity(m))
val result = gaussianElimination(IndexedSeq.tabulate(m, 2 * m)((i, j) => matrices(j / m)(i)(j % m)), precision)
if(abs(result(m - 1)(m - 1) - one) <= precision) {
Some(IndexedSeq.tabulate(m, m)((i, j) => result(i)(m + j)))
} else {
None
}
}
def multiplyGroup[N](matrix1: IndexedSeq[IndexedSeq[N]], matrix2: IndexedSeq[IndexedSeq[N]], mod: N)(implicit ev: Integral[N]): IndexedSeq[IndexedSeq[N]] = {
import ev._
val ((m1, n1), (m2, n2)) = (dimensions(matrix1), dimensions(matrix2))
require(n1 == m2)
IndexedSeq.tabulate(m1, n2)((i, j) => (((0 until n1).foldLeft(zero)((acc, k) => acc + matrix1(i)(k) * matrix2(k)(j)) % mod) + mod) % mod)
}
def gaussianEliminationGroup[N](matrix: IndexedSeq[IndexedSeq[N]], mod: N)(implicit ev: Integral[N]): IndexedSeq[IndexedSeq[N]] = {
import ev._
def positiveMod(a: N): N = ((a % mod) + mod) % mod
def modularInverse(a: N): N = {
def iterate(tn: N, t: N, rn: N, r: N): N = {
if (r != zero) {
val q = rn / r
iterate(t, tn - q * t, r, rn - q * r)
} else {
if (rn > one) throw new Exception else if (tn < zero) tn + mod else tn
}
}
iterate(zero, one, mod, a)
}
val (m, n) = dimensions(matrix)
val (reducedMatrix, _) = (0 until m).foldLeft((matrix.map(_.map(positiveMod)), -1)) { case ((matrix1, r), j) =>
(r + 1 until m).find(i => matrix1(i)(j) != zero) match {
case Some(k) =>
val pivot = matrix1(k)(j)
val pivotInverse = modularInverse(pivot)
val r1 = r + 1
val matrix2 = (0 until n).foldLeft(matrix1)((current, l) => current.updated(k, current(k).updated(l, (current(k)(l) * pivotInverse) % mod)))
val matrix3 = (0 until n).foldLeft(matrix2)((current, l) => current.updated(k, current(k).updated(l, current(r1)(l))).updated(r1, current(r1).updated(l, current(k)(l))))
val matrix4 = (0 until m).foldLeft(matrix3) { (current, i) =>
val v = current(i)(j)
if(i != r1) {
(0 until n).foldLeft(current)((current, l) => current.updated(i, current(i).updated(l, positiveMod(current(i)(l) - current(r1)(l) * v))))
} else {
current
}
}
(matrix4, r1)
case None => (matrix1, r)
}
}
reducedMatrix
}
def inverseGroup[N](matrix: IndexedSeq[IndexedSeq[N]], mod: N)(implicit ev: Integral[N]): Option[IndexedSeq[IndexedSeq[N]]] = {
import ev._
val (m, n) = dimensions(matrix)
require(m == n)
val matrices = IndexedSeq(matrix, identity(m))
val result = gaussianEliminationGroup(IndexedSeq.tabulate(m, 2 * m)((i, j) => matrices(j / m)(i)(j % m)), mod)
if(result(m - 1)(m - 1) == one) {
Some(IndexedSeq.tabulate(m, m)((i, j) => result(i)(m + j)))
} else {
None
}
}
def rotateClockwise[T](matrix: IndexedSeq[IndexedSeq[T]], i: Int = 1): IndexedSeq[IndexedSeq[T]] = ((i % 4) + 4) % 4 match {
case 0 => matrix
case 1 => matrix.transpose.map(_.reverse)
case 2 => matrix.reverse.map(_.reverse)
case 3 => matrix.transpose.reverse
}
}