Passed
Push — master ( 47cdff...ed5fc8 )
by Arkadiusz
03:38
created

Math/LinearAlgebra/EigenvalueDecomposition.php (25 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
/**
5
 *
6
 *	Class to obtain eigenvalues and eigenvectors of a real matrix.
7
 *
8
 *	If A is symmetric, then A = V*D*V' where the eigenvalue matrix D
9
 *	is diagonal and the eigenvector matrix V is orthogonal (i.e.
10
 *	A = V.times(D.times(V.transpose())) and V.times(V.transpose())
11
 *	equals the identity matrix).
12
 *
13
 *	If A is not symmetric, then the eigenvalue matrix D is block diagonal
14
 *	with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues,
15
 *	lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda].  The
16
 *	columns of V represent the eigenvectors in the sense that A*V = V*D,
17
 *	i.e. A.times(V) equals V.times(D).  The matrix V may be badly
18
 *	conditioned, or even singular, so the validity of the equation
19
 *	A = V*D*inverse(V) depends upon V.cond().
20
 *
21
 *	@author  Paul Meagher
22
 *	@license PHP v3.0
23
 *	@version 1.1
24
 *
25
 *  Slightly changed to adapt the original code to PHP-ML library
26
 *  @date 2017/04/11
27
 *  @author Mustafa Karabulut
28
 */
29
30
namespace Phpml\Math\LinearAlgebra;
31
32
use Phpml\Math\Matrix;
33
34
class EigenvalueDecomposition
35
{
36
    /**
37
     *	Row and column dimension (square matrix).
38
     *	@var int
39
     */
40
    private $n;
41
42
    /**
43
     *	Internal symmetry flag.
44
     *	@var bool
45
     */
46
    private $symmetric;
47
48
    /**
49
     *	Arrays for internal storage of eigenvalues.
50
     *	@var array
51
     */
52
    private $d = [];
53
    private $e = [];
54
55
    /**
56
     *	Array for internal storage of eigenvectors.
57
     *	@var array
58
     */
59
    private $V = [];
60
61
    /**
62
    *	Array for internal storage of nonsymmetric Hessenberg form.
63
    *	@var array
64
    */
65
    private $H = [];
66
67
    /**
68
    *	Working storage for nonsymmetric algorithm.
69
    *	@var array
70
    */
71
    private $ort;
72
73
    /**
74
    *	Used for complex scalar division.
75
    *	@var float
76
    */
77
    private $cdivr;
78
    private $cdivi;
79
80
    /**
81
     *	Constructor: Check for symmetry, then construct the eigenvalue decomposition
82
     *
83
     * @param array $Arg
84
     */
85
    public function __construct(array $Arg)
86
    {
87
        $this->A = $Arg;
0 ignored issues
show
The property A does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
88
        $this->n = count($Arg[0]);
89
        $this->symmetric = true;
90
91
        for ($j = 0; ($j < $this->n) && $this->symmetric; ++$j) {
92
            for ($i = 0; ($i < $this->n) & $this->symmetric; ++$i) {
93
                $this->symmetric = ($this->A[$i][$j] == $this->A[$j][$i]);
94
            }
95
        }
96
97
        if ($this->symmetric) {
98
            $this->V = $this->A;
99
            // Tridiagonalize.
100
            $this->tred2();
101
            // Diagonalize.
102
            $this->tql2();
103
        } else {
104
            $this->H = $this->A;
105
            $this->ort = [];
106
            // Reduce to Hessenberg form.
107
            $this->orthes();
108
            // Reduce Hessenberg to real Schur form.
109
            $this->hqr2();
110
        }
111
    }
112
113
    /**
114
     *	Symmetric Householder reduction to tridiagonal form.
115
     */
116
    private function tred2()
117
    {
118
        //  This is derived from the Algol procedures tred2 by
119
        //  Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
120
        //  Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
121
        //  Fortran subroutine in EISPACK.
122
        $this->d = $this->V[$this->n - 1];
123
        // Householder reduction to tridiagonal form.
124
        for ($i = $this->n - 1; $i > 0; --$i) {
125
            $i_ = $i - 1;
126
            // Scale to avoid under/overflow.
127
            $h = $scale = 0.0;
128
            $scale += array_sum(array_map('abs', $this->d));
129
            if ($scale == 0.0) {
130
                $this->e[$i] = $this->d[$i_];
131
                $this->d = array_slice($this->V[$i_], 0, $i_);
132
                for ($j = 0; $j < $i; ++$j) {
133
                    $this->V[$j][$i] = $this->V[$i][$j] = 0.0;
134
                }
135
            } else {
136
                // Generate Householder vector.
137
                for ($k = 0; $k < $i; ++$k) {
138
                    $this->d[$k] /= $scale;
139
                    $h += pow($this->d[$k], 2);
140
                }
141
142
                $f = $this->d[$i_];
143
                $g = sqrt($h);
144
                if ($f > 0) {
145
                    $g = -$g;
146
                }
147
148
                $this->e[$i] = $scale * $g;
149
                $h = $h - $f * $g;
150
                $this->d[$i_] = $f - $g;
151
152
                for ($j = 0; $j < $i; ++$j) {
153
                    $this->e[$j] = 0.0;
154
                }
155
                // Apply similarity transformation to remaining columns.
156
                for ($j = 0; $j < $i; ++$j) {
157
                    $f = $this->d[$j];
158
                    $this->V[$j][$i] = $f;
159
                    $g = $this->e[$j] + $this->V[$j][$j] * $f;
160
161
                    for ($k = $j + 1; $k <= $i_; ++$k) {
162
                        $g += $this->V[$k][$j] * $this->d[$k];
163
                        $this->e[$k] += $this->V[$k][$j] * $f;
164
                    }
165
                    $this->e[$j] = $g;
166
                }
167
168
                $f = 0.0;
169
                if ($h === 0 || $h < 1e-32) {
170
                    $h = 1e-32;
171
                }
172
173
                for ($j = 0; $j < $i; ++$j) {
174
                    $this->e[$j] /= $h;
175
                    $f += $this->e[$j] * $this->d[$j];
176
                }
177
178
                $hh = $f / (2 * $h);
179
                for ($j = 0; $j < $i; ++$j) {
180
                    $this->e[$j] -= $hh * $this->d[$j];
181
                }
182
                for ($j = 0; $j < $i; ++$j) {
183
                    $f = $this->d[$j];
184
                    $g = $this->e[$j];
185
                    for ($k = $j; $k <= $i_; ++$k) {
186
                        $this->V[$k][$j] -= ($f * $this->e[$k] + $g * $this->d[$k]);
187
                    }
188
                    $this->d[$j] = $this->V[$i - 1][$j];
189
                    $this->V[$i][$j] = 0.0;
190
                }
191
            }
192
            $this->d[$i] = $h;
193
        }
194
195
        // Accumulate transformations.
196
        for ($i = 0; $i < $this->n - 1; ++$i) {
197
            $this->V[$this->n - 1][$i] = $this->V[$i][$i];
198
            $this->V[$i][$i] = 1.0;
199
            $h = $this->d[$i + 1];
200
            if ($h != 0.0) {
201 View Code Duplication
                for ($k = 0; $k <= $i; ++$k) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
202
                    $this->d[$k] = $this->V[$k][$i + 1] / $h;
203
                }
204
                for ($j = 0; $j <= $i; ++$j) {
205
                    $g = 0.0;
206 View Code Duplication
                    for ($k = 0; $k <= $i; ++$k) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
207
                        $g += $this->V[$k][$i + 1] * $this->V[$k][$j];
208
                    }
209 View Code Duplication
                    for ($k = 0; $k <= $i; ++$k) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
210
                        $this->V[$k][$j] -= $g * $this->d[$k];
211
                    }
212
                }
213
            }
214
            for ($k = 0; $k <= $i; ++$k) {
215
                $this->V[$k][$i + 1] = 0.0;
216
            }
217
        }
