Completed
Push — develop ( e1f81f...539a89 )
by Adrien
16:11
created

SingularValueDecomposition::getU()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PhpSpreadsheet\Shared\JAMA;
4
5
/**
6
 *
7
 *    For an m-by-n matrix A with m >= n, the singular value decomposition is
8
 *    an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
9
 *    an n-by-n orthogonal matrix V so that A = U*S*V'.
10
 *
11
 *    The singular values, sigma[$k] = S[$k][$k], are ordered so that
12
 *    sigma[0] >= sigma[1] >= ... >= sigma[n-1].
13
 *
14
 *    The singular value decompostion always exists, so the constructor will
15
 *    never fail.  The matrix condition number and the effective numerical
16
 *    rank can be computed from this decomposition.
17
 *
18
 *    @author  Paul Meagher
19
 *    @license PHP v3.0
20
 *    @version 1.1
21
 */
22
class SingularValueDecomposition
23
{
24
    /**
25
     *    Internal storage of U.
26
     *    @var array
27
     */
28
    private $U = array();
29
30
    /**
31
     *    Internal storage of V.
32
     *    @var array
33
     */
34
    private $V = array();
35
36
    /**
37
     *    Internal storage of singular values.
38
     *    @var array
39
     */
40
    private $s = array();
41
42
    /**
43
     *    Row dimension.
44
     *    @var int
45
     */
46
    private $m;
47
48
    /**
49
     *    Column dimension.
50
     *    @var int
51
     */
52
    private $n;
53
54
    /**
55
     *    Construct the singular value decomposition
56
     *
57
     *    Derived from LINPACK code.
58
     *
59
     *    @param $A Rectangular matrix
60
     *    @return Structure to access U, S and V.
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
61
     */
62
    public function __construct($Arg)
63
    {
64
        // Initialize.
65
        $A = $Arg->getArrayCopy();
66
        $this->m = $Arg->getRowDimension();
67
        $this->n = $Arg->getColumnDimension();
68
        $nu      = min($this->m, $this->n);
69
        $e       = array();
70
        $work    = array();
71
        $wantu   = true;
72
        $wantv   = true;
73
        $nct = min($this->m - 1, $this->n);
74
        $nrt = max(0, min($this->n - 2, $this->m));
75
76
        // Reduce A to bidiagonal form, storing the diagonal elements
77
        // in s and the super-diagonal elements in e.
78
        for ($k = 0; $k < max($nct, $nrt); ++$k) {
79
            if ($k < $nct) {
80
                // Compute the transformation for the k-th column and
81
                // place the k-th diagonal in s[$k].
82
                // Compute 2-norm of k-th column without under/overflow.
83
                $this->s[$k] = 0;
84
                for ($i = $k; $i < $this->m; ++$i) {
85
                    $this->s[$k] = hypo($this->s[$k], $A[$i][$k]);
86
                }
87
                if ($this->s[$k] != 0.0) {
88
                    if ($A[$k][$k] < 0.0) {
89
                        $this->s[$k] = -$this->s[$k];
90
                    }
91
                    for ($i = $k; $i < $this->m; ++$i) {
92
                        $A[$i][$k] /= $this->s[$k];
93
                    }
94
                    $A[$k][$k] += 1.0;
95
                }
96
                $this->s[$k] = -$this->s[$k];
97
            }
98
99
            for ($j = $k + 1; $j < $this->n; ++$j) {
100
                if (($k < $nct) & ($this->s[$k] != 0.0)) {
101
                    // Apply the transformation.
102
                    $t = 0;
103
                    for ($i = $k; $i < $this->m; ++$i) {
104
                        $t += $A[$i][$k] * $A[$i][$j];
105
                    }
106
                    $t = -$t / $A[$k][$k];
107
                    for ($i = $k; $i < $this->m; ++$i) {
108
                        $A[$i][$j] += $t * $A[$i][$k];
109
                    }
110
                    // Place the k-th row of A into e for the
111
                    // subsequent calculation of the row transformation.
112
                    $e[$j] = $A[$k][$j];
113
                }
114
            }
115
116
            if ($wantu and ($k < $nct)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and 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...
117
                // Place the transformation in U for subsequent back
118
                // multiplication.
119
                for ($i = $k; $i < $this->m; ++$i) {
120
                    $this->U[$i][$k] = $A[$i][$k];
121
                }
122
            }
123
124
            if ($k < $nrt) {
125
                // Compute the k-th row transformation and place the
126
                // k-th super-diagonal in e[$k].
127
                // Compute 2-norm without under/overflow.
128
                $e[$k] = 0;
129
                for ($i = $k + 1; $i < $this->n; ++$i) {
130
                    $e[$k] = hypo($e[$k], $e[$i]);
131
                }
132
                if ($e[$k] != 0.0) {
133
                    if ($e[$k+1] < 0.0) {
134
                        $e[$k] = -$e[$k];
135
                    }
136
                    for ($i = $k + 1; $i < $this->n; ++$i) {
137
                        $e[$i] /= $e[$k];
138
                    }
139
                    $e[$k+1] += 1.0;
140
                }
141
                $e[$k] = -$e[$k];
142
                if (($k+1 < $this->m) and ($e[$k] != 0.0)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and 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...
143
                    // Apply the transformation.
144 View Code Duplication
                    for ($i = $k+1; $i < $this->m; ++$i) {
0 ignored issues
show
Duplication introduced by
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...
145
                        $work[$i] = 0.0;
146
                    }
147
                    for ($j = $k+1; $j < $this->n; ++$j) {
148
                        for ($i = $k+1; $i < $this->m; ++$i) {
149
                            $work[$i] += $e[$j] * $A[$i][$j];
150
                        }
151
                    }
152
                    for ($j = $k + 1; $j < $this->n; ++$j) {
153
                        $t = -$e[$j] / $e[$k+1];
154
                        for ($i = $k + 1; $i < $this->m; ++$i) {
155
                            $A[$i][$j] += $t * $work[$i];
156
                        }
157
                    }
158
                }
159 View Code Duplication
                if ($wantv) {
0 ignored issues
show
Duplication introduced by
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...
160
                    // Place the transformation in V for subsequent
161
                    // back multiplication.
162
                    for ($i = $k + 1; $i < $this->n; ++$i) {
163
                        $this->V[$i][$k] = $e[$i];
164
                    }
165
                }
166
            }
167
        }
168
169
        // Set up the final bidiagonal matrix or order p.
170
        $p = min($this->n, $this->m + 1);
171
        if ($nct < $this->n) {
172
            $this->s[$nct] = $A[$nct][$nct];
173
        }
174
        if ($this->m < $p) {
175
            $this->s[$p-1] = 0.0;
176
        }
177
        if ($nrt + 1 < $p) {
178
            $e[$nrt] = $A[$nrt][$p-1];
179
        }
180
        $e[$p-1] = 0.0;
181
        // If required, generate U.
182
        if ($wantu) {
183
            for ($j = $nct; $j < $nu; ++$j) {
184
                for ($i = 0; $i < $this->m; ++$i) {
185
                    $this->U[$i][$j] = 0.0;
186
                }
187
                $this->U[$j][$j] = 1.0;
188
            }
189
            for ($k = $nct - 1; $k >= 0; --$k) {
190
                if ($this->s[$k] != 0.0) {
191 View Code Duplication
                    for ($j = $k + 1; $j < $nu; ++$j) {
0 ignored issues
show
Duplication introduced by
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...
192
                        $t = 0;
193
                        for ($i = $k; $i < $this->m; ++$i) {
194
                            $t += $this->U[$i][$k] * $this->U[$i][$j];
195
                        }
196
                        $t = -$t / $this->U[$k][$k];
197
                        for ($i = $k; $i < $this->m; ++$i) {
198
                            $this->U[$i][$j] += $t * $this->U[$i][$k];
199
                        }
200
                    }
201
                    for ($i = $k; $i < $this->m; ++$i) {
202
                        $this->U[$i][$k] = -$this->U[$i][$k];
203
                    }
204
                    $this->U[$k][$k] = 1.0 + $this->U[$k][$k];
205 View Code Duplication
                    for ($i = 0; $i < $k - 1; ++$i) {
0 ignored issues
show
Duplication introduced by
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...
206
                        $this->U[$i][$k] = 0.0;
207
                    }
208
                } else {
209 View Code Duplication
                    for ($i = 0; $i < $this->m; ++$i) {
0 ignored issues
show
Duplication introduced by
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->U[$i][$k] = 0.0;
211
                    }
212
                    $this->U[$k][$k] = 1.0;
213
                }
214
            }
215
        }
216
217
        // If required, generate V.
218
        if ($wantv) {
219
            for ($k = $this->n - 1; $k >= 0; --$k) {
220
                if (($k < $nrt) and ($e[$k] != 0.0)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and 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...
221
                    for ($j = $k + 1; $j < $nu; ++$j) {
222
                        $t = 0;
223
                        for ($i = $k + 1; $i < $this->n; ++$i) {
224
                            $t += $this->V[$i][$k]* $this->V[$i][$j];
225
                        }
226
                        $t = -$t / $this->V[$k+1][$k];
227
                        for ($i = $k + 1; $i < $this->n; ++$i) {
228
                            $this->V[$i][$j] += $t * $this->V[$i][$k];
229
                        }
230
                    }
231
                }
232 View Code Duplication
                for ($i = 0; $i < $this->n; ++$i) {
0 ignored issues
show
Duplication introduced by
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...
233
                    $this->V[$i][$k] = 0.0;
234
                }
235
                $this->V[$k][$k] = 1.0;
236
            }
237
        }
238
239
        // Main iteration loop for the singular values.
240
        $pp   = $p - 1;
241
        $iter = 0;
242
        $eps  = pow(2.0, -52.0);
243
244
        while ($p > 0) {
245
            // Here is where a test for too many iterations would go.
246
            // This section of the program inspects for negligible
247
            // elements in the s and e arrays.  On completion the
248
            // variables kase and k are set as follows:
249
            // kase = 1  if s(p) and e[k-1] are negligible and k<p
250
            // kase = 2  if s(k) is negligible and k<p
251
            // kase = 3  if e[k-1] is negligible, k<p, and
252
            //           s(k), ..., s(p) are not negligible (qr step).
253
            // kase = 4  if e(p-1) is negligible (convergence).
254
            for ($k = $p - 2; $k >= -1; --$k) {
255
                if ($k == -1) {
256
                    break;
257
                }
258
                if (abs($e[$k]) <= $eps * (abs($this->s[$k]) + abs($this->s[$k+1]))) {
259
                    $e[$k] = 0.0;
260
                    break;
261
                }
262
            }
263
            if ($k == $p - 2) {
264
                $kase = 4;
265
            } else {
266
                for ($ks = $p - 1; $ks >= $k; --$ks) {
267
                    if ($ks == $k) {
268
                        break;
269
                    }
270
                    $t = ($ks != $p ? abs($e[$ks]) : 0.) + ($ks != $k + 1 ? abs($e[$ks-1]) : 0.);
271
                    if (abs($this->s[$ks]) <= $eps * $t) {
272
                        $this->s[$ks] = 0.0;
273
                        break;
274
                    }
275
                }
276
                if ($ks == $k) {
277
                    $kase = 3;
278
                } elseif ($ks == $p-1) {
279
                    $kase = 1;
280
                } else {
281
                    $kase = 2;
282
                    $k = $ks;
283
                }
284
            }
285
            ++$k;
286
287
            // Perform the task indicated by kase.
288
            switch ($kase) {
289
                // Deflate negligible s(p).
290
                case 1:
291
                    $f = $e[$p-2];
292
                    $e[$p-2] = 0.0;
293
                    for ($j = $p - 2; $j >= $k; --$j) {
294
                        $t  = hypo($this->s[$j], $f);
295
                        $cs = $this->s[$j] / $t;
296
                        $sn = $f / $t;
297
                        $this->s[$j] = $t;
298
                        if ($j != $k) {
299
                            $f = -$sn * $e[$j-1];
300
                            $e[$j-1] = $cs * $e[$j-1];
301
                        }
302 View Code Duplication
                        if ($wantv) {
0 ignored issues
show
Duplication introduced by
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...
303
                            for ($i = 0; $i < $this->n; ++$i) {
304
                                $t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$p-1];
305
                                $this->V[$i][$p-1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$p-1];
306
                                $this->V[$i][$j] = $t;
307
                            }
308
                        }
309
                    }
310
                    break;
311
                // Split at negligible s(k).
312
                case 2:
313
                    $f = $e[$k-1];
314
                    $e[$k-1] = 0.0;
315
                    for ($j = $k; $j < $p; ++$j) {
316
                        $t = hypo($this->s[$j], $f);
317
                        $cs = $this->s[$j] / $t;
318
                        $sn = $f / $t;
319
                        $this->s[$j] = $t;
320
                        $f = -$sn * $e[$j];
321
                        $e[$j] = $cs * $e[$j];
322 View Code Duplication
                        if ($wantu) {
0 ignored issues
show
Duplication introduced by
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...
323
                            for ($i = 0; $i < $this->m; ++$i) {
324
                                $t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$k-1];
325
                                $this->U[$i][$k-1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$k-1];
326
                                $this->U[$i][$j] = $t;
327
                            }
328
                        }
329
                    }
330
                    break;
331
                // Perform one qr step.
332
                case 3:
333
                    // Calculate the shift.
334
                    $scale = max(max(max(max(abs($this->s[$p-1]), abs($this->s[$p-2])), abs($e[$p-2])), abs($this->s[$k])), abs($e[$k]));
335
                    $sp   = $this->s[$p-1] / $scale;
336
                    $spm1 = $this->s[$p-2] / $scale;
337
                    $epm1 = $e[$p-2] / $scale;
338
                    $sk   = $this->s[$k] / $scale;
339
                    $ek   = $e[$k] / $scale;
340
                    $b    = (($spm1 + $sp) * ($spm1 - $sp) + $epm1 * $epm1) / 2.0;
341
                    $c    = ($sp * $epm1) * ($sp * $epm1);
342
                    $shift = 0.0;
343
                    if (($b != 0.0) || ($c != 0.0)) {
344
                        $shift = sqrt($b * $b + $c);
345
                        if ($b < 0.0) {
346
                            $shift = -$shift;
347
                        }
348
                        $shift = $c / ($b + $shift);
349
                    }
350
                    $f = ($sk + $sp) * ($sk - $sp) + $shift;
351
                    $g = $sk * $ek;
352
                    // Chase zeros.
353
                    for ($j = $k; $j < $p-1; ++$j) {
354
                        $t  = hypo($f, $g);
355
                        $cs = $f/$t;
356
                        $sn = $g/$t;
357
                        if ($j != $k) {
358
                            $e[$j-1] = $t;
359
                        }
360
                        $f = $cs * $this->s[$j] + $sn * $e[$j];
361
                        $e[$j] = $cs * $e[$j] - $sn * $this->s[$j];
362
                        $g = $sn * $this->s[$j+1];
363
                        $this->s[$j+1] = $cs * $this->s[$j+1];
364 View Code Duplication
                        if ($wantv) {
0 ignored issues
show
Duplication introduced by
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...
365
                            for ($i = 0; $i < $this->n; ++$i) {
366
                                $t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$j+1];
367
                                $this->V[$i][$j+1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$j+1];
368
                                $this->V[$i][$j] = $t;
369
                            }
370
                        }
371
                        $t = hypo($f, $g);
372
                        $cs = $f/$t;
373
                        $sn = $g/$t;
374
                        $this->s[$j] = $t;
375
                        $f = $cs * $e[$j] + $sn * $this->s[$j+1];
376
                        $this->s[$j+1] = -$sn * $e[$j] + $cs * $this->s[$j+1];
377
                        $g = $sn * $e[$j+1];
378
                        $e[$j+1] = $cs * $e[$j+1];
379 View Code Duplication
                        if ($wantu && ($j < $this->m - 1)) {
0 ignored issues
show
Duplication introduced by
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...
380
                            for ($i = 0; $i < $this->m; ++$i) {
381
                                $t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$j+1];
382
                                $this->U[$i][$j+1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$j+1];
383
                                $this->U[$i][$j] = $t;
384
                            }
385
                        }
386
                    }
