Passed
Push — main ( 2b687d...6e9b51 )
by Shubham
01:34
created

matrix   F

Complexity

Total Complexity 352

Size/Duplication

Total Lines 1642
Duplicated Lines 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 590
c 1
b 1
f 0
dl 0
loc 1642
rs 2
wmc 352

128 Methods

Rating   Name   Duplication   Size   Complexity  
A joinAbove() 0 15 6
A greater() 0 7 3
A is_zero() 0 5 2
B setData() 0 15 11
A sumScalar() 0 6 2
A subtractScalar() 0 6 2
A abs() 0 2 1
A getShape() 0 2 1
A divideScalar() 0 6 2
A getDiagonalVal() 0 3 2
A uniform() 0 7 2
A floor() 0 2 1
B less() 0 14 7
A gaussian() 0 28 5
A diagonal() 0 7 2
A log() 0 6 2
A asVector() 0 6 2
A eign() 0 2 1
A subtract() 0 7 3
A median() 0 15 3
A equalVector() 0 9 6
A inverse() 0 17 4
A log1p() 0 2 1
A asArray() 0 8 3
A clipLower() 0 10 3
A normINF() 0 2 1
A modVector() 0 9 5
A ref() 0 2 1
A __toString() 0 2 1
A ar() 0 8 3
A rref() 0 2 1
A minimum() 0 7 3
A modMatrix() 0 7 4
A powVector() 0 9 5
A multiplyVector() 0 9 5
A zeros() 0 6 2
A equalScalar() 0 6 3
A printMatrix() 0 7 3
A swapRows() 0 5 2
A tan() 0 2 1
A identity() 0 12 5
A full() 0 6 2
A normL2() 0 2 1
A asin() 0 2 1
A diagonalAsVector() 0 9 3
A subtractVector() 0 9 5
A powScalar() 0 6 2
A argMax() 0 6 2
A powMatrix() 0 7 4
A mean() 0 2 1
A dignoalInterChange() 0 5 3
A diminish_right() 0 8 3
A swapValue() 0 4 1
A __construct() 0 6 3
A svd() 0 2 1
A acos() 0 2 1
A trace() 0 13 5
A factory() 0 2 1
A variance() 0 13 4
A joinRight() 0 15 6
A dotVector() 0 5 3
A cholesky() 0 2 1
A divideMatrix() 0 7 4
A isSymmetric() 0 13 4
A normL1() 0 2 1
A multiplyMatrix() 0 9 5
A square() 0 2 1
A reciprocal() 0 2 1
A greaterScalar() 0 6 3
A atan() 0 2 1
A null() 0 6 2
A scale() 0 11 3
A addScaleRow() 0 3 2
A subtractMatrix() 0 7 4
A dotMatrix() 0 5 3
A copyMatrix() 0 2 1
A sumRows() 0 10 3
A sumMatrix() 0 7 4
A getDtype() 0 2 1
A isSquare() 0 5 2
A convolve() 0 2 1
A divide() 0 7 3
A is_rowZero() 0 7 3
A subtractColumnVector() 0 9 5
A transpose() 0 8 3
A joinLeft() 0 15 6
A sum() 0 7 3
A normFrob() 0 2 1
A joinBelow() 0 15 6
A equalMatrix() 0 7 5
A exp() 0 2 1
A pow() 0 7 3
A clip() 0 14 4
A map() 0 6 2
A rowAsVector() 0 6 2
A det() 0 11 2
A flattenArray() 0 9 5
A sin() 0 2 1
A scaleRow() 0 3 2
A ones() 0 6 2
A ceil() 0 2 1
A randn() 0 7 2
A argMin() 0 7 2
A swapCols() 0 5 2
A degToRad() 0 2 1
A pseudoInverse() 0 22 3
A poisson() 0 16 4
A has_ZeroRow() 0 7 3
A mod() 0 7 3
A maximum() 0 7 3
A exp1() 0 2 1
A diminish_left() 0 8 3
A modScalar() 0 6 2
A greaterVector() 0 9 6
A colAsVector() 0 6 2
A greaterMatrix() 0 7 5
A equal() 0 7 3
A cos() 0 2 1
A lu() 0 2 1
A covariance() 0 13 3
A sumVector() 0 9 5
A divideVector() 0 9 5
A clipUpper() 0 10 3
A getSize() 0 2 1
A radToDeg() 0 2 1
A sqrt() 0 2 1
A dot() 0 5 2
A multiply() 0 7 3

How to fix   Complexity   

Complex Class

Complex classes like matrix often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use matrix, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Np;
6
7
use Np\core\nd;
8
use Np\core\blas;
9
use Np\core\lapack;
10
use Np\reductions\ref;
11
use Np\reductions\rref;
12
use Np\decompositions\lu;
13
use Np\decompositions\svd;
14
use Np\decompositions\eigen;
15
use Np\decompositions\cholesky;
16
17
/**
18
 * Matrix
19
 * A fast lite memory efficient Scientific Computing for php
20
 * 
21
 * @package   NumPhp
22
 * @category  Scientific Computing
23
 * @author    ghost (Shubham Chaudhary)
24
 * @email     [email protected]
25
 * @copyright (c) 2020-2021, Shubham Chaudhary
26
 */