218
219
        $this->d = $this->V[$this->n - 1];
220
        $this->V[$this->n - 1] = array_fill(0, $j, 0.0);
0 ignored issues
show
The variable $j does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
221
        $this->V[$this->n - 1][$this->n - 1] = 1.0;
222
        $this->e[0] = 0.0;
223
    }
224
225
226
    /**
227
     *	Symmetric tridiagonal QL algorithm.
228
     *
229
     *	This is derived from the Algol procedures tql2, by
230
     *	Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
231
     *	Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
232
     *	Fortran subroutine in EISPACK.
233
     */
234
    private function tql2()
235
    {
236
        for ($i = 1; $i < $this->n; ++$i) {
237
            $this->e[$i - 1] = $this->e[$i];
238
        }
239
        $this->e[$this->n - 1] = 0.0;
240
        $f = 0.0;
241
        $tst1 = 0.0;
242
        $eps  = pow(2.0, -52.0);
243
244
        for ($l = 0; $l < $this->n; ++$l) {
245
            // Find small subdiagonal element
246
            $tst1 = max($tst1, abs($this->d[$l]) + abs($this->e[$l]));
247
            $m = $l;
248
            while ($m < $this->n) {
249
                if (abs($this->e[$m]) <= $eps * $tst1) {
250
                    break;
251
                }
252
                ++$m;
253
            }
254
            // If m == l, $this->d[l] is an eigenvalue,
255
            // otherwise, iterate.
256
            if ($m > $l) {
257
                $iter = 0;
258
                do {
259
                    // Could check iteration count here.
260
                    $iter += 1;
261
                    // Compute implicit shift
262
                    $g = $this->d[$l];
263
                    $p = ($this->d[$l + 1] - $g) / (2.0 * $this->e[$l]);
264
                    $r = hypot($p, 1.0);
265
                    if ($p < 0) {
266
                        $r *= -1;
267
                    }
268
                    $this->d[$l] = $this->e[$l] / ($p + $r);
269
                    $this->d[$l + 1] = $this->e[$l] * ($p + $r);
270
                    $dl1 = $this->d[$l + 1];
271
                    $h = $g - $this->d[$l];
272
                    for ($i = $l + 2; $i < $this->n; ++$i) {
273
                        $this->d[$i] -= $h;
274
                    }
275
                    $f += $h;
276
                    // Implicit QL transformation.
277
                    $p = $this->d[$m];
278
                    $c = 1.0;
279
                    $c2 = $c3 = $c;
280
                    $el1 = $this->e[$l + 1];
281
                    $s = $s2 = 0.0;
282
                    for ($i = $m - 1; $i >= $l; --$i) {
283
                        $c3 = $c2;
284
                        $c2 = $c;
285
                        $s2 = $s;
286
                        $g  = $c * $this->e[$i];
287
                        $h  = $c * $p;
288
                        $r  = hypot($p, $this->e[$i]);
289
                        $this->e[$i + 1] = $s * $r;
290
                        $s = $this->e[$i] / $r;
291
                        $c = $p / $r;
292
                        $p = $c * $this->d[$i] - $s * $g;
293
                        $this->d[$i + 1] = $h + $s * ($c * $g + $s * $this->d[$i]);
294
                        // Accumulate transformation.
295
                        for ($k = 0; $k < $this->n; ++$k) {
296
                            $h = $this->V[$k][$i + 1];
297
                            $this->V[$k][$i + 1] = $s * $this->V[$k][$i] + $c * $h;
298
                            $this->V[$k][$i]     = $c * $this->V[$k][$i] - $s * $h;
299
                        }
300
                    }
301
                    $p = -$s * $s2 * $c3 * $el1 * $this->e[$l] / $dl1;
302
                    $this->e[$l] = $s * $p;
303
                    $this->d[$l] = $c * $p;
304
                    // Check for convergence.
305
                } while (abs($this->e[$l]) > $eps * $tst1);
306
            }
307
            $this->d[$l] = $this->d[$l] + $f;
308
            $this->e[$l] = 0.0;
309
        }
310
311
        // Sort eigenvalues and corresponding vectors.
312
        for ($i = 0; $i < $this->n - 1; ++$i) {
313
            $k = $i;
314
            $p = $this->d[$i];
315
            for ($j = $i + 1; $j < $this->n; ++$j) {
316
                if ($this->d[$j] < $p) {
317
                    $k = $j;
318
                    $p = $this->d[$j];
319
                }
320
            }
321
            if ($k != $i) {
322
                $this->d[$k] = $this->d[$i];
323
                $this->d[$i] = $p;
324 View Code Duplication
                for ($j = 0; $j < $this->n; ++$j) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
325
                    $p = $this->V[$j][$i];
326
                    $this->V[$j][$i] = $this->V[$j][$k];
327
                    $this->V[$j][$k] = $p;
328
                }
329
            }
330
        }
