API Documentation
Loading...
Searching...
No Matches
SVD.hpp
Go to the documentation of this file.
1#pragma once
2#include <NDEVR/Buffer.h>
3#include <NDEVR/Exception.h>
4namespace NDEVR
5{
7 /**--------------------------------------------------------------------------------------------------
8 \brief Performs the singular value decomposition of a general matrix,
9 taken and adapted from Numerical Recipes Third Edition svd.h
10 **/
11 class SVD
12 {
13 public:
14 // =============================================================================
15 // Description:
16 /// Constructor
17 ///
18 /// @param inMatrix: matrix to perform SVD on
19 ///
20 explicit SVD(const NdArray& inMatrix)
21 : m_(inMatrix.size())
22 , n_(inMatrix[0].size())
23 , u_(inMatrix)
24 , eps_(std::numeric_limits<double>::epsilon())
25 {
26 v_.setSize(n_);
27 for (uint04 i = 0; i < n_; i++)
28 v_[i].setSize(n_);
29 s_.setSize(n_);
30 decompose();
31 reorder();
32 tsh_ = 0.5 * std::sqrt(m_ + n_ + 1.) * s_[0] * eps_;
33 }
34
35 // =============================================================================
36 // Description:
37 /// the resultant u matrix
38 ///
39 /// @return u matrix
40 ///
41 const NdArray& u() noexcept
42 {
43 return u_;
44 }
45
46 // =============================================================================
47 // Description:
48 /// the resultant v matrix
49 ///
50 /// @return v matrix
51 ///
52 const NdArray& v() noexcept
53 {
54 return v_;
55 }
56
57 // =============================================================================
58 // Description:
59 /// the resultant w matrix
60 ///
61 /// @return s matrix
62 ///
63 const Buffer<double>& s() noexcept
64 {
65 return s_;
66 }
67
68 // =============================================================================
69 // Description:
70 /// solves the linear least squares problem
71 ///
72 /// @param inInput
73 /// @param inThresh (default -1.)
74 ///
75 /// @return NdArray
76 ///
77 Buffer<double> solve(const Buffer<double>& inInput, double inThresh = -1.)
78 {
79 double ss{};
80
81 if (inInput.size() != m_)
82 {
83 throw Exception("bad sizes.");
84 }
85
86 Buffer<double> returnArray;
87 returnArray.setSize(n_);
88
90 tmp.setSize(n_);
91
92 tsh_ = (inThresh >= 0. ? inThresh : 0.5 * std::sqrt(m_ + n_ + 1.) * s_[0] * eps_);
93
94 for (uint04 j = 0; j < n_; j++)
95 {
96 ss = 0.;
97 if (s_[j] > tsh_)
98 {
99 for (uint04 i = 0; i < m_; i++)
100 ss += u_[i][j] * inInput[i];
101 ss /= s_[j];
102 }
103 tmp[j] = ss;
104 }
105
106 for (uint04 j = 0; j < n_; j++)
107 {
108 ss = 0.;
109 for (uint04 jj = 0; jj < n_; jj++)
110 {
111 ss += v_[j][jj] * tmp[jj];
112 }
113
114 returnArray[j] = ss;
115 }
116
117 return returnArray;
118 }
119
120 private:
121 // =============================================================================
122 // Description:
123 /// returns the SIGN of two values
124 ///
125 /// @param inA
126 /// @param inB
127 ///
128 /// @return value
129 ///
130 static double SIGN(double inA, double inB) noexcept
131 {
132 return inB >= 0 ? (inA >= 0 ? inA : -inA) : (inA >= 0 ? -inA : inA);
133 }
134
135 // =============================================================================
136 // Description:
137 /// decomposes the input matrix
138 ///
139 //
140 static bool essentiallyEqual(double a, double b)
141 {
142 if (abs(a - b) < std::numeric_limits<double>::epsilon())
143 return true;
144 return false;
145 }
146 void decompose()
147 {
148 bool flag{};
149 uint04 i{};
150 uint04 its{};
151 uint04 j{};
152 uint04 jj{};
153 uint04 k{};
154 uint04 l{};
155 uint04 nm{};
156
157 double anorm{};
158 double c{};
159 double f{};
160 double g{};
161 double h{};
162 double ss{};
163 double scale{};
164 double x{};
165 double y{};
166 double z{};
167
168 Buffer<double> rv1;
169 rv1.setSize(n_);
170
171 for (i = 0; i < n_; ++i)
172 {
173 l = i + 2;
174 rv1[i] = scale * g;
175 g = ss = scale = 0.;
176
177 if (i < m_)
178 {
179 for (k = i; k < m_; ++k)
180 {
181 scale += std::abs(u_[k][i]);
182 }
183
184 if (!essentiallyEqual(scale, 0.))
185 {
186 for (k = i; k < m_; ++k)
187 {
188 u_[k][i] /= scale;
189 ss += u_[k][i] * u_[k][i];
190 }
191
192 f = u_[i][i];
193 g = -SIGN(std::sqrt(ss), f);
194 h = f * g - ss;
195 u_[i][i] = f - g;
196
197 for (j = l - 1; j < n_; ++j)
198 {
199 for (ss = 0., k = i; k < m_; ++k)
200 {
201 ss += u_[k][i] * u_[k][j];
202 }
203
204 f = ss / h;
205
206 for (k = i; k < m_; ++k)
207 {
208 u_[k][j] += f * u_[k][i];
209 }
210 }
211
212 for (k = i; k < m_; ++k)
213 {
214 u_[k][i] *= scale;
215 }
216 }
217 }
218
219 s_[i] = scale * g;
220 g = ss = scale = 0.;
221
222 if (i + 1 <= m_ && i + 1 != n_)
223 {
224 for (k = l - 1; k < n_; ++k)
225 {
226 scale += std::abs(u_[i][k]);
227 }
228
229 if (!essentiallyEqual(scale, 0.))
230 {
231 for (k = l - 1; k < n_; ++k)
232 {
233 u_[i][k] /= scale;
234 ss += u_[i][k] * u_[i][k];
235 }
236
237 f = u_[i][l - 1];
238 g = -SIGN(std::sqrt(ss), f);
239 h = f * g - ss;
240 u_[i][l - 1] = f - g;
241
242 for (k = l - 1; k < n_; ++k)
243 {
244 rv1[k] = u_[i][k] / h;
245 }
246
247 for (j = l - 1; j < m_; ++j)
248 {
249 for (ss = 0., k = l - 1; k < n_; ++k)
250 {
251 ss += u_[j][k] * u_[i][k];
252 }
253
254 for (k = l - 1; k < n_; ++k)
255 {
256 u_[j][k] += ss * rv1[k];
257 }
258 }
259
260 for (k = l - 1; k < n_; ++k)
261 {
262 u_[i][k] *= scale;
263 }
264 }
265 }
266
267 anorm = std::max(anorm, (std::abs(s_[i]) + std::abs(rv1[i])));
268 }
269
270 for (i = n_ - 1; i != static_cast<uint04>(-1); --i)
271 {
272 if (i < n_ - 1)
273 {
274 if (!essentiallyEqual(g, 0.))
275 {
276 for (j = l; j < n_; ++j)
277 {
278 v_[j][i] = (u_[i][j] / u_[i][l]) / g;
279 }
280
281 for (j = l; j < n_; ++j)
282 {
283 for (ss = 0., k = l; k < n_; ++k)
284 {
285 ss += u_[i][k] * v_[k][j];
286 }
287
288 for (k = l; k < n_; ++k)
289 {
290 v_[k][j] += ss * v_[k][i];
291 }
292 }
293 }
294
295 for (j = l; j < n_; ++j)
296 {
297 v_[i][j] = v_[j][i] = 0.;
298 }
299 }
300
301 v_[i][i] = 1.;
302 g = rv1[i];
303 l = i;
304 }
305
306 for (i = std::min(m_, n_) - 1; i != static_cast<uint04>(-1); --i)
307 {
308 l = i + 1;
309 g = s_[i];
310
311 for (j = l; j < n_; ++j)
312 {
313 u_[i][j] = 0.;
314 }
315
316 if (!essentiallyEqual(g, 0.))
317 {
318 g = 1. / g;
319
320 for (j = l; j < n_; ++j)
321 {
322 for (ss = 0., k = l; k < m_; ++k)
323 {
324 ss += u_[k][i] * u_[k][j];
325 }
326
327 f = (ss / u_[i][i]) * g;
328
329 for (k = i; k < m_; ++k)
330 {
331 u_[k][j] += f * u_[k][i];
332 }
333 }
334
335 for (j = i; j < m_; ++j)
336 {
337 u_[j][i] *= g;
338 }
339 }
340 else
341 {
342 for (j = i; j < m_; ++j)
343 {
344 u_[j][i] = 0.;
345 }
346 }
347
348 ++u_[i][i];
349 }
350
351 for (k = n_ - 1; k != static_cast<uint04>(-1); --k)
352 {
353 for (its = 0; its < 30; ++its)
354 {
355 flag = true;
356 for (l = k; l != static_cast<uint04>(-1); --l)
357 {
358 nm = l - 1;
359 if (l == 0 || std::abs(rv1[l]) <= eps_ * anorm)
360 {
361 flag = false;
362 break;
363 }
364
365 if (std::abs(s_[nm]) <= eps_ * anorm)
366 {
367 break;
368 }
369 }
370
371 if (flag)
372 {
373 c = 0.;
374 ss = 1.;
375 for (i = l; i < k + 1; ++i)
376 {
377 f = ss * rv1[i];
378 rv1[i] = c * rv1[i];
379
380 if (std::abs(f) <= eps_ * anorm)
381 {
382 break;
383 }
384
385 g = s_[i];
386 h = pythag(f, g);
387 s_[i] = h;
388 h = 1. / h;
389 c = g * h;
390 ss = -f * h;
391
392 for (j = 0; j < m_; ++j)
393 {
394 y = u_[j][nm];
395 z = u_[j][i];
396 u_[j][nm] = y * c + z * ss;
397 u_[j][i] = z * c - y * ss;
398 }
399 }
400 }
401
402 z = s_[k];
403 if (l == k)
404 {
405 if (z < 0.)
406 {
407 s_[k] = -z;
408 for (j = 0; j < n_; ++j)
409 {
410 v_[j][k] = -v_[j][k];
411 }
412 }
413 break;
414 }
415
416 if (its == 29)
417 {
418 throw Exception("no convergence in 30 svdcmp iterations");
419 }
420
421 x = s_[l];
422 nm = k - 1;
423 y = s_[nm];
424 g = rv1[nm];
425 h = rv1[k];
426 f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2. * h * y);
427 g = pythag(f, 1.);
428 f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x;
429 c = ss = 1.;
430
431 for (j = l; j <= nm; j++)
432 {
433 i = j + 1;
434 g = rv1[i];
435 y = s_[i];
436 h = ss * g;
437 g = c * g;
438 z = pythag(f, h);
439 rv1[j] = z;
440 c = f / z;
441 ss = h / z;
442 f = x * c + g * ss;
443 g = g * c - x * ss;
444 h = y * ss;
445 y *= c;
446
447 for (jj = 0; jj < n_; ++jj)
448 {
449 x = v_[jj][j];
450 z = v_[jj][i];
451 v_[jj][j] = x * c + z * ss;
452 v_[jj][i] = z * c - x * ss;
453 }
454
455 z = pythag(f, h);
456 s_[j] = z;
457
458 if (!essentiallyEqual(z, 0.))
459 {
460 z = 1. / z;
461 c = f * z;
462 ss = h * z;
463 }
464
465 f = c * g + ss * y;
466 x = c * y - ss * g;
467
468 for (jj = 0; jj < m_; ++jj)
469 {
470 y = u_[jj][j];
471 z = u_[jj][i];
472 u_[jj][j] = y * c + z * ss;
473 u_[jj][i] = z * c - y * ss;
474 }
475 }
476 rv1[l] = 0.;
477 rv1[k] = f;
478 s_[k] = x;
479 }
480 }
481 }
482
483 // =============================================================================
484 // Description:
485 /// reorders the input matrix
486 ///
487 void reorder()
488 {
489 uint04 i = 0;
490 uint04 j = 0;
491 uint04 k = 0;
492 uint04 ss = 0;
493 uint04 inc = 1;
494
495 double sw{};
496 Buffer<double> su;
497 su.setSize(m_);
498 Buffer<double> sv;
499 sv.setSize(n_);
500
501 do
502 {
503 inc *= 3;
504 ++inc;
505 } while (inc <= n_);
506
507 do
508 {
509 inc /= 3;
510 for (i = inc; i < n_; ++i)
511 {
512 sw = s_[i];
513 for (k = 0; k < m_; ++k)
514 {
515 su[k] = u_[k][i];
516 }
517
518 for (k = 0; k < n_; ++k)
519 {
520 sv[k] = v_[k][i];
521 }
522
523 j = i;
524 while (s_[j - inc] < sw)
525 {
526 s_[j] = s_[j - inc];
527
528 for (k = 0; k < m_; ++k)
529 {
530 u_[k][j] = u_[k][j - inc];
531 }
532
533 for (k = 0; k < n_; ++k)
534 {
535 v_[k][j] = v_[k][j - inc];
536 }
537
538 j -= inc;
539
540 if (j < inc)
541 {
542 break;
543 }
544 }
545
546 s_[j] = sw;
547
548 for (k = 0; k < m_; ++k)
549 {
550 u_[k][j] = su[k];
551 }
552
553 for (k = 0; k < n_; ++k)
554 {
555 v_[k][j] = sv[k];
556 }
557 }
558 } while (inc > 1);
559
560 for (k = 0; k < n_; ++k)
561 {
562 ss = 0;
563
564 for (i = 0; i < m_; i++)
565 {
566 if (u_[i][k] < 0.)
567 {
568 ss++;
569 }
570 }
571
572 for (j = 0; j < n_; ++j)
573 {
574 if (v_[j][k] < 0.)
575 {
576 ss++;
577 }
578 }
579
580 if (ss > (m_ + n_) / 2)
581 {
582 for (i = 0; i < m_; ++i)
583 {
584 u_[i][k] = -u_[i][k];
585 }
586
587 for (j = 0; j < n_; ++j)
588 {
589 v_[j][k] = -v_[j][k];
590 }
591 }
592 }
593 }
594
595 // =============================================================================
596 // Description:
597 /// performs pythag of input values
598 ///
599 /// @param inA
600 /// @param inB
601 ///
602 /// @return resultant value
603 ///
604 static double pythag(double inA, double inB) noexcept
605 {
606 const double absa = std::abs(inA);
607 const double absb = std::abs(inB);
608 return (absa > absb
609 ? absa * std::sqrt(1. + (absb / absa) * (absb / absa))
610 : (essentiallyEqual(absb, 0.) ? 0. : absb * std::sqrt(1. + (absb / absa) * (absb / absa))));
611 }
612 static Buffer<double> lstsq(const NdArray& inA, const Buffer<double>& inB, double inTolerance = 1e-12)
613 {
614 SVD svdSolver(inA);
615 const double threshold = inTolerance * svdSolver.s()[0];
616
617 return svdSolver.solve(inB, threshold);
618 }
619 private:
620 // ===============================Attributes====================================
621 const uint04 m_{};
622 const uint04 n_{};
623 NdArray u_{};
624 NdArray v_{};
625 Buffer<double> s_;
626 double eps_{};
627 double tsh_{};
628 };
629
630
631}
The equivelent of std::vector but with a bit more control. The basic array unit of the library.
Definition Buffer.hpp:56
constexpr t_index_type size() const
Definition Buffer.hpp:823
void setSize(t_index_type new_size)
Definition Buffer.hpp:803
Provides consistent interface to handle errors through the throw expression. All exceptions generated...
Definition Exception.hpp:47
Performs the singular value decomposition of a general matrix, taken and adapted from Numerical Recip...
Definition SVD.hpp:12
const NdArray & u() noexcept
Definition SVD.hpp:41
const NdArray & v() noexcept
Definition SVD.hpp:52
const Buffer< double > & s() noexcept
Definition SVD.hpp:63
SVD(const NdArray &inMatrix)
Definition SVD.hpp:20
Buffer< double > solve(const Buffer< double > &inInput, double inThresh=-1.)
Definition SVD.hpp:77
Definition ACIColor.h:37
Buffer< Buffer< double > > NdArray
Definition SVD.hpp:6
uint32_t uint04
-Defines an alias representing a 4 byte, unsigned integer -Can represent exact integer values 0 throu...
Definition BaseValues.hpp:96
constexpr Angle< t_angle_type > abs(const Angle< t_angle_type > &value)
Changes an input with a negative sign, to a positive sign.
Definition AngleFunctions.h:645
Definition File.h:211