387
                    $e[$p-2] = $f;
388
                    $iter = $iter + 1;
389
                    break;
390
                // Convergence.
391
                case 4:
392
                    // Make the singular values positive.
393
                    if ($this->s[$k] <= 0.0) {
394
                        $this->s[$k] = ($this->s[$k] < 0.0 ? -$this->s[$k] : 0.0);
395 View Code Duplication
                        if ($wantv) {
0 ignored issues
show
Duplication introduced by
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...
396
                            for ($i = 0; $i <= $pp; ++$i) {
397
                                $this->V[$i][$k] = -$this->V[$i][$k];
398
                            }
399
                        }
400
                    }
401
                    // Order the singular values.
402
                    while ($k < $pp) {
403
                        if ($this->s[$k] >= $this->s[$k+1]) {
404
                            break;
405
                        }
406
                        $t = $this->s[$k];
407
                        $this->s[$k] = $this->s[$k+1];
408
                        $this->s[$k+1] = $t;
409 View Code Duplication
                        if ($wantv and ($k < $this->n - 1)) {
0 ignored issues
show
Duplication introduced by
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...
Comprehensibility Best Practice introduced by
Using logical operators such as and 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...
410
                            for ($i = 0; $i < $this->n; ++$i) {
411
                                $t = $this->V[$i][$k+1];
412
                                $this->V[$i][$k+1] = $this->V[$i][$k];
413
                                $this->V[$i][$k] = $t;
414
                            }
415
                        }
416 View Code Duplication
                        if ($wantu and ($k < $this->m-1)) {
0 ignored issues
show
Duplication introduced by
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...
Comprehensibility Best Practice introduced by
Using logical operators such as and 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...
417
                            for ($i = 0; $i < $this->m; ++$i) {
418
                                $t = $this->U[$i][$k+1];
419
                                $this->U[$i][$k+1] = $this->U[$i][$k];
420
                                $this->U[$i][$k] = $t;
421
                            }
422
                        }
423
                        ++$k;
424
                    }