331
    }
332
333
334
    /**
335
     *	Nonsymmetric reduction to Hessenberg form.
336
     *
337
     *	This is derived from the Algol procedures orthes and ortran,
338
     *	by Martin and Wilkinson, Handbook for Auto. Comp.,
339
     *	Vol.ii-Linear Algebra, and the corresponding
340
     *	Fortran subroutines in EISPACK.
341
     */
342
    private function orthes()
343
    {
344
        $low  = 0;
345
        $high = $this->n - 1;
346
347
        for ($m = $low + 1; $m <= $high - 1; ++$m) {
348
            // Scale column.
349
            $scale = 0.0;
350 View Code Duplication
            for ($i = $m; $i <= $high; ++$i) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
351
                $scale = $scale + abs($this->H[$i][$m - 1]);
352
            }
353
            if ($scale != 0.0) {
354
                // Compute Householder transformation.
355
                $h = 0.0;
356
                for ($i = $high; $i >= $m; --$i) {
357
                    $this->ort[$i] = $this->H[$i][$m - 1] / $scale;
358
                    $h += $this->ort[$i] * $this->ort[$i];
359
                }
360
                $g = sqrt($h);
361
                if ($this->ort[$m] > 0) {
362
                    $g *= -1;
363
                }
364
                $h -= $this->ort[$m] * $g;
365
                $this->ort[$m] -= $g;
366
                // Apply Householder similarity transformation
367
                // H = (I -u * u' / h) * H * (I -u * u') / h)
368 View Code Duplication
                for ($j = $m; $j < $this->n; ++$j) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
369
                    $f = 0.0;
370
                    for ($i = $high; $i >= $m; --$i) {
371
                        $f += $this->ort[$i] * $this->H[$i][$j];
372
                    }
373
                    $f /= $h;
374
                    for ($i = $m; $i <= $high; ++$i) {
375
                        $this->H[$i][$j] -= $f * $this->ort[$i];
376
                    }
377
                }
378 View Code Duplication
                for ($i = 0; $i <= $high; ++$i) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
379
                    $f = 0.0;
380
                    for ($j = $high; $j >= $m; --$j) {
381
                        $f += $this->ort[$j] * $this->H[$i][$j];
382
                    }
383
                    $f = $f / $h;
384
                    for ($j = $m; $j <= $high; ++$j) {
385
                        $this->H[$i][$j] -= $f * $this->ort[$j];
386
                    }
387
                }