27
class matrix extends nd{
28
29
    /**
30
     * create empty 2d matrix for given data type
31
     * @param int $row  num of rows 
32
     * @param int $col  num of cols
33
     * @param int $dtype matrix data type float|double
34
     * @return \Np\matrix
35
     */
36
    public static function factory(int $row, int $col, int $dtype = self::FLOAT): matrix {
37
        return new self($row, $col, $dtype);
38
    }
39
40
    /**
41
     * create 2d matrix using php array
42
     * @param array $data
43
     * @param int $dtype matrix data type float|double
44
     * @return \Np\matrix
45
     */
46
    public static function ar(array $data, int $dtype = self::FLOAT): matrix {
47
        if (is_array($data) && is_array($data[0])) {
48
            $ar = self::factory(count($data), count($data[0]), $dtype);
49
            $ar->setData($data);
50
            unset($data);
51
            return $ar;
52
        } else {
53
            self::_err('given array is not rank-2 or given is not an array');
54
        }
55
    }
56
57
    /**
58
     * create one like 2d matrix
59
     * @param int $row
60
     * @param int $col
61
     * @return \Np\matrix
62
     */
63
    public static function ones(int $row, int $col, int $dtype = self::FLOAT): matrix {
64
        $ar = self::factory($row, $col, $dtype);
65
        for ($i = 0; $i < $ar->ndim; ++$i) {
66
            $ar->data[$i] = 1;
67
        }
68
        return $ar;
69
    }
70
71
    /**
72
     * Create Matrix with random values
73
     * @param int $row
74
     * @param int $col
75
     * @param int $dtype  Float|Double
76
     * @return \Np\matrix
77
     */
78
    public static function randn(int $row, int $col, int $dtype = self::FLOAT): matrix {
79
        $ar = self::factory($row, $col, $dtype);
80
        $max = getrandmax();
81
        for ($i = 0; $i < $ar->ndim; ++$i) {
82
            $ar->data[$i] = rand() / $max;
83
        }
84
        return $ar;
85
    }
86
87
    /**
88
     * Return 2d matrix with uniform values
89
     * @param int $row
90
     * @param int $col
91
     * @param int $dtype
92
     * @return \Np\matrix
93
     */
94
    public static function uniform(int $row, int $col, int $dtype = self::FLOAT): matrix {
95
        $ar = self::factory($row, $col, $dtype);
96
        $max = getrandmax();
97
        for ($i = 0; $i < $ar->ndim; ++$i) {
98
            $ar->data[$i] = rand(-$max, $max) / $max;
99
        }
100
        return $ar;
101
    }
102
103
    /**
104
     * Return a zero matrix with the given dimensions.
105
     * @param int $row
106
     * @param int $col
107
     * @param int $dtype
108
     * @return \Np\matrix
109
     */
110
    public static function zeros(int $row, int $col, int $dtype = self::FLOAT): matrix {
111
        $ar = self::factory($row, $col, $dtype);
112
        for ($i = 0; $i < $ar->ndim; ++$i) {
113
            $ar->data[$i] = 0.0;
114
        }
115
        return $ar;
116
    }
117
118
    /**
119
     * create a null like 2d matrix
120
     * @param int $row
121
     * @param int $col
122
     * @return \Np\matrix
123
     */
124
    public static function null(int $row, int $col, int $dtype = self::FLOAT): matrix {
125
        $ar = self::factory($row, $col, $dtype);
126
        for ($i = 0; $i < $ar->ndim; ++$i) {
127
            $ar->data[$i] = null;
128
        }
129
        return $ar;
130
    }
131
132
    /**
133
     * create a 2d matrix with given scalar value
134
     * @param int $row
135
     * @param int $col
136
     * @param int|float|double $val
137
     * @return \Np\matrix
138
     */
139
    public static function full(int $row, int $col, $val, int $dtype = self::FLOAT): matrix {
140
        $ar = self::factory($row, $col, $dtype);
141
        for ($i = 0; $i < $ar->ndim; ++$i) {
142
            $ar->data[$i] = $val;
143
        }
144
        return $ar;
145
    }
146
147
    /**
148
     * create a diagonal 2d matrix with given 1d array;
149
     * @param array $elements
150
     * @return \Np\matrix
151
     */
152
    public static function diagonal(array $elements, int $dtype = self::FLOAT): matrix {
153
        $n = count($elements);
154
        $ar = self::factory($n, $n, $dtype);
155
        for ($i = 0; $i < $n; ++$i) {
156
            $ar->data[$i * $n + $i] = $elements[$i]; #for ($j = 0; $j < $n; ++$j) {$i === $j ? $elements[$i] : 0;#} 
157
        }
158
        return $ar;
159
    }
160
161
    /**
162
     * Generate a m x n matrix with elements from a Poisson distribution.
163
     *
164
     * @param int $row
165
     * @param int $col
166
     * @param float $lambda
167
     * @param int $dtype 
168
     * @return \Np\matrix
169
     */
170
    public static function poisson(int $row, int $col, float $lambda = 1.0, int $dtype = self::FLOAT): matrix {
171
        $ar = self::factory($row, $col, $dtype);
172
        $max = getrandmax();
173
        $l = exp(-$lambda);
174
        for ($i = 0; $i < $row; ++$i) {
175
            for ($j = 0; $j < $col; ++$j) {
176
                $k = 0;
177
                $p = 1.0;
178
                while ($p > $l) {
179
                    ++$k;
180
                    $p = $p * rand() / $max;
181
                }
182
                $ar->data[$i * $col + $j] = $k - 1;
183
            }
184
        }
185
        return $ar;
186
    }
187
188
    /**
189
     * Return a standard normally distributed random matrix i.e values
190
     * between -1 and 1.
191
     * @param int $row
192
     * @param int $col
193
     * @param int $dtype Description
194
     * @return \Np\matrix
195
     */
196
    public static function gaussian(int $row, int $col, int $dtype = self::FLOAT): matrix {
197
        $max = getrandmax();
198
        $a = $extras = [];
199
200
        while (count($a) < $row) {
201
            $rowA = [];
202
203
            if (!empty($extras)) {
204
                $rowA[] = array_pop($extras);
205
            }
206
207
            while (count($rowA) < $col) {
208
                $r = sqrt(-2.0 * log(rand() / $max));
209
210
                $phi = rand() / $max * self::TWO_PI;
211
212
                $rowA[] = $r * sin($phi);
213
                $rowA[] = $r * cos($phi);
214
            }
215
216
            if (count($rowA) > $col) {
217
                $extras[] = array_pop($rowA);
218
            }
219
220
            $a[] = $rowA;
221
        }
222
223
        return self::ar($a, $dtype);
224
    }
225
226
    /**
227
     * create an identity matrix with the given dimensions.
228
     * @param int $n
229
     * @param int $dtype
230
     * @return matrix
231
     * @throws \InvalidArgumentException
232
     */
233
    public static function identity(int $n, int $dtype = self::FLOAT): matrix {
234
        if ($n < 1) {
235
            self::_dimensionaMisMatchErr('dimensionality must be greater than 0 on all axes.');
236
        }
237
238
        $ar = self::factory($n, $n, $dtype);
239
        for ($i = 0; $i < $n; ++$i) {
240
            for ($j = 0; $j < $n; ++$j) {
241
                $ar->data[$i * $n + $j] = $i === $j ? 1 : 0;
242
            }
243
        }
244
        return $ar;
245
    }
246
247
    /**
248
     * Return the element-wise minimum of two matrices.
249
     * 
250
     * @param \Np\matrix $m
251
     * @return matrix
252
     */
253
    public function minimum(matrix $m): matrix {
254
        if ($this->checkShape($this,$m)) {
255
            $ar = self::factory($this->row, $this->col, $this->dtype);
256
            for ($i = 0; $i < $this->ndim; ++$i) {
257
                $ar->data[$i] = min($this->data[$i], $m->data[$i]);
258
            }
259
            return $ar;
260
        }
261
    }
262
263
    /**
264
     * Return the element-wise maximum of two matrices.
265
     * 
266
     * @param \Np\matrix $m
267
     * @return matrix
268
     */
269
    public function maximum(matrix $m): matrix {
270
        if ($this->checkShape($this, $m)) {
271
            $ar = self::factory($this->row, $this->col, $this->dtype);
272
            for ($i = 0; $i < $this->ndim; ++$i) {
273
                $ar->data[$i] = max($this->data[$i], $m->data[$i]);
274
            }
275
            return $ar;
276
        }
277
    }
278
279
    /**
280
     * 2D convolution between a matrix ma and kernel kb, with a given stride.
281
     * @param \Np\matrix $m
282
     * @param int $stride
283
     * @return matrix
284
     */
285
    public function convolve(matrix $m, int $stride = 1): matrix {
286
        return convolve::conv2D($this, $m, $stride);
287
    }
288
289
    /**
290
     * Calculate the determinant of the matrix.
291
     * @return float
292
     */
293
    public function det(): float {
294
        if (!$this->isSquare()) {
295
            self::_err('determinant is undefined for a non square matrix');
296
        }
297
        $lu = $this->lu();
298
        $nSwaps = $lu->p()->diagonalAsVector()->subtract($lu->p()->diagonalAsVector()->sum())->col - 1;
299
        $detP = (-1) ** $nSwaps;
300
        $detL = $lu->l()->diagonalAsVector()->product();
301
        $detU = $lu->u()->diagonalAsVector()->product();
302
        unset($lu);
303
        return ($detP * $detL * $detU);
304
    }
305
306
    /**
307
     * Return the trace of the matrix i.e the sum of all diagonal elements of a square matrix.
308
     * @return float
309
     */
310
    public function trace(): float {
311
        if (!$this->isSquare()) {
312
            self::_err('Error::matrix is not a squared can not Trace!');
313
        }
314
        $trace = 0.0;
315
        for ($i = 0; $i < $this->row; ++$i) {
316
            for ($j = 0; $j < $this->col; ++$j) {
317
                if ($i == $j) {
318
                    $trace += $this->data[$i * $this->col + $i];
319
                }
320
            }
321
        }
322
        return $trace;
323
    }
324
325
    /**
326
     * dignoalInterChange
327
     */
328
    public function dignoalInterChange() {
329
        for ($i = 0; $i < $this->row; ++$i) {
330
            for ($j = 0; $j < $this->col; ++$j) {
331
                $tmp = $this->data[$i * $this->col - $j];
332
                $this->data[$i * $this->col - $j] = $tmp;
333
            }
334
        }
335
    }
336
337
    //----------------Linear Algebra Opreations-------------------------------
338
339
    /**
340
     *  
341
     * get dot product of m.m or m.v
342
     * 
343
     * @param \Np\matrix|\Np\vector $d
344
     * @return matrix|vector
345
     */
346
    public function dot(matrix|vector $d): matrix|vector {
347
        if ($d instanceof self) {
348
            return $this->dotMatrix($d);
349
        } else {
350
            return $this->dotVector($d);
351
        }
352
    }
353
354
    /**
355
     * get matrix & matrix dot product
356
     * @param \Np\matrix $matrix
357
     * @return \Np\matrix
358
     */
359
    protected function dotMatrix(matrix $matrix): matrix {
360
        if ($this->checkDtype($this, $matrix) && $this->checkDimensions($this,$matrix)) {
361
            $ar = self::factory($this->row, $this->col, $this->dtype);
362
            blas::gemm($this, $matrix, $ar);
363
            return $ar;
364
        }
365
    }
366
367
    /**
368
     * get dot product of matrix & a vector
369
     * @param \Np\vector $vector
370
     * @return \Np\vector
371
     */
372
    protected function dotVector(vector $vector): vector {
373
        if ($this->checkDtype($this, $vector) && $this->checkDimensions($vector, $this)) {
374
            $mvr = vector::factory($this->col, $this->dtype);
375
            blas::gemv($this, $vector, $mvr);
376
            return $mvr;
377
        }
378
    }
379
380
    //---------------Arthmetic Opreations-----------------------------------
381
382
    /**
383
     * multiply this matrix with another matrix|scalar element-wise
384
     * Matrix Scalar\Matrix multiplication
385
     * @param int|float|matrix|vector $m
386
     * @return matrix|vector
387
     */
388
    public function multiply(int|float|matrix|vector $m): matrix|vector {
389
        if ($m instanceof self) {
390
            return $this->multiplyMatrix($m);
391
        } else if ($m instanceof vector) {
392
            return $this->multiplyVector($m);
393
        } else {
394
            return $this->scale($m);
395
        }
396
    }
397
398
    /**
399
     * 
400
     * @param \Np\vector $v
401
     * @return matrix
402
     */
403
    protected function multiplyVector(vector $v): matrix {
404
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
405
            $ar = matrix::factory($this->row, $this->col, $this->dtype);
406
            for ($i = 0; $i < $this->row; ++$i) {
407
                for ($j = 0; $j < $this->col; ++$j) {
408
                    $ar->data[$i * $this->col + $j] = $v->data[$j] * $this->data[$i * $this->col + $j];
409
                }
410
            }
411
            return $ar;
412
        }
413
    }
