Chris@5
|
1
|
Chris@94
|
2 module yetilab.matrix.matrix;
|
Chris@94
|
3
|
Chris@94
|
4 // A matrix is an array of fvectors (i.e. primitive double[]s).
|
Chris@94
|
5
|
Chris@94
|
6 // A matrix can be either RowMajor, akin to a C multidimensional array
|
Chris@96
|
7 // in which each row is a separate fvector, or ColumnMajor, akin to a
|
Chris@94
|
8 // FORTAN multidimensional array in which each column is a separate
|
Chris@96
|
9 // fvector. The default is ColumnMajor. Storage order is an efficiency
|
Chris@94
|
10 // concern only, all operations behave identically regardless. (The
|
Chris@94
|
11 // transpose function just switches the row/column order without
|
Chris@94
|
12 // moving the elements.)
|
Chris@18
|
13
|
Chris@93
|
14 vec = load yetilab.block.fvector;
|
Chris@96
|
15 block = load yetilab.block.block;
|
Chris@9
|
16
|
Chris@96
|
17 make d = {
|
Chris@96
|
18 get data () = d,
|
Chris@96
|
19 get size () =
|
Chris@96
|
20 case d of
|
Chris@96
|
21 RowM r:
|
Chris@96
|
22 major = length r;
|
Chris@96
|
23 {
|
Chris@96
|
24 rows = major,
|
Chris@96
|
25 columns = if major > 0 then vec.length r[0] else 0 fi,
|
Chris@96
|
26 };
|
Chris@96
|
27 ColM c:
|
Chris@96
|
28 major = length c;
|
Chris@96
|
29 {
|
Chris@96
|
30 rows = if major > 0 then vec.length c[0] else 0 fi,
|
Chris@96
|
31 columns = major,
|
Chris@96
|
32 };
|
Chris@94
|
33 esac,
|
Chris@96
|
34 getColumn j =
|
Chris@96
|
35 case d of
|
Chris@96
|
36 RowM rows: block.fromList (map do i: getAt i j done [0..length rows-1]);
|
Chris@96
|
37 ColM cols: block.block cols[j];
|
Chris@94
|
38 esac,
|
Chris@96
|
39 getRow i =
|
Chris@96
|
40 case d of
|
Chris@96
|
41 RowM rows: block.block rows[i];
|
Chris@96
|
42 ColM cols: block.fromList (map do j: getAt i j done [0..length cols-1]);
|
Chris@94
|
43 esac,
|
Chris@96
|
44 getAt row col =
|
Chris@96
|
45 case d of
|
Chris@96
|
46 RowM rows: r = rows[row]; (r is ~double[])[col];
|
Chris@96
|
47 ColM cols: c = cols[col]; (c is ~double[])[row];
|
Chris@94
|
48 esac,
|
Chris@96
|
49 setAt row col n =
|
Chris@96
|
50 case d of
|
Chris@96
|
51 RowM rows: r = rows[row]; (r is ~double[])[col] := n;
|
Chris@96
|
52 ColM cols: c = cols[col]; (c is ~double[])[row] := n;
|
Chris@95
|
53 esac,
|
Chris@95
|
54 get isRowMajor? () =
|
Chris@96
|
55 case d of
|
Chris@96
|
56 RowM _: true;
|
Chris@96
|
57 ColM _: false;
|
Chris@95
|
58 esac,
|
Chris@94
|
59 };
|
Chris@94
|
60
|
Chris@98
|
61 newStorage { rows, columns } =
|
Chris@97
|
62 if rows < 1 then array []
|
Chris@98
|
63 else array (map \(vec.zeros rows) [1..columns])
|
Chris@97
|
64 fi;
|
Chris@94
|
65
|
Chris@98
|
66 zeroMatrix { rows, columns } =
|
Chris@98
|
67 make (ColM (newStorage { rows, columns }));
|
Chris@5
|
68
|
Chris@98
|
69 generate f { rows, columns } =
|
Chris@98
|
70 (m = newStorage { rows, columns };
|
Chris@98
|
71 for [0..columns-1] do col:
|
Chris@94
|
72 for [0..rows-1] do row:
|
Chris@94
|
73 m[col][row] := f row col;
|
Chris@5
|
74 done;
|
Chris@5
|
75 done;
|
Chris@96
|
76 make (ColM m));
|
Chris@5
|
77
|
Chris@20
|
78 constMatrix n = generate do row col: n done;
|
Chris@20
|
79 randomMatrix = generate do row col: Math#random() done;
|
Chris@5
|
80 identityMatrix = constMatrix 1;
|
Chris@5
|
81
|
Chris@96
|
82 width m = m.size.columns;
|
Chris@96
|
83 height m = m.size.rows;
|
Chris@6
|
84
|
Chris@98
|
85 //!!! should matrices with the same content but different storage order be equal?
|
Chris@97
|
86 equal m1 m2 =
|
Chris@97
|
87 (compare d1 d2 =
|
Chris@97
|
88 all id (map2 vec.equal d1 d2);
|
Chris@97
|
89 case m1.data of
|
Chris@97
|
90 RowM d1:
|
Chris@97
|
91 case m2.data of RowM d2: compare d1 d2; _: false; esac;
|
Chris@97
|
92 ColM d1:
|
Chris@97
|
93 case m2.data of ColM d2: compare d1 d2; _: false; esac;
|
Chris@97
|
94 esac);
|
Chris@97
|
95
|
Chris@95
|
96 copyOf m =
|
Chris@95
|
97 (copyOfData d = (array (map vec.copyOf d));
|
Chris@96
|
98 make
|
Chris@95
|
99 (case m.data of
|
Chris@96
|
100 RowM d: RowM (copyOfData d);
|
Chris@96
|
101 ColM d: ColM (copyOfData d);
|
Chris@95
|
102 esac));
|
Chris@6
|
103
|
Chris@95
|
104 transposed m =
|
Chris@96
|
105 make
|
Chris@95
|
106 (case m.data of
|
Chris@96
|
107 RowM d: ColM d;
|
Chris@96
|
108 ColM d: RowM d;
|
Chris@95
|
109 esac);
|
Chris@5
|
110
|
Chris@96
|
111 //!!! Change storage from column to row major order (or back
|
Chris@96
|
112 //again). Is there a word for this?
|
Chris@96
|
113 flipped m =
|
Chris@96
|
114 if m.isRowMajor? then id else transposed fi
|
Chris@98
|
115 (generate do row col: m.getAt col row done m.size);
|
Chris@96
|
116
|
Chris@96
|
117 newMatrix type data is RowMajor () | ColumnMajor () -> list?<list?<number>> -> 'a =
|
Chris@96
|
118 (tagger = case type of RowMajor (): RowM; ColumnMajor (): ColM esac;
|
Chris@97
|
119 if empty? data or empty? (head data)
|
Chris@98
|
120 then zeroMatrix { rows = 0, columns = 0 }
|
Chris@96
|
121 else make (tagger (array (map vec.vector data)))
|
Chris@96
|
122 fi);
|
Chris@96
|
123
|
Chris@96
|
124 newRowVector data =
|
Chris@96
|
125 newMatrix (RowMajor ()) [data];
|
Chris@96
|
126
|
Chris@96
|
127 newColumnVector data =
|
Chris@96
|
128 newMatrix (ColumnMajor ()) [data];
|
Chris@8
|
129
|
Chris@98
|
130 scaled factor m =
|
Chris@98
|
131 generate do row col: factor * m.getAt row col done m.size;
|
Chris@98
|
132
|
Chris@98
|
133 sum' m1 m2 =
|
Chris@98
|
134 if m1.size != m2.size
|
Chris@98
|
135 then failWith "Matrices are not the same size: \(m1.size), \(m2.size)";
|
Chris@98
|
136 else
|
Chris@98
|
137 generate do row col: m1.getAt row col + m2.getAt row col done m1.size;
|
Chris@98
|
138 fi;
|
Chris@98
|
139
|
Chris@98
|
140 product m1 m2 =
|
Chris@98
|
141 if m1.size.columns != m2.size.rows
|
Chris@98
|
142 then failWith "Matrix dimensions incompatible: \(m1.size), \(m2.size) (\(m1.size.columns != m2.size.rows)";
|
Chris@98
|
143 else
|
Chris@98
|
144 //!!! super-slow!
|
Chris@98
|
145 generate do row col:
|
Chris@98
|
146 r = block.list (m1.getRow row);
|
Chris@98
|
147 c = block.list (m2.getColumn col);
|
Chris@98
|
148 sum (map2 (*) r c);
|
Chris@98
|
149 done { rows = m1.size.rows, columns = m2.size.columns }
|
Chris@98
|
150 fi;
|
Chris@98
|
151
|
Chris@5
|
152 {
|
Chris@97
|
153 constMatrix, randomMatrix, zeroMatrix, identityMatrix,
|
Chris@96
|
154 generate,
|
Chris@96
|
155 width, height,
|
Chris@97
|
156 equal,
|
Chris@20
|
157 copyOf,
|
Chris@15
|
158 transposed,
|
Chris@96
|
159 flipped,
|
Chris@98
|
160 scaled,
|
Chris@98
|
161 sum = sum', product,
|
Chris@98
|
162 newMatrix, newRowVector, newColumnVector,
|
Chris@5
|
163 }
|
Chris@5
|
164
|