388
                $this->ort[$m] = $scale * $this->ort[$m];
389
                $this->H[$m][$m - 1] = $scale * $g;
390
            }
391
        }
392
393
        // Accumulate transformations (Algol's ortran).
394
        for ($i = 0; $i < $this->n; ++$i) {
395
            for ($j = 0; $j < $this->n; ++$j) {
396
                $this->V[$i][$j] = ($i == $j ? 1.0 : 0.0);
397
            }
398
        }
399
        for ($m = $high - 1; $m >= $low + 1; --$m) {
400
            if ($this->H[$m][$m - 1] != 0.0) {
401 View Code Duplication
                for ($i = $m + 1; $i <= $high; ++$i) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
402
                    $this->ort[$i] = $this->H[$i][$m - 1];
403
                }
404
                for ($j = $m; $j <= $high; ++$j) {
405
                    $g = 0.0;
406
                    for ($i = $m; $i <= $high; ++$i) {
407
                        $g += $this->ort[$i] * $this->V[$i][$j];
408
                    }
409
                    // Double division avoids possible underflow
410
                    $g = ($g / $this->ort[$m]) / $this->H[$m][$m - 1];
411
                    for ($i = $m; $i <= $high; ++$i) {
412
                        $this->V[$i][$j] += $g * $this->ort[$i];
413
                    }
414
                }
415
            }
416
        }
417
    }
418
419
    /**
420
     * Performs complex division.
421
     *
422
     * @param int|float $xr
423
     * @param int|float $xi
424
     * @param int|float $yr
425
     * @param int|float $yi
426
     */
427
    private function cdiv($xr, $xi, $yr, $yi)
428
    {
429
        if (abs($yr) > abs($yi)) {
430
            $r = $yi / $yr;
431
            $d = $yr + $r * $yi;
432
            $this->cdivr = ($xr + $r * $xi) / $d;
433
            $this->cdivi = ($xi - $r * $xr) / $d;
434
        } else {
435
            $r = $yr / $yi;
436
            $d = $yi + $r * $yr;
437
            $this->cdivr = ($r * $xr + $xi) / $d;
438
            $this->cdivi = ($r * $xi - $xr) / $d;
439
        }
440
    }
441
442
    /**
443
     *	Nonsymmetric reduction from Hessenberg to real Schur form.
444
     *
445
     *	Code is derived from the Algol procedure hqr2,
446
     *	by Martin and Wilkinson, Handbook for Auto. Comp.,
447
     *	Vol.ii-Linear Algebra, and the corresponding
448
     *	Fortran subroutine in EISPACK.
449
     */
450
    private function hqr2()
