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