UNPKG

13.1 kBJavaScriptView Raw
1// test lup
2var assert = require('assert'),
3 approx = require('../../../../tools/approx'),
4 math = require('../../../../index');
5
6/**
7 * Tests whether `Q` and `R` are the valid QR decomposition of `A`.
8 *
9 * Given a real matrix `A`, `Q` and `R` should be the solutions to the equation
10 * `A = Q*R` where Q is [orthoganal](https://en.wikipedia.org/wiki/Orthogonal_matrix) and
11 * R is [upper triangular](https://en.wikipedia.org/wiki/Triangular_matrix).
12 *
13 * If `A` is a complex matrix then `Q` should be a [unitary](https://en.wikipedia.org/wiki/Unitary_matrix)
14 *
15 * Syntax:
16 *
17 * math.isValidQRDecomposition(A);
18 *
19 * Example:
20 *
21 * var m = [
22 * [1, -1, 4],
23 * [1, 4, -2],
24 * [1, 4, 2],
25 * [1, -1, 0]
26 * ];
27 * var result = math.qr(m);
28 * // r = {
29 * // Q: [
30 * // [0.5, -0.5, 0.5],
31 * // [0.5, 0.5, -0.5],
32 * // [0.5, 0.5, 0.5],
33 * // [0.5, -0.5, -0.5],
34 * // ],
35 * // R: [
36 * // [2, 3, 2],
37 * // [0, 5, -2],
38 * // [0, 0, 4],
39 * // [0, 0, 0]
40 * // ]
41 * // }
42 *
43 * isValidQRDecomposition(m, r.Q, r.R);
44 * // true
45 *
46 * r.Q[2][1] = 9;
47 *
48 * isValidQRDecomposition(m, r.Q, r.R);
49 * // false
50 *
51 *
52 * @param {Matrix | Array} A A two dimensional matrix or array from which the QR decomposition was formed.
53 * @param {Matrix | Array} Q A two dimensional matrix or array equal to `Q` is an QR decomposition.
54 * @param {Matrix | Array} R A two dimensional matrix or array equal to `R` is an QR decomposition.
55 *
56 * @return {Boolean} Returns true if `Q` and `R` form a valid QR decomposition of `A`
57 */
58function 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 // sizes match
68 assert.deepEqual(math.size(Q).valueOf(), [rows, rows]);
69 assert.deepEqual(math.size(R).valueOf(), [rows, cols]);
70
71 // A = Q * R
72 approx.deepEqual(math.multiply(Q, R).valueOf(), A.valueOf());
73
74 // Q has unitary (orthonormal for real A) columns
75 // use math.equal as approx.deepEqual cannot handle complex vs real number comparision
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 // R is upper triangular
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 // All elements on leading diagonal of R are positive
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}
95describe('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 // L
103 approx.deepEqual(r.Q.valueOf(), [[0.6, -0.8], [0.8, 0.6]]);
104 // U
105 approx.deepEqual(r.R.valueOf(), [[25, 90], [0, 15]]);
106 // verify
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 // Q
135 approx.deepEqual(r.Q.valueOf(), [[0.6, -0.8], [0.8, 0.6]]);
136 // R
137 approx.deepEqual(r.R.valueOf(), [[25, 90], [0, 15]]);
138 // verify
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 // Q
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 // R
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 // verify
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 // Q
179 approx.deepEqual(
180 r.Q,
181 math.matrix(
182 [
183 [0.6, -0.8],
184 [0.8, 0.6],
185 ]
186 ));
187 // R
188 approx.deepEqual(
189 r.R,
190 math.matrix(
191 [
192 [25, 90, 35, 95],
193 [0 , 15, 40, 60],
194 ]
195 ));
196 // verify
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 // Q
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 // R
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 // verify
247 assertValidQRDecomposition(m, r.Q, r.R);
248 });
249/*
250 it('should decompose matrix, n x n, dense format', function () {
251 var m = math.matrix(
252 [
253 [16, -120, 240, -140],
254 [-120, 1200, -2700, 1680],
255 [240, -2700, 6480, -4200],
256 [-140, 1680, -4200, 2800]
257 ]
258 );
259
260 var r = math.lup(m);
261 // L
262 approx.deepEqual(
263 r.L.valueOf(),
264 [
265 [1, 0, 0, 0],
266 [-0.5, 1, 0, 0],
267 [-0.5833333333333334, -0.7, 1, 0],
268 [0.06666666666666667, -0.4, -0.5714285714285776, 1]
269 ]);
270 // U
271 approx.deepEqual(
272 r.U.valueOf(),
273 [
274 [240, -2700, 6480, -4200],
275 [0, -150, 540, -420],
276 [0, 0, -42, 56],
277 [0, 0, 0, 4]
278 ]);
279 // P
280 assert.deepEqual(r.p, [3, 1, 0, 2]);
281 // verify
282 approx.deepEqual(math.multiply(_p(r.p), m).valueOf(), math.multiply(r.L, r.U).valueOf());
283 });
284/*
285 it('should decompose matrix, 3 x 3, zero pivote value, dense format', function () {
286 var m = math.matrix(
287 [
288 [1, 2, 3],
289 [2, 4, 6],
290 [4, 8, 9]
291 ]);
292
293 var r = math.lup(m);
294 // L
295 approx.deepEqual(
296 r.L.valueOf(),
297 [
298 [1, 0, 0],
299 [0.5, 1, 0],
300 [0.25, 0, 1.0]
301 ]);
302 // U
303 approx.deepEqual(
304 r.U.valueOf(),
305 [
306 [4, 8, 9],
307 [0, 0, 1.5],
308 [0, 0, 0.75]
309 ]);
310 // P
311 assert.deepEqual(r.p, [2, 1, 0]);
312 // verify
313 approx.deepEqual(math.multiply(_p(r.p), m).valueOf(), math.multiply(r.L, r.U).valueOf());
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 // Q
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 // R
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 // verify
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 // Q
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 // R
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 // verify
378 assertValidQRDecomposition(m, r.Q, r.R);
379 });
380
381});