451
    {
452
        //  Initialize
453
        $nn = $this->n;
454
        $n  = $nn - 1;
455
        $low = 0;
456
        $high = $nn - 1;
457
        $eps = pow(2.0, -52.0);
458
        $exshift = 0.0;
459
        $p = $q = $r = $s = $z = 0;
460
        // Store roots isolated by balanc and compute matrix norm
461
        $norm = 0.0;
462
463
        for ($i = 0; $i < $nn; ++$i) {
464
            if (($i < $low) or ($i > $high)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
465
                $this->d[$i] = $this->H[$i][$i];
466
                $this->e[$i] = 0.0;
467
            }
468
            for ($j = max($i - 1, 0); $j < $nn; ++$j) {
469
                $norm = $norm + abs($this->H[$i][$j]);
470
            }
471
        }
472
473
        // Outer loop over eigenvalue index
474
        $iter = 0;
475
        while ($n >= $low) {
476
            // Look for single small sub-diagonal element
477
            $l = $n;
478
            while ($l > $low) {
479
                $s = abs($this->H[$l - 1][$l - 1]) + abs($this->H[$l][$l]);
480
                if ($s == 0.0) {
481
                    $s = $norm;
482
                }
483
                if (abs($this->H[$l][$l - 1]) < $eps * $s) {
484
                    break;
485
                }
486
                --$l;
487
            }
488
            // Check for convergence
489
            // One root found
490
            if ($l == $n) {
491
                $this->H[$n][$n] = $this->H[$n][$n] + $exshift;
492
                $this->d[$n] = $this->H[$n][$n];
493
                $this->e[$n] = 0.0;
494
                --$n;
495
                $iter = 0;
496
                // Two roots found
497
            } elseif ($l == $n - 1) {
498
                $w = $this->H[$n][$n - 1] * $this->H[$n - 1][$n];
499
                $p = ($this->H[$n - 1][$n - 1] - $this->H[$n][$n]) / 2.0;
500
                $q = $p * $p + $w;
501
                $z = sqrt(abs($q));
502
                $this->H[$n][$n] = $this->H[$n][$n] + $exshift;
503
                $this->H[$n - 1][$n - 1] = $this->H[$n - 1][$n - 1] + $exshift;
504
                $x = $this->H[$n][$n];
505
                // Real pair
506
                if ($q >= 0) {
507
                    if ($p >= 0) {
508
                        $z = $p + $z;
509
                    } else {
510
                        $z = $p - $z;
511
                    }
512
                    $this->d[$n - 1] = $x + $z;
513
                    $this->d[$n] = $this->d[$n - 1];
514
                    if ($z != 0.0) {
515
                        $this->d[$n] = $x - $w / $z;
516
                    }
517
                    $this->e[$n - 1] = 0.0;
518
                    $this->e[$n] = 0.0;
519
                    $x = $this->H[$n][$n - 1];
520
                    $s = abs($x) + abs($z);
521
                    $p = $x / $s;
522
                    $q = $z / $s;
523
                    $r = sqrt($p * $p + $q * $q);
524
                    $p = $p / $r;
525
                    $q = $q / $r;
526
                    // Row modification
527 View Code Duplication
                    for ($j = $n - 1; $j < $nn; ++$j) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
528
                        $z = $this->H[$n - 1][$j];
529
                        $this->H[$n - 1][$j] = $q * $z + $p * $this->H[$n][$j];
530
                        $this->H[$n][$j] = $q * $this->H[$n][$j] - $p * $z;
531
                    }
532
                    // Column modification
533 View Code Duplication
                    for ($i = 0; $i <= $n; ++$i) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
534
                        $z = $this->H[$i][$n - 1];
535
                        $this->H[$i][$n - 1] = $q * $z + $p * $this->H[$i][$n];
536
                        $this->H[$i][$n] = $q * $this->H[$i][$n] - $p * $z;
537
                    }
538
                    // Accumulate transformations
539 View Code Duplication
                    for ($i = $low; $i <= $high; ++$i) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
540
                        $z = $this->V[$i][$n - 1];
541
                        $this->V[$i][$n - 1] = $q * $z + $p * $this->V[$i][$n];
542
                        $this->V[$i][$n] = $q * $this->V[$i][$n] - $p * $z;
543
                    }
544
                    // Complex pair
545
                } else {
546
                    $this->d[$n - 1] = $x + $p;
547
                    $this->d[$n]     = $x + $p;
548
                    $this->e[$n - 1] = $z;
549
                    $this->e[$n]     = -$z;
550
                }
551
                $n = $n - 2;
552
                $iter = 0;
553
                // No convergence yet