425
                    $iter = 0;
426
                    --$p;
427
                    break;
428
            } // end switch
429
        } // end while
430
    } // end constructor
431
432
433
    /**
434
     *    Return the left singular vectors
435
     *
436
     *    @access public
437
     *    @return U
438
     */
439
    public function getU()
440
    {
441
        return new Matrix($this->U, $this->m, min($this->m + 1, $this->n));
442
    }
443
444
445
    /**
446
     *    Return the right singular vectors
447
     *
448
     *    @access public
449
     *    @return V
450
     */
451
    public function getV()
452
    {
453
        return new Matrix($this->V);
454
    }
455
456
457
    /**
458
     *    Return the one-dimensional array of singular values
459
     *
460
     *    @access public
461
     *    @return diagonal of S.
462
     */
463
    public function getSingularValues()
464
    {
465
        return $this->s;
466
    }
467
468
469
    /**
470
     *    Return the diagonal matrix of singular values
471
     *
472
     *    @access public
473
     *    @return S
474
     */
475
    public function getS()
476
    {
477
        for ($i = 0; $i < $this->n; ++$i) {
478
            for ($j = 0; $j < $this->n; ++$j) {
479
                $S[$i][$j] = 0.0;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$S was never initialized. Although not strictly required by PHP, it is generally a good practice to add $S = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
480
            }
481
            $S[$i][$i] = $this->s[$i];
0 ignored issues
show
Bug introduced by
The variable $S 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...
482
        }
483
        return new Matrix($S);
484
    }