414
415
    /**
416
     * 
417
     * @param \Np\matrix $m
418
     * @return matrix
419
     */
420
    protected function multiplyMatrix(matrix $m): matrix {
421
        if ($this->checkDtype($this, $m) && $this->checkShape($this, $m)) {
422
            $ar = self::factory($this->row, $this->col, $this->dtype);
423
            for ($i = 0; $i < $this->row; ++$i) {
424
                for ($j = 0; $j < $this->col; ++$j) {
425
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] * $m->data[$i * $this->col + $j];
426
                }
427
            }
428
            return $ar;
429
        }
430
    }
431
432
    /**
433
     * 
434
     * @param int|float $scalar
435
     * @return matrix
436
     */
437
    public function scale(int|float $scalar): matrix {
438
        if ($scalar == 0) {
439
            return self::zeros($this->row, $this->col, $this->dtype);
440
        }
441
442
        $ar = $this->copyMatrix();
443
        for ($i = 0; $i < $this->ndim; ++$i) {
444
            $ar->data[$i] *= $scalar;
445
        }
446
447
        return $ar;
448
    }
449
450
    /**
451
     * Sum of Rows of matrix
452
     * @return vector
453
     */
454
    public function sumRows(): vector {
455
        $vr = vector::factory($this->row, $this->dtype);
456
        for ($i = 0; $i < $this->row; ++$i) {
457
            $sum = 0.0;
458
            for ($j = 0; $j < $this->col; ++$j) {
459
                $sum += $this->data[$i * $this->col + $j];
460
            }
461
            $vr->data[$i] = $sum;
462
        }
463
        return $vr;
464
    }
465
466
    /**
467
     * Sum of two matrix, vector or a scalar to current matrix
468
     * 
469
     * @param int|float|matrix|vector $m
470
     * @return matrix
471
     */
472
    public function sum(int|float|matrix|vector $m): matrix {
473
        if ($m instanceof self) {
474
            return $this->sumMatrix($m);
475
        } elseif ($m instanceof vector) {
476
            return $this->sumVector($m);
477
        } else {
478
            return $this->sumScalar($m);
479
        }
480
    }
481
482
    protected function sumScalar(int|float $s): matrix {
483
        $ar = self::factory($this->row, $this->col, $this->dtype);
484
        for ($i = 0; $i < $this->ndim; ++$i) {
485
            $ar->data[$i] = $this->data[$i] + $s;
486
        }
487
        return $ar;
488
    }
489
490
    protected function sumMatrix(matrix $m): matrix {
491
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
492
            $ar = self::factory($this->row, $this->col, $this->dtype);
493
            for ($i = 0; $i < $this->ndim; ++$i) {
494
                $ar->data[$i] = $this->data[$i] + $m->data[$i];
495
            }
496
            return $ar;
497
        }
498
    }
499
500
    protected function sumVector(vector $v): matrix {
501
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
502
            $ar = self::factory($this->row, $this->col, $this->dtype);
503
            for ($i = 0; $i < $this->row; ++$i) {
504
                for ($j = 0; $j < $this->col; ++$j) {
505
                    $ar->data[$i * $this->col + $j] = $v->data[$j] + $this->data[$i * $this->col + $j];
506
                }
507
            }
508
            return $ar;
509
        }
510
    }
511
512
    /**
513
     * subtract another matrix, vector or a scalar to this matrix
514
     * @param int|float|matrix|vector $d matrix|$scalar to subtract this matrix
515
     * @return \Np\matrix
516
     */
517
    public function subtract(int|float|matrix|vector $d): matrix {
518
        if ($d instanceof self) {
519
            return $this->subtractMatrix($d);
520
        } elseif ($d instanceof vector) {
521
            return $this->subtractVector($d);
522
        } else {
523
            return $this->subtractScalar($d);
524
        }
525
    }
526
527
    protected function subtractScalar(int|float $s): matrix {
528
        $ar = self::factory($this->row, $this->col, $this->dtype);
529
        for ($i = 0; $i < $this->ndim; ++$i) {
530
            $ar->data[$i] = $this->data[$i] - $s;
531
        }
532
        return $ar;
533
    }
534
535
    /**
536
     * 
537
     * @param matrix $m
538
     * @return matrix
539
     */