554
            } else {
555
                // Form shift
556
                $x = $this->H[$n][$n];
557
                $y = 0.0;
558
                $w = 0.0;
559
                if ($l < $n) {
560
                    $y = $this->H[$n - 1][$n - 1];
561
                    $w = $this->H[$n][$n - 1] * $this->H[$n - 1][$n];
562
                }
563
                // Wilkinson's original ad hoc shift
564
                if ($iter == 10) {
565
                    $exshift += $x;
566
                    for ($i = $low; $i <= $n; ++$i) {
567
                        $this->H[$i][$i] -= $x;
568
                    }
569
                    $s = abs($this->H[$n][$n - 1]) + abs($this->H[$n - 1][$n - 2]);
570
                    $x = $y = 0.75 * $s;
571
                    $w = -0.4375 * $s * $s;
572
                }
573
                // MATLAB's new ad hoc shift
574
                if ($iter == 30) {
575
                    $s = ($y - $x) / 2.0;
576
                    $s = $s * $s + $w;
577
                    if ($s > 0) {
578
                        $s = sqrt($s);
579
                        if ($y < $x) {
580
                            $s = -$s;
581
                        }
582
                        $s = $x - $w / (($y - $x) / 2.0 + $s);
583
                        for ($i = $low; $i <= $n; ++$i) {
584
                            $this->H[$i][$i] -= $s;
585
                        }
586
                        $exshift += $s;
587
                        $x = $y = $w = 0.964;
588
                    }
589
                }
590
                // Could check iteration count here.
591
                $iter = $iter + 1;
592
                // Look for two consecutive small sub-diagonal elements
593
                $m = $n - 2;
594
                while ($m >= $l) {
595
                    $z = $this->H[$m][$m];
596
                    $r = $x - $z;
597
                    $s = $y - $z;
598
                    $p = ($r * $s - $w) / $this->H[$m + 1][$m] + $this->H[$m][$m + 1];
599
                    $q = $this->H[$m + 1][$m + 1] - $z - $r - $s;
600
                    $r = $this->H[$m + 2][$m + 1];
601
                    $s = abs($p) + abs($q) + abs($r);
602
                    $p = $p / $s;
603
                    $q = $q / $s;
604
                    $r = $r / $s;
605
                    if ($m == $l) {
606
                        break;
607
                    }
608
                    if (abs($this->H[$m][$m - 1]) * (abs($q) + abs($r)) <
609
                        $eps * (abs($p) * (abs($this->H[$m - 1][$m - 1]) + abs($z) + abs($this->H[$m + 1][$m + 1])))) {
610
                        break;
611
                    }
612
                    --$m;
613
                }
614
                for ($i = $m + 2; $i <= $n; ++$i) {
615
                    $this->H[$i][$i - 2] = 0.0;
616
                    if ($i > $m + 2) {
617
                        $this->H[$i][$i - 3] = 0.0;
618
                    }
619
                }
620
                // Double QR step involving rows l:n and columns m:n
621
                for ($k = $m; $k <= $n - 1; ++$k) {
622
                    $notlast = ($k != $n - 1);
623
                    if ($k != $m) {
624
                        $p = $this->H[$k][$k - 1];
625
                        $q = $this->H[$k + 1][$k - 1];
626
                        $r = ($notlast ? $this->H[$k + 2][$k - 1] : 0.0);
627
                        $x = abs($p) + abs($q) + abs($r);
628
                        if ($x != 0.0) {
629
                            $p = $p / $x;
630
                            $q = $q / $x;
631
                            $r = $r / $x;
632
                        }
633
                    }
634
                    if ($x == 0.0) {
635
                        break;
636
                    }
637
                    $s = sqrt($p * $p + $q * $q + $r * $r);
638
                    if ($p < 0) {
639
                        $s = -$s;
640
                    }
641
                    if ($s != 0) {
642
                        if ($k != $m) {
643
                            $this->H[$k][$k - 1] = -$s * $x;
644
                        } elseif ($l != $m) {
645
                            $this->H[$k][$k - 1] = -$this->H[$k][$k - 1];
646
                        }
647
                        $p = $p + $s;
648
                        $x = $p / $s;
649
                        $y = $q / $s;
650
                        $z = $r / $s;
651
                        $q = $q / $p;
652
                        $r = $r / $p;
653
                        // Row modification
654 View Code Duplication
                        for ($j = $k; $j < $nn; ++$j) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
655
                            $p = $this->H[$k][$j] + $q * $this->H[$k + 1][$j];
656
                            if ($notlast) {
657
                                $p = $p + $r * $this->H[$k + 2][$j];
658
                                $this->H[$k + 2][$j] = $this->H[$k + 2][$j] - $p * $z;
659
                            }
660
                            $this->H[$k][$j] = $this->H[$k][$j] - $p * $x;
661
                            $this->H[$k + 1][$j] = $this->H[$k + 1][$j] - $p * $y;
662
                        }
663
                        // Column modification
664 View Code Duplication
                        for ($i = 0; $i <= min($n, $k + 3); ++$i) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
665
                            $p = $x * $this->H[$i][$k] + $y * $this->H[$i][$k + 1];
666
                            if ($notlast) {
667
                                $p = $p + $z * $this->H[$i][$k + 2];
668
                                $this->H[$i][$k + 2] = $this->H[$i][$k + 2] - $p * $r;
669
                            }
670
                            $this->H[$i][$k] = $this->H[$i][$k] - $p;
671
                            $this->H[$i][$k + 1] = $this->H[$i][$k + 1] - $p * $q;
672
                        }
673
                        // Accumulate transformations
674 View Code Duplication
                        for ($i = $low; $i <= $high; ++$i) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
675
                            $p = $x * $this->V[$i][$k] + $y * $this->V[$i][$k + 1];
676
                            if ($notlast) {
677
                                $p = $p + $z * $this->V[$i][$k + 2];
678
                                $this->V[$i][$k + 2] = $this->V[$i][$k + 2] - $p * $r;
679
                            }
680
                            $this->V[$i][$k] = $this->V[$i][$k] - $p;
681
                            $this->V[$i][$k + 1] = $this->V[$i][$k + 1] - $p * $q;
682
                        }
683
                    }  // ($s != 0)
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
684
                }  // k loop
685
            }  // check convergence
686
        }  // while ($n >= $low)
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
687
688
        // Backsubstitute to find vectors of upper triangular form
689
        if ($norm == 0.0) {
690
            return;
691
        }