485
486
487
    /**
488
     *    Two norm
489
     *
490
     *    @access public
491
     *    @return max(S)
0 ignored issues
show
Documentation introduced by
The doc-type max(S) could not be parsed: Expected "|" or "end of type", but got "(" at position 3. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
492
     */
493
    public function norm2()
494
    {
495
        return $this->s[0];
496
    }
497
498
499
    /**
500
     *    Two norm condition number
501
     *
502
     *    @access public
503
     *    @return max(S)/min(S)
0 ignored issues
show
Documentation introduced by
The doc-type max(S)/min(S) could not be parsed: Expected "|" or "end of type", but got "(" at position 3. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
504
     */
505
    public function cond()
506
    {
507
        return $this->s[0] / $this->s[min($this->m, $this->n) - 1];
508
    }
509
510
511
    /**
512
     *    Effective numerical matrix rank
513
     *
514
     *    @access public
515
     *    @return Number of nonnegligible singular values.
516
     */
517
    public function rank()
518
    {
519
        $eps = pow(2.0, -52.0);
520
        $tol = max($this->m, $this->n) * $this->s[0] * $eps;
521
        $r = 0;
522
        for ($i = 0; $i < count($this->s); ++$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...
523
            if ($this->s[$i] > $tol) {
524
                ++$r;
525
            }
526
        }
527
        return $r;
528
    }
529
}
530