540
    protected function subtractMatrix(matrix $m): matrix {
541
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
542
            $ar = self::factory($this->row, $this->col, $this->dtype);
543
            for ($i = 0; $i < $this->ndim; ++$i) {
544
                $ar->data[$i] = $this->data[$i] - $m->data[$i];
545
            }
546
            return $ar;
547
        }
548
    }
549
550
    /**
551
     * 
552
     * @param vector $v
553
     * @return matrix
554
     */
555
    protected function subtractVector(vector $v): matrix {
556
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this,$v)) {
557
            $ar = self::factory($this->row, $this->col, $this->dtype);
558
            for ($i = 0; $i < $this->row; ++$i) {
559
                for ($j = 0; $j < $this->col; ++$j) {
560
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] - $v->data[$j];
561
                }
562
            }
563
            return $ar;
564
        }
565
    }
566
567
    /**
568
     * 
569
     * @param vector $v
570
     * @return matrix
571
     */
572
    public function subtractColumnVector(vector $v): matrix {
573
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
574
            $ar = self::factory($this->row, $this->col, $this->dtype);
575
            for ($j = 0; $j < $this->col; ++$j) {
576
                for ($i = 0; $i < $this->row; ++$i) {
577
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] - $v->data[$i];
578
                }
579
            }
580
            return $ar;
581
        }
582
    }
583
584
    /**
585
     * Return the division of two elements, element-wise.
586
     * @param int|float|matrix $d
587
     * @return matrix
588
     */
589
    public function divide(int|float|matrix|vector $d): matrix {
590
        if ($d instanceof self) {
591
            return $this->divideMatrix($d);
592
        } elseif ($d instanceof vector) {
593
            return $this->divideVector($d);
594
        } else {
595
            return $this->divideScalar($d);
596
        }
597
    }
598
599
    protected function divideMatrix(matrix $m): matrix {
600
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
601
            $ar = self::factory($this->row, $this->col, $this->dtype);
602
            for ($i = 0; $i < $this->ndim; ++$i) {
603
                $ar->data[$i] = $this->data[$i] / $m->data[$i];
604
            }
605
            return $ar;
606
        }
607
    }
608
609
    protected function divideVector(vector $v): matrix {
610
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
611
            $ar = self::factory($this->row, $this->col, $this->dtype);
612
            for ($i = 0; $i < $this->row; ++$i) {
613
                for ($j = 0; $j < $this->col; ++$j) {
614
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] / $v->data[$j];
615
                }
616
            }
617
            return $ar;
618
        }
619
    }
620
621
    protected function divideScalar(int|float $s): matrix {
622
        $ar = self::factory($this->row, $this->col, $this->dtype);
623
        for ($i = 0; $i < $this->ndim; ++$i) {
624
            $ar->data[$i] = $this->data[$i] / $s;
625
        }
626
        return $ar;
627
    }
628
629
    /**
630
     * 
631
     * Raise this matrix to the power of the element-wise entry in another matrix.
632
     * 
633
     * @param int|float|matrix $m
634
     * @return matrix
635
     */
636
    public function pow(int|float|matrix|vector $d): matrix {
637
        if ($d instanceof self) {
638
            return $this->powMatrix($d);
639
        } else if ($d instanceof vector) {
640
            return $this->powVector($d);
641
        } else {
642
            return $this->powScalar($d);
643
        }
644
    }
645
646
    protected function powMatrix(matrix $m): matrix {
647
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
648
            $ar = self::factory($this->row, $this->col, $this->dtype);
649
            for ($i = 0; $i < $this->ndim; ++$i) {
650
                $ar->data[$i] = $this->data[$i] ** $m->data[$i];
651
            }
652
            return $ar;
653
        }
654
    }
655
656
    protected function powVector(vector $v): matrix {
657
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
658
            $ar = self::factory($this->row, $this->col, $this->dtype);
659
            for ($i = 0; $i < $this->row; ++$i) {
660
                for ($j = 0; $j < $this->col; ++$j) {
661
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] ** $v->data[$j];
662
                }
663
            }
664
            return $ar;
665
        }
666
    }
667
668
    protected function powScalar(int|float $s): matrix {
669
        $ar = $this->copyMatrix();
670
        for ($i = 0; $i < $this->ndim; ++$i) {
671
            $ar->data[$i] **= $s;
672
        }
673
        return $ar;
674
    }
675
676
    /**
677
     * Calculate the modulus i.e remainder of division between this matrix and another matrix.
678
     * @param int|float|matrix|vector $d
679
     * @return matrix
680
     */
681
    public function mod(int|float|matrix|vector $d): matrix {
682
        if ($d instanceof self) {
683
            $this->modMatrix($d);
684
        } else if ($d instanceof vector) {
685
            $this->modVector($d);
686
        } else {
687
            $this->modScalar($d);
688
        }
689
    }
690
691
    protected function modMatrix(matrix $m): matrix {
692
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
693
            $ar = self::factory($this->row, $this->col, $this->dtype);
694
            for ($i = 0; $i < $this->ndim; ++$i) {
695
                $ar->data[$i] = $this->data[$i] % $m->data[$i];
696
            }
697
            return $ar;
698
        } 
699
    }
700
701
    protected function modVector(vector $v): matrix {
702
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
703
            $ar = self::factory($this->row, $this->col, $this->dtype);
704
            for ($i = 0; $i < $this->row; ++$i) {
705
                for ($j = 0; $j < $this->col; ++$j) {
706
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] % $v->data[$j];
707
                }
708
            }
709
            return $ar;
710
        }
711
    }
712
713
    protected function modScalar(int|float $s): matrix {
714
        $ar = $this->copyMatrix();
715
        for ($i = 0; $i < $this->ndim; ++$i) {
716
            $ar->data[$i] %= $s;
717
        }
718
        return $ar;
719
    }
720
721
    /**
722
     * Return the element-wise reciprocal of the matrix.
723
     * 
724
     * @return matrix
725
     */
726
    public function reciprocal(): matrix {
727
        return self::ones($this->row, $this->col, $this->dtype)->divideMatrix($this);
728
    }
729
730
    /**
731
     * 
732
     * @param int|float $d
733
     * @return bool
734
     */
735
    public static function is_zero($d): bool {
736
        if (abs($d) < self::EPSILON) {
737
            return true;
738
        }
739
        return false;
740
    }
741
742
    /**
743
     *  is row zero
744
     * @param int $row
745
     * @return bool
746
     */
747
    public function is_rowZero(int $row): bool {
748
        for ($i = 0; $i < $this->col; ++$i) {
749
            if ($this->data[$row * $this->col + $i] != 0) {
750
                return false;
751
            }
752
        }
753
        return true;
754
    }
755
756
    /**
757
     * 
758
     * @return bool
759
     */
760
    public function has_ZeroRow(): bool {
761
        for ($i = 0; $i < $this->row; ++$i) {
762
            if ($this->is_rowZero($i)) {
763
                return true;
764
            }
765
        }
766
        return false;
767
    }
768
769
    /**
770
     * Transpose the matrix i.e row become cols and cols become rows.
771
     * @return \Np\matrix
772
     */
773
    public function transpose(): matrix {
774
        $ar = self::factory($this->col, $this->row, $this->dtype);
775
        for ($i = 0; $i < $ar->row; ++$i) {
776
            for ($j = 0; $j < $ar->col; ++$j) {
777
                $ar->data[$i * $ar->col + $j] = $this->data[$j * $ar->col + $i];
778
            }
779
        }
780
        return $ar;
781
    }
782
783
    /**
784
     * swap specific values in matrix
785
     * @param int $i1
786
     * @param int $i2
787
     */