692
693
        for ($n = $nn - 1; $n >= 0; --$n) {
694
            $p = $this->d[$n];
695
            $q = $this->e[$n];
696
            // Real vector
697
            if ($q == 0) {
698
                $l = $n;
699
                $this->H[$n][$n] = 1.0;
700
                for ($i = $n - 1; $i >= 0; --$i) {
701
                    $w = $this->H[$i][$i] - $p;
702
                    $r = 0.0;
703
                    for ($j = $l; $j <= $n; ++$j) {
704
                        $r = $r + $this->H[$i][$j] * $this->H[$j][$n];
705
                    }
706
707
                    if ($this->e[$i] < 0.0) {
708
                        $z = $w;
709
                        $s = $r;
710
                    } else {
711
                        $l = $i;
712
                        if ($this->e[$i] == 0.0) {
713
                            if ($w != 0.0) {
714
                                $this->H[$i][$n] = -$r / $w;
715
                            } else {
716
                                $this->H[$i][$n] = -$r / ($eps * $norm);
717
                            }
718
                            // Solve real equations
719
                        } else {
720
                            $x = $this->H[$i][$i + 1];
721
                            $y = $this->H[$i + 1][$i];
722
                            $q = ($this->d[$i] - $p) * ($this->d[$i] - $p) + $this->e[$i] * $this->e[$i];
723
                            $t = ($x * $s - $z * $r) / $q;
724
                            $this->H[$i][$n] = $t;
725
                            if (abs($x) > abs($z)) {
726
                                $this->H[$i + 1][$n] = (-$r - $w * $t) / $x;
727
                            } else {
728
                                $this->H[$i + 1][$n] = (-$s - $y * $t) / $z;
729
                            }
730
                        }
731
                        // Overflow control
732
                        $t = abs($this->H[$i][$n]);
733
                        if (($eps * $t) * $t > 1) {
734
                            for ($j = $i; $j <= $n; ++$j) {
735
                                $this->H[$j][$n] = $this->H[$j][$n] / $t;
736
                            }
737
                        }
738
                    }
739
                }
740
                // Complex vector
741
            } elseif ($q < 0) {
742
                $l = $n - 1;
743
                // Last vector component imaginary so matrix is triangular
744
                if (abs($this->H[$n][$n - 1]) > abs($this->H[$n - 1][$n])) {
745
                    $this->H[$n - 1][$n - 1] = $q / $this->H[$n][$n - 1];
746
                    $this->H[$n - 1][$n] = -($this->H[$n][$n] - $p) / $this->H[$n][$n - 1];
747
                } else {
748
                    $this->cdiv(0.0, -$this->H[$n - 1][$n], $this->H[$n - 1][$n - 1] - $p, $q);
749
                    $this->H[$n - 1][$n - 1] = $this->cdivr;
750
                    $this->H[$n - 1][$n]     = $this->cdivi;
751
                }
752
                $this->H[$n][$n - 1] = 0.0;
753
                $this->H[$n][$n]     = 1.0;
754
                for ($i = $n - 2; $i >= 0; --$i) {
755
                    // double ra,sa,vr,vi;
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
756
                    $ra = 0.0;
757
                    $sa = 0.0;
758
                    for ($j = $l; $j <= $n; ++$j) {
759
                        $ra = $ra + $this->H[$i][$j] * $this->H[$j][$n - 1];
760
                        $sa = $sa + $this->H[$i][$j] * $this->H[$j][$n];
761
                    }
762
                    $w = $this->H[$i][$i] - $p;
763
                    if ($this->e[$i] < 0.0) {
764
                        $z = $w;
765
                        $r = $ra;
766
                        $s = $sa;
767
                    } else {
768
                        $l = $i;
769
                        if ($this->e[$i] == 0) {
770
                            $this->cdiv(-$ra, -$sa, $w, $q);
771
                            $this->H[$i][$n - 1] = $this->cdivr;
772
                            $this->H[$i][$n]     = $this->cdivi;
773
                        } else {
774
                            // Solve complex equations
775
                            $x = $this->H[$i][$i + 1];
776
                            $y = $this->H[$i + 1][$i];
777
                            $vr = ($this->d[$i] - $p) * ($this->d[$i] - $p) + $this->e[$i] * $this->e[$i] - $q * $q;
778
                            $vi = ($this->d[$i] - $p) * 2.0 * $q;
779
                            if ($vr == 0.0 & $vi == 0.0) {
0 ignored issues
show
Comprehensibility introduced by
Consider adding parentheses for clarity. Current Interpretation: ($vr == 0.0) & $vi == 0.0, Probably Intended Meaning: $vr == (0.0 & $vi == 0.0)

When comparing the result of a bit operation, we suggest to add explicit parenthesis and not to rely on PHP’s built-in operator precedence to ensure the code behaves as intended and to make it more readable.

Let’s take a look at these examples:

// Returns always int(0).
return 0 === $foo & 4;
return (0 === $foo) & 4;

// More likely intended return: true/false
return 0 === ($foo & 4);
Loading history...
780
                                $vr = $eps * $norm * (abs($w) + abs($q) + abs($x) + abs($y) + abs($z));
781
                            }
782
                            $this->cdiv($x * $r - $z * $ra + $q * $sa, $x * $s - $z * $sa - $q * $ra, $vr, $vi);
783
                            $this->H[$i][$n - 1] = $this->cdivr;
784
                            $this->H[$i][$n]     = $this->cdivi;
785
                            if (abs($x) > (abs($z) + abs($q))) {
786
                                $this->H[$i + 1][$n - 1] = (-$ra - $w * $this->H[$i][$n - 1] + $q * $this->H[$i][$n]) / $x;
787
                                $this->H[$i + 1][$n]     = (-$sa - $w * $this->H[$i][$n] - $q * $this->H[$i][$n - 1]) / $x;
788
                            } else {
789
                                $this->cdiv(-$r - $y * $this->H[$i][$n - 1], -$s - $y * $this->H[$i][$n], $z, $q);
790
                                $this->H[$i + 1][$n - 1] = $this->cdivr;
791
                                $this->H[$i + 1][$n]     = $this->cdivi;
792
                            }
793
                        }
794
                        // Overflow control
795
                        $t = max(abs($this->H[$i][$n - 1]), abs($this->H[$i][$n]));
796
                        if (($eps * $t) * $t > 1) {
797
                            for ($j = $i; $j <= $n; ++$j) {
798
                                $this->H[$j][$n - 1] = $this->H[$j][$n - 1] / $t;
799
                                $this->H[$j][$n]     = $this->H[$j][$n] / $t;
800
                            }
801
                        }
802
                    } // end else
803
                } // end for
804
            } // end else for complex case
805
        } // end for
