1 |
|
2 | var assert = require('assert'),
|
3 | approx = require('../../../../tools/approx'),
|
4 | math = require('../../../../index');
|
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 | function assertValidQRDecomposition(A, Q, R) {
|
59 |
|
60 |
|
61 |
|
62 | var Asize = math.size(A).valueOf();
|
63 |
|
64 | var rows = Asize[0];
|
65 | var cols = Asize[1];
|
66 |
|
67 |
|
68 | assert.deepEqual(math.size(Q).valueOf(), [rows, rows]);
|
69 | assert.deepEqual(math.size(R).valueOf(), [rows, cols]);
|
70 |
|
71 |
|
72 | approx.deepEqual(math.multiply(Q, R).valueOf(), A.valueOf());
|
73 |
|
74 |
|
75 |
|
76 | assert(math.equal(math.multiply(math.conj(math.transpose(Q)), Q).valueOf(), math.eye([Asize[0], Asize[0]]).valueOf()),
|
77 | 'Matrix Q is not unitary/orthonormal');
|
78 |
|
79 |
|
80 |
|
81 | for (var i = 0; i < rows; i++) {
|
82 | for (var j = 0; j < i && j < cols; j++) {
|
83 | assert(math.isZero(math.subset(R, math.index(i, j))), 'R is not an upper triangular matrix');
|
84 | }
|
85 | }
|
86 |
|
87 |
|
88 | for (var i = 0; i < Math.min(rows, cols); i++) {
|
89 | var diagonalElement = math.subset(R, math.index(i, i))
|
90 |
|
91 | assert(!math.isNegative(math.re(diagonalElement)),
|
92 | 'R has elements on the leading diagonal with a negative real part (R[' + i + '][' + i + '] = ' + diagonalElement + ')');
|
93 | }
|
94 | }
|
95 | describe('qr', function () {
|
96 |
|
97 | it('should decompose matrix, n x n, no permutations, array', function () {
|
98 |
|
99 | var m = [[15, 42], [20, 81]];
|
100 |
|
101 | var r = math.qr(m);
|
102 |
|
103 | approx.deepEqual(r.Q.valueOf(), [[0.6, -0.8], [0.8, 0.6]]);
|
104 |
|
105 | approx.deepEqual(r.R.valueOf(), [[25, 90], [0, 15]]);
|
106 |
|
107 | assertValidQRDecomposition(m, r.Q, r.R);
|
108 |
|
109 | var m2 = [
|
110 | [7.507, 9.868, 5.057],
|
111 | [4.482, 2.536, 9.744],
|
112 | [6.527, 1.094, 3.321],
|
113 | ];
|
114 |
|
115 | var r2 = math.qr(m2);
|
116 |
|
117 | assertValidQRDecomposition(m2, r2.Q, r2. R);
|
118 |
|
119 | });
|
120 |
|
121 | it('should throw a helpfull error for sparse matricies', function () {
|
122 |
|
123 | var m = math.matrix([[15, 42], [20, 81]], 'sparse');
|
124 |
|
125 | var r = assert.throws(math.qr.bind(null, m));
|
126 |
|
127 | });
|
128 |
|
129 | it('should decompose matrix, n x n, dense format', function () {
|
130 |
|
131 | var m = math.matrix([[15, 42], [20, 81]], 'dense');
|
132 |
|
133 | var r = math.qr(m);
|
134 |
|
135 | approx.deepEqual(r.Q.valueOf(), [[0.6, -0.8], [0.8, 0.6]]);
|
136 |
|
137 | approx.deepEqual(r.R.valueOf(), [[25, 90], [0, 15]]);
|
138 |
|
139 | assertValidQRDecomposition(m, r.Q, r.R);
|
140 | });
|
141 |
|
142 | it('should decompose matrix, n x n, with a column of zeros dense format', function () {
|
143 |
|
144 | var m = math.matrix([[5, 0, 15], [223, 0, 34.5], [1, 0, 19]], 'dense');
|
145 |
|
146 | var r = math.qr(m);
|
147 |
|
148 | approx.deepEqual(
|
149 | r.Q.valueOf(),
|
150 | [
|
151 | [ 0.02241566559605479, 0.9997386855840484, -0.004483133119210979],
|
152 | [ 0.9997386855840484, -0.02243532343507404, -0.004383698101188009 ],
|
153 | [ 0.004483133119210979, 0.004383698101188009, 0.9999803421609812 ]
|
154 | ]);
|
155 |
|
156 |
|
157 | approx.deepEqual(
|
158 | r.R.valueOf(),
|
159 | [
|
160 | [ 223.0582883463423, -0, 34.912399165855504 ],
|
161 | [ -0, -0, 14.305351889173245 ],
|
162 | [ -0, -0, 18.781141919779493 ]
|
163 | ]);
|
164 |
|
165 | assertValidQRDecomposition(m, r.Q, r.R);
|
166 | });
|
167 |
|
168 | it('should decompose matrix, m x n, m < n, dense format', function () {
|
169 | var m = math.matrix(
|
170 | [
|
171 | [15, 42, -11, 9],
|
172 | [20, 81, 52, 112]
|
173 | ],
|
174 | 'dense'
|
175 | );
|
176 |
|
177 | var r = math.qr(m);
|
178 |
|
179 | approx.deepEqual(
|
180 | r.Q,
|
181 | math.matrix(
|
182 | [
|
183 | [0.6, -0.8],
|
184 | [0.8, 0.6],
|
185 | ]
|
186 | ));
|
187 |
|
188 | approx.deepEqual(
|
189 | r.R,
|
190 | math.matrix(
|
191 | [
|
192 | [25, 90, 35, 95],
|
193 | [0 , 15, 40, 60],
|
194 | ]
|
195 | ));
|
196 |
|
197 | assertValidQRDecomposition(m, r.Q, r.R);
|
198 |
|
199 | var m2 = math.matrix([
|
200 | [7.865, 9.293, 0.534, 7.023, 9.526, 6.005, 5.007, 5.581],
|
201 | [3.842, 7.807, 8.208, 2.108, 3.947, 1.154, 6.086, 6.21 ],
|
202 | [3.003, 4.084, 5.593, 4.738, 9.48 , 0.927, 7.294, 5.225]
|
203 | ]);
|
204 |
|
205 | var r2 = math.qr(m2);
|
206 |
|
207 | assertValidQRDecomposition(m, r.Q, r.R);
|
208 |
|
209 | });
|
210 |
|
211 | it('should decompose matrix, m x n, m > n, dense format', function () {
|
212 |
|
213 | var m = math.matrix(
|
214 | [
|
215 | [8, 4],
|
216 | [2, -12],
|
217 | [9, -2],
|
218 | [1, 94],
|
219 | ],
|
220 | 'dense'
|
221 | );
|
222 |
|
223 | var r = math.qr(m);
|
224 |
|
225 | assert.deepEqual(
|
226 | r.Q,
|
227 | math.matrix(
|
228 | [
|
229 | [ 0.6531972647421809 , -0.0050729188524001045, -0.7248169493126636 , -0.21897029208715485 ],
|
230 | [ 0.16329931618554522, -0.13865978196560358 , -0.14374377465457616, 0.9661493287513265 ],
|
231 | [ 0.7348469228349535 , -0.07440280983520192 , 0.6732450861047025 , -0.034717084043718795 ],
|
232 | [ 0.08164965809277261, 0.9875282032672256 , 0.026817368868139818, 0.13191743558805435 ]
|
233 | ]
|
234 | ));
|
235 |
|
236 | assert.deepEqual(
|
237 | r.R,
|
238 | math.matrix(
|
239 | [
|
240 | [ 12.24744871391589, 6.858571279792898],
|
241 | [ -0 , 94.62008243496727],
|
242 | [ -0 , -0 ],
|
243 | [ -0 , -0 ],
|
244 | ]
|
245 | ));
|
246 |
|
247 | assertValidQRDecomposition(m, r.Q, r.R);
|
248 | });
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 | it('should decompose matrix, n x n, complex numbers, dense format', function () {
|
317 | var m = math.matrix(
|
318 | [
|
319 | [math.complex(24, 3) , math.complex(10) ],
|
320 | [math.complex(12, 53) , math.complex(1.46, 10.6)],
|
321 | [math.complex(0.345345, 234), math.complex(1) ],
|
322 | ]);
|
323 |
|
324 | var r = math.qr(m);
|
325 |
|
326 |
|
327 | assert.deepEqual(
|
328 | r.Q,
|
329 | math.eval('[\
|
330 | [0.09940285751055641 + 0.012425357188819552i, 0.6771044400000075 + 0.0032268934486674216i, 0.7225638487314755 + 0.09687792016125076i],\
|
331 | [0.049701428755278255 + 0.2195146436691456i, 0.07692808877592644 + 0.6944571280351147i, 0.00524374167953522 - 0.6790632951693036i],\
|
332 | [0.0014303449927909801 + 0.969177860727926i, 0.009498908256891047 - 0.23073860039312136i, -0.03522342137225792 + 0.07823687113774894i]\
|
333 | ]'));
|
334 |
|
335 |
|
336 | assert.deepEqual(
|
337 | r.R,
|
338 | math.eval('[\
|
339 | [241.44175128417413 + 0i, 3.3948782289740067 - 0.8870876675671249i],\
|
340 | [0 + 0i, 14.254103875042043 - 4.440892098500626e-16i],\
|
341 | [0 + 0i, 0 + 0i]\
|
342 | ]'));
|
343 |
|
344 |
|
345 | assertValidQRDecomposition(m, r.Q, r.R);
|
346 | });
|
347 |
|
348 | it('should decompose matrix, m x n, n > m, complex numbers, dense format', function () {
|
349 | var m = math.eval('[\
|
350 | [-0.3264527816002377 + 2.493709974375747i, 27.144413452851555 - 95.38310595714056i, 24.851291758133694 - 31.358002980198492i, 17.60452153083572 - 58.02180107190187i, 29.062500250928192 - 57.24316264710557i, 5.699170296748263 - 65.11241969628546i, 19.819861372592023 + 25.900390198129045i, 16.557353232092076 - 37.25486567332457i],\
|
351 | [8.548264534732331 - 47.59913064936665i, 14.40138539657334 - 90.80495969865513i, 29.343082104326758 - 15.039062252958018i, 27.20916452240602 + 25.774841219390325i, 19.38506691927698 - 95.11167912062224i, 29.17634152715012 - 95.07970712229994i, 2.1987345350210092 - 9.041770826482406i, 2.806832236244097 + 2.0385477771778966i], [24.20532702537307 + 12.879358968749457i, 25.839682426729887 - 18.102222530229938i, 29.093489513094948 - 9.581972254775465i, 12.65038940459419 - 55.38946414968438i, -0.7049513892161683 - 23.70085292748422i, 7.910814607291806 + 24.701861346839564i, 2.4219941297871004 + 28.36329723916822i, 16.535587534250833 - 38.86239252709116i],\
|
352 | [25.78464278752434 - 59.91370905634549i, 29.424608924558413 - 19.120899022196383i, 25.6548685301034 + 6.075863297676378i, 3.693006642780766 - 63.363384338945906i, 15.716418860938354 - 73.40923022486281i, 28.9161836809681 - 58.38357844908446i, 10.13807260697836 - 3.5085542186585883i, 16.925761654754282 - 37.905623267161424i]\
|
353 | ]');
|
354 |
|
355 | var r = math.qr(m);
|
356 |
|
357 |
|
358 | assert.deepEqual(
|
359 | r.Q,
|
360 | math.eval('[\
|
361 | [-0.0038074725834465403 + 0.029084550335153184i, 0.22686378024210954 - 0.8031909609489004i, -0.1539944364016218 - 0.08044026151398012i, 0.15914274660150135 - 0.4970365797781979i],\
|
362 | [0.09969981781897692 - 0.5551565039665838i, 0.03656768230049788 - 0.4048572821234369i, 0.03460099750064215 + 0.4176688417721519i, 0.06529314053052465 + 0.5802645116992661i],\
|
363 | [0.2823107175583003 + 0.1502140858641239i, 0.04201869101132175 - 0.25276582362981437i, 0.7610890159088707 - 0.3999596125636107i, -0.24146613640405268 + 0.18587678263056984i],\
|
364 | [0.3007305375258921 - 0.6987834610763923i, 0.02974780206453512 + 0.2676367453654318i, 0.23430030839452232 - 0.007054866167671124i, -0.024719751847322398 - 0.5414711325141984i]\
|
365 | ]'));
|
366 |
|
367 |
|
368 | assert.deepEqual(
|
369 | r.R,
|
370 | math.eval('[\
|
371 | [85.74002161421444 - 1.7763568394002505e-15i, 75.75511004703746 + 4.3347264490288016i, 20.511425451943854 + 26.86626726613313i, 27.288058950461433 - 16.62801026736354i, 105.22335436327181 - 17.027323945468076i, 109.21486260617472 + 15.233872631050161i, 16.361518290342467 + 13.316745322711627i, 28.409955756511188 - 11.605326516313891i],\
|
372 | [0 + 0i, 121.47784233162547 + 1.7763568394002505e-15i, 44.01977059734889 + 24.441930600590624i, 38.83986358402923 + 10.93198966397847i, 78.56760829656308 + 7.162388196994509i, 72.474482997425 - 8.297010771192621i, -20.270457048330027 + 21.34082444731987i, 33.83280850600839 + 2.9469680307519037i],\
|
373 | [0 + 0i, 0 + 0i, 25.372653909655675 + 5.329070518200751e-15i, 46.75701662904174 - 52.038112884483404i, -25.7821433027293 - 35.64391269354021i, -31.014234782164266 + 3.4985227007956983i, -15.936684410229294 + 18.179762871924087i, 33.75717971935531 - 25.758933854786893i],\
|
374 | [0 + 0i, 0 + 0i, 0 + 0i, 69.24128415239949 + 0i, 14.27806840079945 + 4.055317531798819i, 13.583401274164364 - 21.002114936285405i, -8.485891575536547 + 10.384078077176659i, 31.408176714183693 + 17.21736552045245i]\
|
375 | ]'));
|
376 |
|
377 |
|
378 | assertValidQRDecomposition(m, r.Q, r.R);
|
379 | });
|
380 |
|
381 | });
|