788
    public function swapValue(int $i1, int $i2) {
789
        $tmp = $this->data[$i1];
790
        $this->data[$i1] = $this->data[$i2];
791
        $this->data[$i2] = $tmp;
792
    }
793
794
    /**
795
     * swap specific rows in matrix
796
     * @param int $r1
797
     * @param int $r2
798
     */
799
    public function swapRows(int $r1, int $r2) {
800
        for ($i = 0; $i < $this->col; ++$i) {
801
            $tmp = $this->data[$r1 * $this->col + $i];
802
            $this->data[$r1 * $this->col + $i] = $this->data[$r2 * $this->col + $i];
803
            $this->data[$r2 * $this->col + $i] = $tmp;
804
        }
805
    }
806
807
    /**
808
     * swap specific cols in matrix
809
     * @param int $c1
810
     * @param int $c2
811
     */
812
    public function swapCols(int $c1, int $c2) {
813
        for ($i = 0; $i < $this->row; ++$i) {
814
            $tmp = $this->data[$i * $this->row + $c1];
815
            $this->data[$i * $this->row + $c1] = $this->data[$i * $this->row + $c2];
816
            $this->data[$i * $this->row + $c2] = $tmp;
817
        }
818
    }
819
820
    /**
821
     * scale all the elements of a row 
822
     * @param int $row
823
     * @param float $c
824
     */
825
    public function scaleRow(int $row, float $c) {
826
        for ($i = 0; $i < $this->col; ++$i) {
827
            $this->data[$row * $this->col + $i] *= $c;
828
        }
829
    }
830
831
    /**
832
     * 
833
     * @param int $r1
834
     * @param int $r2
835
     * @param float $c
836
     */
837
    public function addScaleRow(int $r1, int $r2, float $c) {
838
        for ($i = 0; $i < $this->col; ++$i) {
839
            $this->data[$r2 * $this->col + $i] += $this->data[$r1 * $this->col + $i] * $c;
840
        }
841
    }
842
843
    /**
844
     * Attach given matrix to the left of this matrix.
845
     * 
846
     * @param \Np\matrix $m
847
     * @return \Np\matrix
848
     */
849
    public function joinLeft(matrix $m): matrix {
850
        if ($this->row != $m->row && !$this->checkDtype($this, $m)) {
851
            self::_err('Error::Invalid size! or DataType!');
852
        }
853
        $col = $this->col + $m->col;
854
        $ar = self::factory($this->row, $col, $this->dtype);
855
        for ($i = 0; $i < $this->row; ++$i) {
856
            for ($j = 0; $j < $this->col; ++$j) {
857
                $ar->data[$i * $col + $j] = $this->data[$i * $this->col + $j];
858
            }
859
            for ($j = 0; $j < $m->col; ++$j) {
860
                $ar->data[$i * $col + ($this->col + $j)] = $m->data[$i * $m->col + $j];
861
            }
862
        }
863
        return $ar;
864
    }
865
866
    /**
867
     * Join matrix m to the Right of this matrix.
868
     * @param \Np\matrix $m
869
     * @return matrix
870
     */
871
    public function joinRight(matrix $m): matrix {
872
        if ($this->row != $m->row && !$this->checkDtype($this,$m)) {
873
            self::_err('Error::Invalid size! or DataType!');
874
        }
875
        $col = $this->col + $m->col;
876
        $ar = self::factory($this->row, $col, $this->dtype);
877
        for ($i = 0; $i < $m->row; ++$i) {
878
            for ($j = 0; $j < $m->col; ++$j) {
879
                $ar->data[$i * $col + $j] = $m->data[$i * $m->col + $j];
880
            }
881
            for ($j = 0; $j < $this->col; ++$j) {
882
                $ar->data[$i * $col + ($this->col + $j)] = $this->data[$i * $this->col + $j];
883
            }
884
        }
885
        return $ar;
886
    }
887
888
    /**
889
     * Join matrix m Above this matrix.
890
     * @param \Np\matrix $m
891
     * @return matrix
892
     */
893
    public function joinAbove(matrix $m): matrix {
894
        if ($this->col !== $m->col && !$this->checkDtype($this, $m)) {
895
            self::_err('Error::Invalid size! or DataType!');
896
        }
897
        $row = $this->row + $m->row;
898
        $ar = self::factory($row, $this->col, $this->dtype);
899
        for ($i = 0; $i < $m->row; ++$i) {
900
            for ($j = 0; $j < $m->col; ++$j) {
901
                $ar->data[$i * $m->col + $j] = $m->data[$i * $m->col + $j];
902
            }
903
            for ($j = 0; $j < $this->col; ++$j) {
904
                $ar->data[($i + $this->row) * $this->col + $j] = $this->data[$i * $this->col + $j];
905
            }
906
        }
907
        return $ar;
908
    }
909
910
    /**
911
     * Join matrix m below this matrix.
912
     * @param \Np\matrix $m
913
     * @return matrix
914
     */
915
    public function joinBelow(matrix $m): matrix {
916
        if ($this->col !== $m->col && !$this->checkDtype($this, $m)) {
917
            self::_err('Error::Invalid size! or DataType!');
918
        }
919
        $row = $this->row + $m->row;
920
        $ar = self::factory($row, $this->col, $this->dtype);
921
        for ($i = 0; $i < $this->row; ++$i) {
922
            for ($j = 0; $j < $this->col; ++$j) {
923
                $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j];
924
            }
925
            for ($j = 0; $j < $m->col; ++$j) {
926
                $ar->data[($i + $m->row) * $m->col + $j] = $m->data[$i * $m->col + $j];
927
            }
928
        }
929
        return $ar;
930
    }
931
932
    /**
933
     * Calculate the row echelon form of the matrix. 
934
     * Return the reduced matrix.
935
     *
936
     * @return matrix|null
937
     */
938
    public function ref(): matrix|null {
939
        return ref::factory($this);
940
    }
941
942
    /**
943
     * Return the lower triangular matrix of the Cholesky decomposition.
944
     *
945
     * @return matrix|null
946
     */
947
    public function cholesky(): matrix|null {
948
        return cholesky::factory($this);
949
    }
950
951
    /**
952
     * FIXME--------------
953
     * RREF
954
     * The reduced row echelon form (RREF) of a matrix.
955
     * @return \Np\matrix
956
     */
957
    public function rref(): matrix {
958
        return rref::factory($this);
959
    }
960
961
    /**
962
     * make copy of the matrix
963
     * @return \Np\matrix
964
     */
965
    public function copyMatrix(): matrix {
966
        return clone $this;
967
    }
968
969
    /**
970
     * 
971
     * @param int $cols
972
     * @return \Np\matrix
973
     */
974
    public function diminish_left(int $cols): matrix {
975
        $ar = self::factory($this->row, $cols, $this->dtype);
976
        for ($i = 0; $i < $ar->row; ++$i) {
977
            for ($j = 0; $j < $ar->col; ++$j) {
978
                $ar->data[$i * $ar->col + $j] = $this->data[$i * $this->col + $j];
979
            }
980
        }
981
        return $ar;
982
    }
983
984
    /**
985
     * 
986
     * @param int $cols
987
     * @return \Np\matrix
988
     */
989
    public function diminish_right(int $cols): matrix {
990
        $ar = self::factory($this->row, $cols, $this->dtype);
991
        for ($i = 0; $i < $ar->row; ++$i) {
992
            for ($j = 0; $j < $ar->col; ++$j) {
993
                $ar->data[$i * $ar->col + $j] = $this->data[$i * $this->col - $cols + $j];
994
            }
995
        }
996
        return $ar;
997
    }