806
807
        // Vectors of isolated roots
808
        for ($i = 0; $i < $nn; ++$i) {
809
            if ($i < $low | $i > $high) {
810
                for ($j = $i; $j < $nn; ++$j) {
811
                    $this->V[$i][$j] = $this->H[$i][$j];
812
                }
813
            }
814
        }
815
816
        // Back transformation to get eigenvectors of original matrix
817
        for ($j = $nn - 1; $j >= $low; --$j) {
818
            for ($i = $low; $i <= $high; ++$i) {
819
                $z = 0.0;
820
                for ($k = $low; $k <= min($j, $high); ++$k) {
821
                    $z = $z + $this->V[$i][$k] * $this->H[$k][$j];
822
                }
823
                $this->V[$i][$j] = $z;
824
            }
825
        }
826
    } // end hqr2
827
828
    /**
829
     * Return the eigenvector matrix
830
     *
831
     * @access public
832
     *
833
     * @return array
834
     */
835
    public function getEigenvectors()
836
    {
837
        $vectors = $this->V;
838
839
        // Always return the eigenvectors of length 1.0
840
        $vectors = new Matrix($vectors);
841
        $vectors = array_map(function ($vect) {
842
            $sum = 0;
843 View Code Duplication
            for ($i = 0; $i < count($vect); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
844
                $sum += $vect[$i] ** 2;
845
            }
846
847
            $sum = sqrt($sum);
848 View Code Duplication
            for ($i = 0; $i < count($vect); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
849
                $vect[$i] /= $sum;
850
            }
851
852
            return $vect;
853
        }, $vectors->transpose()->toArray());
854
855
        return $vectors;
856
    }
857
858
    /**
859
     *	Return the real parts of the eigenvalues<br>
860
     *  d = real(diag(D));
861
     *
862
     *	@return array
863
     */
864
    public function getRealEigenvalues()
865
    {
866
        return $this->d;
867
    }
868
869
    /**
870
     *	Return the imaginary parts of the eigenvalues <br>
871
     *  d = imag(diag(D))
872
     *
873
     *	@return array
874
     */
875
    public function getImagEigenvalues()
876
    {
877
        return $this->e;
878
    }
879
880
    /**
881
     *	Return the block diagonal eigenvalue matrix
882
     *
883
     *	@return array
884
     */
885
    public function getDiagonalEigenvalues()
886
    {
887
        $D = [];
888
889
        for ($i = 0; $i < $this->n; ++$i) {
890
            $D[$i] = array_fill(0, $this->n, 0.0);
891
            $D[$i][$i] = $this->d[$i];
892
            if ($this->e[$i] == 0) {
893
                continue;
894
            }
895
896
            $o = ($this->e[$i] > 0) ? $i + 1 : $i - 1;
897
            $D[$i][$o] = $this->e[$i];
898
        }
899
900
        return $D;
901
    }
902
}    //	class EigenvalueDecomposition
903