998
999
    /**
1000
     * Return the index of the maximum element in every row of the matrix.
1001
     * @return \Np\vector int
1002
     */
1003
    public function argMax(): vector {
1004
        $v = vector::factory($this->row, vector::INT);
1005
        for ($i = 0; $i < $this->row; ++$i) {
1006
            $v->data[$i] = blas::max($this->rowAsVector($i));
1007
        }
1008
        return $v;
1009
    }
1010
1011
    /**
1012
     * Return the index of the minimum element in every row of the matrix.
1013
     * @return \Np\vector int
1014
     */
1015
    public function argMin(): vector {
1016
        $v = vector::factory($this->row, vector::INT);
1017
        for ($i = 0; $i < $this->row; ++$i) {
1018
            $v->data[$i] = blas::min($this->rowAsVector($i));
1019
        }
1020
1021
        return $v;
1022
    }
1023
1024
    /**
1025
     * Set given data in matrix
1026
     * @param int|float|array $data
1027
     * @param bool $dignoal
1028
     * @return void
1029
     */
1030
    public function setData(int|float|array $data, bool $dignoal = false): void {
1031
        if ($dignoal == false) {
1032
            if (is_array($data) && is_array($data[0])) {
1033
                $f = $this->flattenArray($data);
1034
                foreach ($f as $k => $v) {
1035
                    $this->data[$k] = $v;
1036
                }
1037
            } elseif (is_numeric($data)) {
1038
                for ($i = 0; $i < $this->ndim; ++$i) {
1039
                    $this->data[$i] = $data;
1040
                }
1041
            }
1042
        } elseif (is_numeric($data) || is_array($data) && !is_array($data[0])) {
1043
            for ($i = 0; $i < $this->row; ++$i) {
1044
                $this->data[$i * $this->col * $i] = $data;
1045
            }
1046
        }
1047
    }
1048
1049
    /**
1050
     * get the shape of matrix
1051
     * @return object
1052
     */
1053
    public function getShape(): object {
1054
        return (object) ['m' => $this->row, 'n' => $this->col];
1055
    }
1056
1057
    /**
1058
     * get the number of elements in the matrix.
1059
     * @return int
1060
     */
1061
    public function getSize(): int {
1062
        return $this->row * $this->col;
1063
    }
1064
1065
    /**
1066
     * 
1067
     * @return bool
1068
     */
1069
    public function isSquare(): bool {
1070
        if ($this->row === $this->col) {
1071
            return true;
1072
        }
1073
        return false;
1074
    }
1075
1076
    public function getDtype() {
1077
        return $this->dtype;
1078
    }
1079
1080
    /**
1081
     * Return a row as vector from the matrix.
1082
     * @param int $index
1083
     * @return \Np\vector
1084
     */
1085
    public function rowAsVector(int $index): vector {
1086
        $vr = vector::factory($this->col, $this->dtype);
1087
        for ($j = 0; $j < $this->col; ++$j) {
1088
            $vr->data[$j] = $this->data[$index * $this->col + $j];
1089
        }
1090
        return $vr;
1091
    }
1092
1093
    /**
1094
     * Return a col as vector from the matrix.
1095
     * @param int $index
1096
     * @return \Np\vector
1097
     */
1098
    public function colAsVector(int $index): vector {
1099
        $vr = vector::factory($this->row, $this->dtype);
1100
        for ($i = 0; $i < $this->row; ++$i) {
1101
            $vr->data[$i] = $this->data[$i * $this->row + $index];
1102
        }
1103
        return $vr;
1104
    }
1105
1106
    /**
1107
     * Return the diagonal elements of a square matrix as a vector.
1108
     * @return \Np\vector
1109
     */
1110
    public function diagonalAsVector(): vector {
1111
        if (!$this->isSquare()) {
1112
            self::_err('Can not trace of a none square matrix');
1113
        }
1114
        $vr = vector::factory($this->row, $this->dtype);
1115
        for ($i = 0; $i < $this->row; ++$i) {
1116
            $vr->data[$i] = $this->getDiagonalVal($i);
1117
        }
1118
        return $vr;
1119
    }
1120
1121
    /**
1122
     * Flatten i.e unravel the matrix into a vector.
1123
     *
1124
     * @return \Np\vector
1125
     */
1126
    public function asVector(): vector {
1127
        $vr = vector::factory($this->ndim, $this->dtype);
1128
        for ($i = 0; $i < $this->ndim; ++$i) {
1129
            $vr->data[$i] = $this->data[$i];
1130
        }
1131
        return $vr;
1132
    }
1133
1134
    /**
1135
     * Return the elements of the matrix in a 2-d array.
1136
     * @return array
1137
     */
1138
    public function asArray(): array {
1139
        $ar = array_fill(0, $this->row, array_fill(0, $this->col, null));
1140
        for ($i = 0; $i < $this->row; ++$i) {
1141
            for ($j = 0; $j < $this->col; ++$j) {
1142
                $ar[$i][$j] = $this->data[$i * $this->col + $j];
1143
            }
1144
        }
1145
        return $ar;
1146
    }
1147
1148
    /**
1149
     * get a diagonal value from matrix
1150
     * @param int $i
1151
     * @return float
1152
     */
1153
    public function getDiagonalVal(int $i) {
1154
        if ($this->isSquare()) {
1155
            return $this->data[$i * $this->row + $i];
1156
        }
1157
    }
1158
1159
    /**
1160
     *
1161
     * Compute the multiplicative inverse of the matrix.
1162
     * @return matrix
1163
     */
1164
    public function inverse(): matrix {
1165
        if (!$this->isSquare()) {
1166
            self::_err('Error::invalid Size of matrix!');
1167
        }
1168
        $imat = $this->copyMatrix();
1169
        $ipiv = vector::factory($this->row, vector::INT);
1170
        $lp = lapack::getrf($imat, $ipiv);
1171
        if ($lp != 0) {
1172
            return null;
1173
        }
1174
        $lp = lapack::getri($imat, $ipiv);
1175
        if ($lp != 0) {
1176
            return null;
1177
        }
1178
        unset($ipiv);
1179
        unset($lp);
1180
        return $imat;
1181
    }
1182
    
1183
    /**
1184
     * Compute the (Moore-Penrose) pseudo inverse of the general matrix.
1185
     * @return matrix|null
1186
     */
1187
    public function pseudoInverse(): matrix|null {
1188
        $k = min($this->row, $this->col);
1189
        $s = vector::factory($k, $this->dtype);
1190
        $u = self::factory($this->row, $this->row, $this->dtype);
1191
        $vt = self::factory($this->col, $this->col, $this->dtype);
1192
        $imat = $this->copyMatrix();
1193
        $lp = lapack::gesdd($imat, $s, $u, $vt);
1194
        if ($lp != 0) {
1195
            return null;
1196
        }
1197
        for ($i = 0; $i < $k; ++$i) {
1198
            blas::scale(1.0 / $s->data[$i], $vt->rowAsVector($i));
1199
        }
1200
        unset($imat);
1201
        unset($k);
1202
        unset($lp);
1203
        unset($s);
1204
        $mr = self::factory($this->col, $this->row, $this->dtype);
1205
        blas::gemm($vt, $u, $mr);
1206
        unset($u);
1207
        unset($vt);
1208
        return $mr;
1209
    }
1210
1211
    /**
1212
     * Compute the singular value decomposition of a matrix and 
1213
     * return an object of the singular values and unitary matrices
1214
     *
1215
     * @return object (u,s,v)
1216
     */
1217
    public function svd(): svd {
1218
        return svd::factory($this);
1219
    }
1220
1221
    /**
1222
     * Compute the eigen decomposition of a general matrix.
1223
     * return the eigenvalues and eigenvectors as object
1224
     * 
1225
     * @param bool $symmetric
1226
     * @return eigen
1227
     */
1228
    public function eign(bool $symmetric = false): eigen {
1229
        return eigen::factory($this, $symmetric);
1230
    }
1231
1232
    /**
1233
     *  
1234
     * Compute the LU factorization of matrix.
1235
     * return lower, upper, and permutation matrices as object.
1236
     * 
1237
     * @return lu
1238
     */
1239
    public function lu(): lu {
1240
        return lu::factory($this);
1241
    }
1242
1243
    /**
1244
     * Return the L1 norm of the matrix.
1245
     * @return float
1246
     */
1247
    public function normL1(): float {
1248
        return lapack::lange('l', $this);
1249
    }
1250
1251
    /**
1252
     * Return the L2 norm of the matrix.
1253
     * @return float
1254
     */
1255
    public function normL2(): float {
1256
        return lapack::lange('f', $this);
1257
    }
1258
1259
    /**
1260
     * Return the L1 norm of the matrix.
1261
     * @return float
1262
     */
1263
    public function normINF(): float {
1264
        return lapack::lange('i', $this);
1265
    }
1266
1267
    /**
1268
     * Return the Frobenius norm of the matrix.
1269
     * @return float
1270
     */
1271
    public function normFrob(): float {
1272
        return $this->normL2();
1273
    }
1274
1275
    /**
1276
     * Run a function over all of the elements in the matrix. 
1277
     * @param callable $func
1278
     * @return \Np\matrix
1279
     */
1280
    public function map(callable $func): matrix {
1281
        $ar = self::factory($this->row, $this->col, $this->dtype);
1282
        for ($i = 0; $i < $this->ndim; ++$i) {
1283
            $ar->data[$i] = $func($this->data[$i]);
1284
        }
1285
        return $ar;
1286
    }
1287
1288
    public function abs(): matrix {
1289
        return $this->map('abs');
1290
    }
1291
1292
    public function sqrt(): matrix {
1293
        return $this->map('sqrt');
1294
    }
1295
1296
    public function exp(): matrix {
1297
        return $this->map('exp');
1298
    }
1299
1300
    public function exp1(): matrix {
1301
        return $this->map('exp1');
1302
    }
1303
1304
    public function log(float $b = M_E): matrix {
1305
        $ar = $this->copyMatrix();
1306
        for ($i = 0; $i < $ar->ndim; ++$i) {
1307
            log($ar->data[$i], $b);
1308
        }
1309
        return $ar;
1310
    }
1311
1312
    public function log1p(): matrix {
1313
        return $this->map('log1p');
1314
    }
1315
1316
    public function sin(): matrix {
1317
        return $this->map('sin');
1318
    }
1319
1320
    public function asin(): matrix {
1321
        return $this->map('asin');
1322
    }
1323
1324
    public function cos(): matrix {
1325
        return $this->map('cos');
1326
    }
1327
1328
    public function acos(): matrix {
1329
        return $this->map('acos');
1330
    }
1331
1332
    public function tan(): matrix {
1333
        return $this->map('tan');
1334
    }
1335
1336
    public function atan(): matrix {
1337
        return $this->map('atan');
1338
    }
1339
1340
    public function radToDeg(): matrix {
1341
        return $this->map('rad2deg');
1342
    }
1343
1344
    public function degToRad(): matrix {
1345
        return $this->map('deg2rad');
1346
    }
1347
1348
    public function floor(): matrix {
1349
        return $this->map('floor');
1350
    }
1351
1352
    public function ceil(): matrix {
1353
        return $this->map('ceil');
1354
    }
1355
1356
    /**
1357
     * Compute the means of each row and return them in a vector.
1358
     *
1359
     * @return vector
1360
     */
1361
    public function mean(): vector {
1362
        return $this->sumRows()->divide($this->col);
1363
    }
1364
1365
    /**
1366
     * Compute the row variance of the matrix.
1367
     * 
1368
     * @param vector|null $mean
1369
     * @return vector
1370
     */
1371
    public function variance(vector|null $mean = null): vector {
1372
        if (isset($mean)) {
1373
            if (!$mean instanceof vector) {
1374
                self::_invalidArgument('mean must be a vector!');
1375
            }
1376
            if ($this->row !== $mean->col) {
1377
                self::_err('Err:: given mean vector dimensionality mismatched!');
1378
            }
1379
        } else {
1380
            $mean = $this->mean();
1381
        }
1382
        return $this->subtractColumnVector($mean)->square()
1383
                        ->sumRows()->divide($this->row);
1384
    }
1385
1386
    /**
1387
     *  Return the median vector of this matrix.
1388
     * @return vector
1389
     */
1390
    public function median(): vector {
1391
        $mid = intdiv($this->col, 2);
1392
        $odd = $this->col % 2 === 1;
1393
        $vr = vector::factory($this->row, $this->dtype);
1394
        for ($i = 0; $i < $this->row; ++$i) {
1395
            $a = $this->rowAsVector($i)->sort();
1396
            if ($odd) {
1397
                $median = $a->data[$mid];
1398
            } else {
1399
                $median = ($a->data[$mid - 1] + $a->data[$mid]) / 2.0;
1400
            }
1401
            $vr->data[$i] = $median;
1402
        }
1403
        unset($a);
1404
        return $vr;
1405
    }
1406
1407
    /**
1408
     * Compute the covariance matrix.
1409
     * 
1410
     * @param vector|null $mean
1411
     * @return matrix
1412
     */
1413
    public function covariance(vector|null $mean = null): matrix {
1414
        if (isset($mean)) {
1415
            if ($mean->col !== $this->row) {
1416
                self::_err('Err:: given mean vector dimensionality mismatched!');
1417
            }
1418
        } else {
1419
            $mean = $this->mean();
1420
        }
1421
1422
        $b = $this->subtractColumnVector($mean);
1 ignored issue
show
Bug introduced by
It seems like $mean can also be of type null; however, parameter $v of Np\matrix::subtractColumnVector() does only seem to accept Np\vector, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1422
        $b = $this->subtractColumnVector(/** @scrutinizer ignore-type */ $mean);
Loading history...
1423
1424
        return $b->dot($b->transpose())
1425
                        ->divideScalar($this->row);
1426
    }
1427
1428
    /**
1429
     * Clip the elements in the matrix to be between given minimum and maximum
1430
     * and return a new matrix.
1431
     * 
1432
     * @param float $min
1433
     * @param float $max
1434
     * @return matrix
1435
     */
1436
    public function clip(float $min, float $max): matrix {
1437
        $ar = self::factory($this->row, $this->col, $this->dtype);
1438
        for ($i = 0; $i < $this->ndim; ++$i) {
1439
            if ($this->data[$i] > $max) {
1440
                $ar->data[$i] = $max;
1441
                continue;
1442
            }
1443
            if ($this->data[$i] < $min) {
1444
                $ar->data[$i] = $min;
1445
                continue;
1446
            }
1447
            $ar->data[$i] = $this->data[$i];
1448
        }
1449
        return $ar;
1450
    }
1451
1452
    /**
1453
     * Clip the matrix to be lower bounded by a given minimum.
1454
     * @param float $min
1455
     * @return matrix
1456
     */
1457
    public function clipLower(float $min): matrix {
1458
        $ar = self::factory($this->row, $this->col, $this->dtype);
1459
        for ($i = 0; $i < $this->ndim; ++$i) {
1460
            if ($this->data[$i] < $min) {
1461
                $ar->data[$i] = $min;
1462
                continue;
1463
            }
1464
            $ar->data[$i] = $this->data[$i];
1465
        }
1466
        return $ar;
1467
    }
1468
1469
    /**
1470
     * Clip the matrix to be upper bounded by a given maximum.
1471
     *
1472
     * @param float $max
1473
     * @return matrix
1474
     */
1475
    public function clipUpper(float $max): matrix {
1476
        $ar = self::factory($this->row, $this->col, $this->dtype);
1477
        for ($i = 0; $i < $this->ndim; ++$i) {
1478
            if ($this->data[$i] > $max) {
1479
                $ar->data[$i] = $max;
1480
                continue;
1481
            }
1482
            $ar->data[$i] = $this->data[$i];
1483
        }
1484
        return $ar;
1485
    }
1486
1487
    /**
1488
     * Square of matrix
1489
     * @return matrix
1490
     */
1491
    public function square(): matrix {
1492
        return $this->multiplyMatrix($this);
1493
    }
1494
1495
    /**
1496
     * 
1497
     * @param int|float|matrix|vector $d
1498
     * @return matrix
1499
     */
1500
    public function equal(int|float|matrix|vector $d): matrix {
1501
        if ($d instanceof self) {
1502
            return $this->equalMatrix($d);
1503
        } elseif ($d instanceof vector) {
1504
            return $this->equalVector($d);
1505
        } else {
1506
            return $this->equalScalar($d);
1507
        }
1508
    }
1509
1510
    protected function equalMatrix(matrix $m): matrix {
1511
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
1512
            $ar = self::factory($this->row, $this->col, $this->dtype);
1513
            for ($i = 0; $i < $this->ndim; ++$i) {
1514
                $ar->data[$i] = $this->data[$i] == $m->data[$i] ? 1 : 0;
1515
            }
1516
            return $ar;
1517
        }
1518
    }
1519
1520
    protected function equalVector(vector $v): matrix {
1521
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
1522
            $ar = self::factory($this->row, $this->col, $this->dtype);
1523
            for ($i = 0; $i < $this->row; ++$i) {
1524
                for ($j = 0; $j < $this->col; ++$j) {
1525
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] == $v->data[$j] ? 1 : 0;
1526
                }
1527
            }
1528
            return $ar;
1529
        }
1530
    }
1531
1532
    protected function equalScalar(int|float $s): matrix {
1533
        $ar = self::factory($this->row, $this->col, $this->dtype);
1534
        for ($i = 0; $i < $this->ndim; ++$i) {
1535
            $ar->data[$i] = $this->data[$i] == $s ? 1 : 0;
1536
        }
1537
        return $ar;
1538
    }
1539
1540
    /**
1541
     * 
1542
     * @param int|float|matrix|vector $d
1543
     * @return matrix
1544
     */
1545
    public function greater(int|float|matrix|vector $d): matrix {
1546
        if ($d instanceof self) {
1547
            return $this->greaterMatrix($d);
1548
        } elseif ($d instanceof vector) {
1549
            return $this->greaterVector($d);
1550
        } else {
1551
            return $this->greaterScalar($d);
1552
        }
1553
    }
1554
1555
    protected function greaterMatrix(matrix $m): matrix {
1556
        if ($this->checkShape($this, $m) && $this->checkDtype($this,$m)) {
1557
            $ar = self::factory($this->row, $this->col, $this->dtype);
1558
            for ($i = 0; $i < $this->ndim; ++$i) {
1559
                $ar->data[$i] = $this->data[$i] > $m->data[$i] ? 1 : 0;
1560
            }
1561
            return $ar;
1562
        }
1563
    }
1564
1565
    protected function greaterVector(vector $v): matrix {
1566
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this,$v)) {
1567
            $ar = self::factory($this->row, $this->col, $this->dtype);
1568
            for ($i = 0; $i < $this->row; ++$i) {
1569
                for ($j = 0; $j < $this->col; ++$j) {
1570
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] > $v->data[$j] ? 1 : 0;
1571
                }
1572
            }
1573
            return $ar;
1574
        }
1575
    }
1576
1577
    protected function greaterScalar(int|float $s): matrix {
1578
        $ar = self::factory($this->row, $this->col, $this->dtype);
1579
        for ($i = 0; $i < $this->ndim; ++$i) {
1580
            $ar->data[$i] = $this->data[$i] > $s ? 1 : 0;
1581
        }
1582
        return $ar;
1583
    }
1584
1585
    /**
1586
     * 
1587
     * @param int|float|matrix $m
1588
     * @return matrix
1589
     */
1590
    public function less(int|float|matrix $m): matrix {
1591
        $ar = self::factory($this->row, $this->col, $this->dtype);
1592
        if ($m instanceof self) {
1593
            if ($this->checkShape($m)) {
0 ignored issues
show
Bug introduced by
The call to Np\core\nd::checkShape() has too few arguments starting with Obj2. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1593
            if ($this->/** @scrutinizer ignore-call */ checkShape($m)) {

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
1594
                for ($i = 0; $i < $this->ndim; ++$i) {
1595
                    $ar->data[$i] = $this->data[$i] < $m->data[$i] ? 1 : 0;
1596
                }
1597
                return $ar;
1598
            }
1599
        } else {
1600
            for ($i = 0; $i < $this->ndim; ++$i) {
1601
                $ar->data[$i] = $this->data[$i] < $m ? 1 : 0;
1602
            }
1603
            return $ar;
1604
        }
1605
    }
1606
1607
    /**
1608
     * Is the matrix symmetric i.e. is it equal to its own transpose?
1609
     *
1610
     * @return bool
1611
     */
1612
    public function isSymmetric(): bool {
1613
        if (!$this->isSquare()) {
1614
            return false;
1615
        }
1616
        $ar = $this->transpose();
1617
        for ($i = 0; $i < $ar->ndim; ++$i) {
1618
            if ($ar->data[$i] != $this->data[$i]) {
1619
                unset($ar);
1620
                return false;
1621
            }
1622
        }
1623
        unset($ar);
1624
        return true;
1625
    }
1626
1627
    /**
1628
     * print the matrix in consol
1629
     */
1630
    public function printMatrix() {
1631
        echo __CLASS__ . PHP_EOL;
1632
        for ($i = 0; $i < $this->row; ++$i) {
1633
            for ($j = 0; $j < $this->col; ++$j) {
1634
                printf('%lf  ', $this->data[$i * $this->col + $j]);
1635
            }
1636
            echo PHP_EOL;
1637
        }
1638
    }
1639
1640
    public function __toString() {
1641
        return (string) $this->printMatrix();
1642
    }
1643
1644
    protected function flattenArray(array $ar) {
1645
        if (is_array($ar) && is_array($ar[0])) {
1646
            $a = [];
1647
            foreach ($ar as $y => $value) {
1648
                foreach ($value as $k => $v) {
1649
                    $a[] = $v;
1650
                }
1651
            }
1652
            return $a;
1653
        }
1654
    }
1655
    
1656
    /**
1657
     * 
1658
     * @param int $row
1659
     * @param int $col
1660
     * @param int $dtype
1661
     * @return $this
1662
     */
1663
    protected function __construct(public int $row, public int $col, int $dtype = self::Float) {
1664
        if ($this->row < 1 || $this->col < 1) {
1665
            self::_invalidArgument('* To create Numphp/Matrix row & col must be greater than 0!, Op Failed! * ');
1666
        }
1667
        parent::__construct($this->row * $this->col, $dtype);
1668
        return $this;
1669
    }
1670
}
1671