Passed
Push — main ( 16ad7b...83c0b8 )
by Shubham
01:42
created

matrix::reshape()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 7
rs 10
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
     * @param int $row
164
     * @param int $col
165
     * @param float $lambda
166
     * @param int $dtype 
167
     * @return \Np\matrix
168
     */
169
    public static function poisson(int $row, int $col, float $lambda = 1.0, int $dtype = self::FLOAT): matrix {
170
        $ar = self::factory($row, $col, $dtype);
171
        $max = getrandmax();
172
        $l = exp(-$lambda);
173
        for ($i = 0; $i < $row; ++$i) {
174
            for ($j = 0; $j < $col; ++$j) {
175
                $k = 0;
176
                $p = 1.0;
177
                while ($p > $l) {
178
                    ++$k;
179
                    $p = $p * rand() / $max;
180
                }
181
                $ar->data[$i * $col + $j] = $k - 1;
182
            }
183
        }
184
        return $ar;
185
    }
186
187
    /**
188
     * Return a standard normally distributed random matrix i.e values
189
     * between -1 and 1.
190
     * @param int $row
191
     * @param int $col
192
     * @param int $dtype Description
193
     * @return \Np\matrix
194
     */
195
    public static function gaussian(int $row, int $col, int $dtype = self::FLOAT): matrix {
196
        $max = getrandmax();
197
        $a = $extras = [];
198
199
        while (count($a) < $row) {
200
            $rowA = [];
201
202
            if (!empty($extras)) {
203
                $rowA[] = array_pop($extras);
204
            }
205
206
            while (count($rowA) < $col) {
207
                $r = sqrt(-2.0 * log(rand() / $max));
208
209
                $phi = rand() / $max * self::TWO_PI;
210
211
                $rowA[] = $r * sin($phi);
212
                $rowA[] = $r * cos($phi);
213
            }
214
215
            if (count($rowA) > $col) {
216
                $extras[] = array_pop($rowA);
217
            }
218
219
            $a[] = $rowA;
220
        }
221
222
        return self::ar($a, $dtype);
223
    }
224
225
    /**
226
     * create an identity matrix with the given dimensions.
227
     * @param int $n
228
     * @param int $dtype
229
     * @return matrix
230
     * @throws \InvalidArgumentException
231
     */
232
    public static function identity(int $n, int $dtype = self::FLOAT): matrix {
233
        if ($n < 1) {
234
            self::_dimensionaMisMatchErr('dimensionality must be greater than 0 on all axes.');
235
        }
236
237
        $ar = self::factory($n, $n, $dtype);
238
        for ($i = 0; $i < $n; ++$i) {
239
            for ($j = 0; $j < $n; ++$j) {
240
                $ar->data[$i * $n + $j] = $i === $j ? 1 : 0;
241
            }
242
        }
243
        return $ar;
244
    }
245
246
    /**
247
     * Return the element-wise minimum of two matrices.
248
     * 
249
     * @param \Np\matrix $m
250
     * @return matrix
251
     */
252
    public function minimum(matrix $m): matrix {
253
        if ($this->checkShape($this,$m)) {
254
            $ar = self::factory($this->row, $this->col, $this->dtype);
255
            for ($i = 0; $i < $this->ndim; ++$i) {
256
                $ar->data[$i] = min($this->data[$i], $m->data[$i]);
257
            }
258
            return $ar;
259
        }
260
    }
261
262
    /**
263
     * Return the element-wise maximum of two matrices.
264
     * 
265
     * @param \Np\matrix $m
266
     * @return matrix
267
     */
268
    public function maximum(matrix $m): matrix {
269
        if ($this->checkShape($this, $m)) {
270
            $ar = self::factory($this->row, $this->col, $this->dtype);
271
            for ($i = 0; $i < $this->ndim; ++$i) {
272
                $ar->data[$i] = max($this->data[$i], $m->data[$i]);
273
            }
274
            return $ar;
275
        }
276
    }
277
278
    /**
279
     * 2D convolution between a matrix ma and kernel kb, with a given stride.
280
     * @param \Np\matrix $m
281
     * @param int $stride
282
     * @return matrix
283
     */
284
    public function convolve(matrix $m, int $stride = 1): matrix {
285
        return convolve::conv2D($this, $m, $stride);
286
    }
287
288
    /**
289
     * Calculate the determinant of the matrix.
290
     * @return float
291
     */
292
    public function det(): float {
293
        if (!$this->isSquare()) {
294
            self::_err('determinant is undefined for a non square matrix');
295
        }
296
        $lu = $this->lu();
297
        $nSwaps = $lu->p()->diagonalAsVector()->subtract($lu->p()->diagonalAsVector()->sum())->col - 1;
298
        $detP = (-1) ** $nSwaps;
299
        $detL = $lu->l()->diagonalAsVector()->product();
300
        $detU = $lu->u()->diagonalAsVector()->product();
301
        unset($lu);
302
        return ($detP * $detL * $detU);
303
    }
304
305
    /**
306
     * Return the trace of the matrix i.e the sum of all diagonal elements of a square matrix.
307
     * @return float
308
     */
309
    public function trace(): float {
310
        if (!$this->isSquare()) {
311
            self::_err('Error::matrix is not a squared can not Trace!');
312
        }
313
        $trace = 0.0;
314
        for ($i = 0; $i < $this->row; ++$i) {
315
            for ($j = 0; $j < $this->col; ++$j) {
316
                if ($i == $j) {
317
                    $trace += $this->data[$i * $this->col + $i];
318
                }
319
            }
320
        }
321
        return $trace;
322
    }
323
324
    /**
325
     * dignoalInterChange
326
     */
327
    public function dignoalInterChange() {
328
        for ($i = 0; $i < $this->row; ++$i) {
329
            for ($j = 0; $j < $this->col; ++$j) {
330
                $tmp = $this->data[$i * $this->col - $j];
331
                $this->data[$i * $this->col - $j] = $tmp;
332
            }
333
        }
334
    }
335
336
    //----------------Linear Algebra Opreations-------------------------------
337
338
    /**
339
     *  
340
     * get dot product of m.m or m.v
341
     * 
342
     * @param \Np\matrix|\Np\vector $d
343
     * @return matrix|vector
344
     */
345
    public function dot(matrix|vector $d): matrix|vector {
346
        if ($d instanceof self) {
347
            return $this->dotMatrix($d);
348
        } else {
349
            return $this->dotVector($d);
350
        }
351
    }
352
353
    /**
354
     * get matrix & matrix dot product
355
     * @param \Np\matrix $matrix
356
     * @return \Np\matrix
357
     */
358
    protected function dotMatrix(matrix $matrix): matrix {
359
        if ($this->checkDtype($this, $matrix) && $this->checkDimensions($this,$matrix)) {
360
            $ar = self::factory($this->row, $this->col, $this->dtype);
361
            blas::gemm($this, $matrix, $ar);
362
            return $ar;
363
        }
364
    }
365
366
    /**
367
     * get dot product of matrix & a vector
368
     * @param \Np\vector $vector
369
     * @return \Np\vector
370
     */
371
    protected function dotVector(vector $vector): vector {
372
        if ($this->checkDtype($this, $vector) && $this->checkDimensions($vector, $this)) {
373
            $mvr = vector::factory($this->col, $this->dtype);
374
            blas::gemv($this, $vector, $mvr);
375
            return $mvr;
376
        }
377
    }
378
379
    //---------------Arthmetic Opreations-----------------------------------
380
381
    /**
382
     * multiply this matrix with another matrix|scalar element-wise
383
     * Matrix Scalar\Matrix multiplication
384
     * @param int|float|matrix|vector $m
385
     * @return matrix|vector
386
     */
387
    public function multiply(int|float|matrix|vector $m): matrix|vector {
388
        if ($m instanceof self) {
389
            return $this->multiplyMatrix($m);
390
        } else if ($m instanceof vector) {
391
            return $this->multiplyVector($m);
392
        } else {
393
            return $this->scale($m);
394
        }
395
    }
396
397
    /**
398
     * 
399
     * @param \Np\vector $v
400
     * @return matrix
401
     */
402
    protected function multiplyVector(vector $v): matrix {
403
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
404
            $ar = matrix::factory($this->row, $this->col, $this->dtype);
405
            for ($i = 0; $i < $this->row; ++$i) {
406
                for ($j = 0; $j < $this->col; ++$j) {
407
                    $ar->data[$i * $this->col + $j] = $v->data[$j] * $this->data[$i * $this->col + $j];
408
                }
409
            }
410
            return $ar;
411
        }
412
    }
413
414
    /**
415
     * 
416
     * @param \Np\matrix $m
417
     * @return matrix
418
     */
419
    protected function multiplyMatrix(matrix $m): matrix {
420
        if ($this->checkDtype($this, $m) && $this->checkShape($this, $m)) {
421
            $ar = self::factory($this->row, $this->col, $this->dtype);
422
            for ($i = 0; $i < $this->row; ++$i) {
423
                for ($j = 0; $j < $this->col; ++$j) {
424
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] * $m->data[$i * $this->col + $j];
425
                }
426
            }
427
            return $ar;
428
        }
429
    }
430
431
    /**
432
     * Sum of Rows of matrix
433
     * @return vector
434
     */
435
    public function sumRows(): vector {
436
        $vr = vector::factory($this->row, $this->dtype);
437
        for ($i = 0; $i < $this->row; ++$i) {
438
            $sum = 0.0;
439
            for ($j = 0; $j < $this->col; ++$j) {
440
                $sum += $this->data[$i * $this->col + $j];
441
            }
442
            $vr->data[$i] = $sum;
443
        }
444
        return $vr;
445
    }
446
447
    /**
448
     * Sum of two matrix, vector or a scalar to current matrix
449
     * 
450
     * @param int|float|matrix|vector $m
451
     * @return matrix
452
     */
453
    public function sum(int|float|matrix|vector $m): matrix {
454
        if ($m instanceof self) {
455
            return $this->sumMatrix($m);
456
        } elseif ($m instanceof vector) {
457
            return $this->sumVector($m);
458
        } else {
459
            return $this->sumScalar($m);
460
        }
461
    }
462
463
    protected function sumScalar(int|float $s): matrix {
464
        $ar = self::factory($this->row, $this->col, $this->dtype);
465
        for ($i = 0; $i < $this->ndim; ++$i) {
466
            $ar->data[$i] = $this->data[$i] + $s;
467
        }
468
        return $ar;
469
    }
470
471
    protected function sumMatrix(matrix $m): matrix {
472
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
473
            $ar = self::factory($this->row, $this->col, $this->dtype);
474
            for ($i = 0; $i < $this->ndim; ++$i) {
475
                $ar->data[$i] = $this->data[$i] + $m->data[$i];
476
            }
477
            return $ar;
478
        }
479
    }
480
481
    protected function sumVector(vector $v): matrix {
482
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
483
            $ar = self::factory($this->row, $this->col, $this->dtype);
484
            for ($i = 0; $i < $this->row; ++$i) {
485
                for ($j = 0; $j < $this->col; ++$j) {
486
                    $ar->data[$i * $this->col + $j] = $v->data[$j] + $this->data[$i * $this->col + $j];
487
                }
488
            }
489
            return $ar;
490
        }
491
    }
492
493
    /**
494
     * subtract another matrix, vector or a scalar to this matrix
495
     * @param int|float|matrix|vector $d matrix|$scalar to subtract this matrix
496
     * @return \Np\matrix
497
     */
498
    public function subtract(int|float|matrix|vector $d): matrix {
499
        if ($d instanceof self) {
500
            return $this->subtractMatrix($d);
501
        } elseif ($d instanceof vector) {
502
            return $this->subtractVector($d);
503
        } else {
504
            return $this->subtractScalar($d);
505
        }
506
    }
507
508
    protected function subtractScalar(int|float $s): matrix {
509
        $ar = self::factory($this->row, $this->col, $this->dtype);
510
        for ($i = 0; $i < $this->ndim; ++$i) {
511
            $ar->data[$i] = $this->data[$i] - $s;
512
        }
513
        return $ar;
514
    }
515
516
    /**
517
     * 
518
     * @param matrix $m
519
     * @return matrix
520
     */
521
    protected function subtractMatrix(matrix $m): matrix {
522
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
523
            $ar = self::factory($this->row, $this->col, $this->dtype);
524
            for ($i = 0; $i < $this->ndim; ++$i) {
525
                $ar->data[$i] = $this->data[$i] - $m->data[$i];
526
            }
527
            return $ar;
528
        }
529
    }
530
531
    /**
532
     * 
533
     * @param vector $v
534
     * @return matrix
535
     */
536
    protected function subtractVector(vector $v): matrix {
537
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this,$v)) {
538
            $ar = self::factory($this->row, $this->col, $this->dtype);
539
            for ($i = 0; $i < $this->row; ++$i) {
540
                for ($j = 0; $j < $this->col; ++$j) {
541
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] - $v->data[$j];
542
                }
543
            }
544
            return $ar;
545
        }
546
    }
547
548
    /**
549
     * 
550
     * @param vector $v
551
     * @return matrix
552
     */
553
    public function subtractColumnVector(vector $v): matrix {
554
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
555
            $ar = self::factory($this->row, $this->col, $this->dtype);
556
            for ($j = 0; $j < $this->col; ++$j) {
557
                for ($i = 0; $i < $this->row; ++$i) {
558
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] - $v->data[$i];
559
                }
560
            }
561
            return $ar;
562
        }
563
    }
564
565
    /**
566
     * Return the division of two elements, element-wise.
567
     * @param int|float|matrix $d
568
     * @return matrix
569
     */
570
    public function divide(int|float|matrix|vector $d): matrix {
571
        if ($d instanceof self) {
572
            return $this->divideMatrix($d);
573
        } elseif ($d instanceof vector) {
574
            return $this->divideVector($d);
575
        } else {
576
            return $this->divideScalar($d);
577
        }
578
    }
579
580
    protected function divideMatrix(matrix $m): matrix {
581
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
582
            $ar = self::factory($this->row, $this->col, $this->dtype);
583
            for ($i = 0; $i < $this->ndim; ++$i) {
584
                $ar->data[$i] = $this->data[$i] / $m->data[$i];
585
            }
586
            return $ar;
587
        }
588
    }
589
590
    protected function divideVector(vector $v): matrix {
591
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
592
            $ar = self::factory($this->row, $this->col, $this->dtype);
593
            for ($i = 0; $i < $this->row; ++$i) {
594
                for ($j = 0; $j < $this->col; ++$j) {
595
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] / $v->data[$j];
596
                }
597
            }
598
            return $ar;
599
        }
600
    }
601
602
    protected function divideScalar(int|float $s): matrix {
603
        $ar = self::factory($this->row, $this->col, $this->dtype);
604
        for ($i = 0; $i < $this->ndim; ++$i) {
605
            $ar->data[$i] = $this->data[$i] / $s;
606
        }
607
        return $ar;
608
    }
609
610
    /**
611
     * 
612
     * Raise this matrix to the power of the element-wise entry in another matrix.
613
     * 
614
     * @param int|float|matrix $m
615
     * @return matrix
616
     */
617
    public function pow(int|float|matrix|vector $d): matrix {
618
        if ($d instanceof self) {
619
            return $this->powMatrix($d);
620
        } else if ($d instanceof vector) {
621
            return $this->powVector($d);
622
        } else {
623
            return $this->powScalar($d);
624
        }
625
    }
626
627
    protected function powMatrix(matrix $m): matrix {
628
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
629
            $ar = self::factory($this->row, $this->col, $this->dtype);
630
            for ($i = 0; $i < $this->ndim; ++$i) {
631
                $ar->data[$i] = $this->data[$i] ** $m->data[$i];
632
            }
633
            return $ar;
634
        }
635
    }
636
637
    protected function powVector(vector $v): matrix {
638
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
639
            $ar = self::factory($this->row, $this->col, $this->dtype);
640
            for ($i = 0; $i < $this->row; ++$i) {
641
                for ($j = 0; $j < $this->col; ++$j) {
642
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] ** $v->data[$j];
643
                }
644
            }
645
            return $ar;
646
        }
647
    }
648
649
    protected function powScalar(int|float $s): matrix {
650
        $ar = $this->copyMatrix();
651
        for ($i = 0; $i < $this->ndim; ++$i) {
652
            $ar->data[$i] **= $s;
653
        }
654
        return $ar;
655
    }
656
657
    /**
658
     * Calculate the modulus i.e remainder of division between this matrix and another matrix.
659
     * @param int|float|matrix|vector $d
660
     * @return matrix
661
     */
662
    public function mod(int|float|matrix|vector $d): matrix {
663
        if ($d instanceof self) {
664
            $this->modMatrix($d);
665
        } else if ($d instanceof vector) {
666
            $this->modVector($d);
667
        } else {
668
            $this->modScalar($d);
669
        }
670
    }
671
672
    protected function modMatrix(matrix $m): matrix {
673
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
674
            $ar = self::factory($this->row, $this->col, $this->dtype);
675
            for ($i = 0; $i < $this->ndim; ++$i) {
676
                $ar->data[$i] = $this->data[$i] % $m->data[$i];
677
            }
678
            return $ar;
679
        } 
680
    }
681
682
    protected function modVector(vector $v): matrix {
683
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
684
            $ar = self::factory($this->row, $this->col, $this->dtype);
685
            for ($i = 0; $i < $this->row; ++$i) {
686
                for ($j = 0; $j < $this->col; ++$j) {
687
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] % $v->data[$j];
688
                }
689
            }
690
            return $ar;
691
        }
692
    }
693
694
    protected function modScalar(int|float $s): matrix {
695
        $ar = $this->copyMatrix();
696
        for ($i = 0; $i < $this->ndim; ++$i) {
697
            $ar->data[$i] %= $s;
698
        }
699
        return $ar;
700
    }
701
702
    /**
703
     * Return the element-wise reciprocal of the matrix.
704
     * 
705
     * @return matrix
706
     */
707
    public function reciprocal(): matrix {
708
        return self::ones($this->row, $this->col, $this->dtype)->divideMatrix($this);
709
    }
710
711
    /**
712
     * 
713
     * @param int|float $d
714
     * @return bool
715
     */
716
    public static function is_zero($d): bool {
717
        if (abs($d) < self::EPSILON) {
718
            return true;
719
        }
720
        return false;
721
    }
722
723
    /**
724
     *  is row zero
725
     * @param int $row
726
     * @return bool
727
     */
728
    public function is_rowZero(int $row): bool {
729
        for ($i = 0; $i < $this->col; ++$i) {
730
            if ($this->data[$row * $this->col + $i] != 0) {
731
                return false;
732
            }
733
        }
734
        return true;
735
    }
736
737
    /**
738
     * 
739
     * @return bool
740
     */
741
    public function has_ZeroRow(): bool {
742
        for ($i = 0; $i < $this->row; ++$i) {
743
            if ($this->is_rowZero($i)) {
744
                return true;
745
            }
746
        }
747
        return false;
748
    }
749
750
    /**
751
     * Transpose the matrix i.e row become cols and cols become rows.
752
     * @return \Np\matrix
753
     */
754
    public function transpose(): matrix {
755
        $ar = self::factory($this->col, $this->row, $this->dtype);
756
        for ($i = 0; $i < $ar->row; ++$i) {
757
            for ($j = 0; $j < $ar->col; ++$j) {
758
                $ar->data[$i * $ar->col + $j] = $this->data[$j * $ar->col + $i];
759
            }
760
        }
761
        return $ar;
762
    }
763
764
    /**
765
     * swap specific values in matrix
766
     * @param int $i1
767
     * @param int $i2
768
     */
769
    public function swapValue(int $i1, int $i2) {
770
        $tmp = $this->data[$i1];
771
        $this->data[$i1] = $this->data[$i2];
772
        $this->data[$i2] = $tmp;
773
    }
774
775
    /**
776
     * swap specific rows in matrix
777
     * @param int $r1
778
     * @param int $r2
779
     */
780
    public function swapRows(int $r1, int $r2) {
781
        for ($i = 0; $i < $this->col; ++$i) {
782
            $tmp = $this->data[$r1 * $this->col + $i];
783
            $this->data[$r1 * $this->col + $i] = $this->data[$r2 * $this->col + $i];
784
            $this->data[$r2 * $this->col + $i] = $tmp;
785
        }
786
    }
787
788
    /**
789
     * swap specific cols in matrix
790
     * @param int $c1
791
     * @param int $c2
792
     */
793
    public function swapCols(int $c1, int $c2) {
794
        for ($i = 0; $i < $this->row; ++$i) {
795
            $tmp = $this->data[$i * $this->row + $c1];
796
            $this->data[$i * $this->row + $c1] = $this->data[$i * $this->row + $c2];
797
            $this->data[$i * $this->row + $c2] = $tmp;
798
        }
799
    }
800
    
801
    /**
802
     * 
803
     * @param int|float $scalar
804
     * @return matrix
805
     */
806
    public function scale(int|float $scalar): matrix {
807
        if ($scalar == 0) {
808
            return self::zeros($this->row, $this->col, $this->dtype);
809
        }
810
811
        $ar = $this->copyMatrix();
812
        for ($i = 0; $i < $this->ndim; ++$i) {
813
            $ar->data[$i] *= $scalar;
814
        }
815
816
        return $ar;
817
    }
818
    
819
    /**
820
     * scale all the elements of a row 
821
     * @param int $row
822
     * @param int|float $c
823
     */
824
    public function scaleRow(int $row, int|float $c) {
825
        for ($i = 0; $i < $this->col; ++$i) {
826
            $this->data[$row * $this->col + $i] *= $c;
827
        }
828
    }
829
    
830
    /**
831
     * scale all the elements of 
832
     * @param int $col
833
     * @param int|float $c
834
     */
835
    public function scaleCol(int $col, int|float $c) {
836
        for ($i = 0; $i < $this->row; ++$i) {
837
            $this->data[$i * $this->col + $col] *= $c;
838
        }
839
    }
840
    
841
    /**
842
     * Scale digonally 
843
     * @param int|float $c
844
     * @param bool $lDig
845
     */
846
    public function scaleDigonalCol(int|float $c, bool $lDig = true) {
847
        if($lDig){
848
            for ($i = 0; $i < $this->row ; ++$i) {
849
                $this->data[$i * $this->col + $i] *= $c;
850
            }
851
        }
852
        else{
853
            for ($i = $this->row; $i > 0 ; --$i) {
854
                $this->data[$i * $this->col - $i] *= $c;
855
            }
856
        }
857
    }
858
859
    /**
860
     * 
861
     * @param int $r1
862
     * @param int $r2
863
     * @param float $c
864
     */
865
    public function addScaleRow(int $r1, int $r2, float $c) {
866
        for ($i = 0; $i < $this->col; ++$i) {
867
            $this->data[$r2 * $this->col + $i] += $this->data[$r1 * $this->col + $i] * $c;
868
        }
869
    }
870
871
    /**
872
     * Attach given matrix to the left of this matrix.
873
     * 
874
     * @param \Np\matrix $m
875
     * @return \Np\matrix
876
     */
877
    public function joinLeft(matrix $m): matrix {
878
        if ($this->row != $m->row && !$this->checkDtype($this, $m)) {
879
            self::_err('Error::Invalid size! or DataType!');
880
        }
881
        $col = $this->col + $m->col;
882
        $ar = self::factory($this->row, $col, $this->dtype);
883
        for ($i = 0; $i < $this->row; ++$i) {
884
            for ($j = 0; $j < $this->col; ++$j) {
885
                $ar->data[$i * $col + $j] = $this->data[$i * $this->col + $j];
886
            }
887
            for ($j = 0; $j < $m->col; ++$j) {
888
                $ar->data[$i * $col + ($this->col + $j)] = $m->data[$i * $m->col + $j];
889
            }
890
        }
891
        return $ar;
892
    }
893
894
    /**
895
     * Join matrix m to the Right of this matrix.
896
     * @param \Np\matrix $m
897
     * @return matrix
898
     */
899
    public function joinRight(matrix $m): matrix {
900
        if ($this->row != $m->row && !$this->checkDtype($this,$m)) {
901
            self::_err('Error::Invalid size! or DataType!');
902
        }
903
        $col = $this->col + $m->col;
904
        $ar = self::factory($this->row, $col, $this->dtype);
905
        for ($i = 0; $i < $m->row; ++$i) {
906
            for ($j = 0; $j < $m->col; ++$j) {
907
                $ar->data[$i * $col + $j] = $m->data[$i * $m->col + $j];
908
            }
909
            for ($j = 0; $j < $this->col; ++$j) {
910
                $ar->data[$i * $col + ($this->col + $j)] = $this->data[$i * $this->col + $j];
911
            }
912
        }
913
        return $ar;
914
    }
915
916
    /**
917
     * Join matrix m Above this matrix.
918
     * @param \Np\matrix $m
919
     * @return matrix
920
     */
921
    public function joinAbove(matrix $m): matrix {
922
        if ($this->col !== $m->col && !$this->checkDtype($this, $m)) {
923
            self::_err('Error::Invalid size! or DataType!');
924
        }
925
        $row = $this->row + $m->row;
926
        $ar = self::factory($row, $this->col, $this->dtype);
927
        for ($i = 0; $i < $m->row; ++$i) {
928
            for ($j = 0; $j < $m->col; ++$j) {
929
                $ar->data[$i * $m->col + $j] = $m->data[$i * $m->col + $j];
930
            }
931
            for ($j = 0; $j < $this->col; ++$j) {
932
                $ar->data[($i + $this->row) * $this->col + $j] = $this->data[$i * $this->col + $j];
933
            }
934
        }
935
        return $ar;
936
    }
937
938
    /**
939
     * Join matrix m below this matrix.
940
     * @param \Np\matrix $m
941
     * @return matrix
942
     */
943
    public function joinBelow(matrix $m): matrix {
944
        if ($this->col !== $m->col && !$this->checkDtype($this, $m)) {
945
            self::_err('Error::Invalid size! or DataType!');
946
        }
947
        $row = $this->row + $m->row;
948
        $ar = self::factory($row, $this->col, $this->dtype);
949
        for ($i = 0; $i < $this->row; ++$i) {
950
            for ($j = 0; $j < $this->col; ++$j) {
951
                $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j];
952
            }
953
            for ($j = 0; $j < $m->col; ++$j) {
954
                $ar->data[($i + $m->row) * $m->col + $j] = $m->data[$i * $m->col + $j];
955
            }
956
        }
957
        return $ar;
958
    }
959
960
    /**
961
     * Calculate the row echelon form of the matrix. 
962
     * Return the reduced matrix.
963
     *
964
     * @return matrix|null
965
     */
966
    public function ref(): matrix|null {
967
        return ref::factory($this);
968
    }
969
970
    /**
971
     * Return the lower triangular matrix of the Cholesky decomposition.
972
     *
973
     * @return matrix|null
974
     */
975
    public function cholesky(): matrix|null {
976
        return cholesky::factory($this);
977
    }
978
979
    /**
980
     * FIXME--------------
981
     * RREF
982
     * The reduced row echelon form (RREF) of a matrix.
983
     * @return \Np\matrix
984
     */
985
    public function rref(): matrix {
986
        return rref::factory($this);
987
    }
988
989
    /**
990
     * make copy of the matrix
991
     * @return \Np\matrix
992
     */
993
    public function copyMatrix(): matrix {
994
        return clone $this;
995
    }
996
997
    /**
998
     * 
999
     * @param int $cols
1000
     * @return \Np\matrix
1001
     */
1002
    public function diminish_left(int $cols): matrix {
1003
        $ar = self::factory($this->row, $cols, $this->dtype);
1004
        for ($i = 0; $i < $ar->row; ++$i) {
1005
            for ($j = 0; $j < $ar->col; ++$j) {
1006
                $ar->data[$i * $ar->col + $j] = $this->data[$i * $this->col + $j];
1007
            }
1008
        }
1009
        return $ar;
1010
    }
1011
1012
    /**
1013
     * 
1014
     * @param int $cols
1015
     * @return \Np\matrix
1016
     */
1017
    public function diminish_right(int $cols): matrix {
1018
        $ar = self::factory($this->row, $cols, $this->dtype);
1019
        for ($i = 0; $i < $ar->row; ++$i) {
1020
            for ($j = 0; $j < $ar->col; ++$j) {
1021
                $ar->data[$i * $ar->col + $j] = $this->data[$i * $this->col - $cols + $j];
1022
            }
1023
        }
1024
        return $ar;
1025
    }
1026
1027
    /**
1028
     * Return the index of the maximum element in every row of the matrix.
1029
     * @return \Np\vector int
1030
     */
1031
    public function argMax(): vector {
1032
        $v = vector::factory($this->row, vector::INT);
1033
        for ($i = 0; $i < $this->row; ++$i) {
1034
            $v->data[$i] = blas::max($this->rowAsVector($i));
1035
        }
1036
        return $v;
1037
    }
1038
1039
    /**
1040
     * Return the index of the minimum element in every row of the matrix.
1041
     * @return \Np\vector int
1042
     */
1043
    public function argMin(): vector {
1044
        $v = vector::factory($this->row, vector::INT);
1045
        for ($i = 0; $i < $this->row; ++$i) {
1046
            $v->data[$i] = blas::min($this->rowAsVector($i));
1047
        }
1048
1049
        return $v;
1050
    }
1051
1052
    /**
1053
     * Set given data in matrix
1054
     * @param int|float|array $data
1055
     * @param bool $dignoal
1056
     * @return void
1057
     */
1058
    public function setData(int|float|array $data, bool $dignoal = false): void {
1059
        if ($dignoal == false) {
1060
            if (is_array($data) && is_array($data[0])) {
1061
                $f = $this->flattenArray($data);
1062
                foreach ($f as $k => $v) {
1063
                    $this->data[$k] = $v;
1064
                }
1065
            } elseif (is_numeric($data)) {
1066
                for ($i = 0; $i < $this->ndim; ++$i) {
1067
                    $this->data[$i] = $data;
1068
                }
1069
            }
1070
        } elseif (is_numeric($data) || is_array($data) && !is_array($data[0])) {
1071
            for ($i = 0; $i < $this->row; ++$i) {
1072
                $this->data[$i * $this->col * $i] = $data;
1073
            }
1074
        }
1075
    }
1076
    
1077
    /**
1078
     * get the matrix data type
1079
     * @return type
1080
     */
1081
    public function getDtype() {
1082
        return $this->dtype;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->dtype returns the type integer which is incompatible with the documented return type Np\type.
Loading history...
1083
    }
1084
1085
    /**
1086
     * get the shape of matrix
1087
     * @return object
1088
     */
1089
    public function getShape(): object {
1090
        return (object) ['m' => $this->row, 'n' => $this->col];
1091
    }
1092
1093
    /**
1094
     * get the number of elements in the matrix.
1095
     * @return int
1096
     */
1097
    public function getSize(): int {
1098
        return $this->ndim;
1099
    }
1100
1101
    /**
1102
     * is matrix squred
1103
     * @return bool
1104
     */
1105
    public function isSquare(): bool {
1106
        if ($this->row === $this->col) {
1107
            return true;
1108
        }
1109
        return false;
1110
    }
1111
1112
    /**
1113
     * Return a row as vector from the matrix.
1114
     * @param int $index
1115
     * @return \Np\vector
1116
     */
1117
    public function rowAsVector(int $index): vector {
1118
        $vr = vector::factory($this->col, $this->dtype);
1119
        for ($j = 0; $j < $this->col; ++$j) {
1120
            $vr->data[$j] = $this->data[$index * $this->col + $j];
1121
        }
1122
        return $vr;
1123
    }
1124
1125
    /**
1126
     * Return a col as vector from the matrix.
1127
     * @param int $index
1128
     * @return \Np\vector
1129
     */
1130
    public function colAsVector(int $index): vector {
1131
        $vr = vector::factory($this->row, $this->dtype);
1132
        for ($i = 0; $i < $this->row; ++$i) {
1133
            $vr->data[$i] = $this->data[$i * $this->row + $index];
1134
        }
1135
        return $vr;
1136
    }
1137
1138
    /**
1139
     * Return the diagonal elements of a square matrix as a vector.
1140
     * @return \Np\vector
1141
     */
1142
    public function diagonalAsVector(): vector {
1143
        if (!$this->isSquare()) {
1144
            self::_err('Can not trace of a none square matrix');
1145
        }
1146
        $vr = vector::factory($this->row, $this->dtype);
1147
        for ($i = 0; $i < $this->row; ++$i) {
1148
            $vr->data[$i] = $this->getDiagonalVal($i);
1149
        }
1150
        return $vr;
1151
    }
1152
1153
    /**
1154
     * Flatten i.e unravel the matrix into a vector.
1155
     *
1156
     * @return \Np\vector
1157
     */
1158
    public function asVector(): vector {
1159
        $vr = vector::factory($this->ndim, $this->dtype);
1160
        for ($i = 0; $i < $this->ndim; ++$i) {
1161
            $vr->data[$i] = $this->data[$i];
1162
        }
1163
        return $vr;
1164
    }
1165
1166
    /**
1167
     * Return the elements of the matrix in a 2-d array.
1168
     * @return array
1169
     */
1170
    public function asArray(): array {
1171
        $ar = array_fill(0, $this->row, array_fill(0, $this->col, null));
1172
        for ($i = 0; $i < $this->row; ++$i) {
1173
            for ($j = 0; $j < $this->col; ++$j) {
1174
                $ar[$i][$j] = $this->data[$i * $this->col + $j];
1175
            }
1176
        }
1177
        return $ar;
1178
    }
1179
1180
    /**
1181
     * get a diagonal value from matrix
1182
     * @param int $i
1183
     * @return float
1184
     */
1185
    public function getDiagonalVal(int $i) {
1186
        if ($this->isSquare()) {
1187
            return $this->data[$i * $this->row + $i];
1188
        }
1189
    }
1190
1191
    /**
1192
     *
1193
     * Compute the multiplicative inverse of the matrix.
1194
     * @return matrix
1195
     */
1196
    public function inverse(): matrix {
1197
        if (!$this->isSquare()) {
1198
            self::_err('Error::invalid Size of matrix!');
1199
        }
1200
        $imat = $this->copyMatrix();
1201
        $ipiv = vector::factory($this->row, vector::INT);
1202
        $lp = lapack::getrf($imat, $ipiv);
1203
        if ($lp != 0) {
1204
            return null;
1205
        }
1206
        $lp = lapack::getri($imat, $ipiv);
1207
        if ($lp != 0) {
1208
            return null;
1209
        }
1210
        unset($ipiv);
1211
        unset($lp);
1212
        return $imat;
1213
    }
1214
    
1215
    /**
1216
     * Compute the (Moore-Penrose) pseudo inverse of the general matrix.
1217
     * @return matrix|null
1218
     */
1219
    public function pseudoInverse(): matrix|null {
1220
        $k = min($this->row, $this->col);
1221
        $s = vector::factory($k, $this->dtype);
1222
        $u = self::factory($this->row, $this->row, $this->dtype);
1223
        $vt = self::factory($this->col, $this->col, $this->dtype);
1224
        $imat = $this->copyMatrix();
1225
        $lp = lapack::gesdd($imat, $s, $u, $vt);
1226
        if ($lp != 0) {
1227
            return null;
1228
        }
1229
        for ($i = 0; $i < $k; ++$i) {
1230
            blas::scale(1.0 / $s->data[$i], $vt->rowAsVector($i));
1231
        }
1232
        unset($imat);
1233
        unset($k);
1234
        unset($lp);
1235
        unset($s);
1236
        $mr = self::factory($this->col, $this->row, $this->dtype);
1237
        blas::gemm($vt, $u, $mr);
1238
        unset($u);
1239
        unset($vt);
1240
        return $mr;
1241
    }
1242
1243
    /**
1244
     * Compute the singular value decomposition of a matrix and 
1245
     * return an object of the singular values and unitary matrices
1246
     *
1247
     * @return object (u,s,v)
1248
     */
1249
    public function svd(): svd {
1250
        return svd::factory($this);
1251
    }
1252
1253
    /**
1254
     * Compute the eigen decomposition of a general matrix.
1255
     * return the eigenvalues and eigenvectors as object
1256
     * 
1257
     * @param bool $symmetric
1258
     * @return eigen
1259
     */
1260
    public function eign(bool $symmetric = false): eigen {
1261
        return eigen::factory($this, $symmetric);
1262
    }
1263
1264
    /**
1265
     *  
1266
     * Compute the LU factorization of matrix.
1267
     * return lower, upper, and permutation matrices as object.
1268
     * 
1269
     * @return lu
1270
     */
1271
    public function lu(): lu {
1272
        return lu::factory($this);
1273
    }
1274
1275
    /**
1276
     * Return the L1 norm of the matrix.
1277
     * @return float
1278
     */
1279
    public function normL1(): float {
1280
        return lapack::lange('l', $this);
1281
    }
1282
1283
    /**
1284
     * Return the L2 norm of the matrix.
1285
     * @return float
1286
     */
1287
    public function normL2(): float {
1288
        return lapack::lange('f', $this);
1289
    }
1290
1291
    /**
1292
     * Return the L1 norm of the matrix.
1293
     * @return float
1294
     */
1295
    public function normINF(): float {
1296
        return lapack::lange('i', $this);
1297
    }
1298
1299
    /**
1300
     * Return the Frobenius norm of the matrix.
1301
     * @return float
1302
     */
1303
    public function normFrob(): float {
1304
        return $this->normL2();
1305
    }
1306
1307
    /**
1308
     * Run a function over all of the elements in the matrix. 
1309
     * @param callable $func
1310
     * @return \Np\matrix
1311
     */
1312
    public function map(callable $func): matrix {
1313
        $ar = self::factory($this->row, $this->col, $this->dtype);
1314
        for ($i = 0; $i < $this->ndim; ++$i) {
1315
            $ar->data[$i] = $func($this->data[$i]);
1316
        }
1317
        return $ar;
1318
    }
1319
1320
    public function abs(): matrix {
1321
        return $this->map('abs');
1322
    }
1323
1324
    public function sqrt(): matrix {
1325
        return $this->map('sqrt');
1326
    }
1327
1328
    public function exp(): matrix {
1329
        return $this->map('exp');
1330
    }
1331
1332
    public function exp1(): matrix {
1333
        return $this->map('exp1');
1334
    }
1335
1336
    public function log(float $b = M_E): matrix {
1337
        $ar = $this->copyMatrix();
1338
        for ($i = 0; $i < $ar->ndim; ++$i) {
1339
            log($ar->data[$i], $b);
1340
        }
1341
        return $ar;
1342
    }
1343
1344
    public function log1p(): matrix {
1345
        return $this->map('log1p');
1346
    }
1347
1348
    public function sin(): matrix {
1349
        return $this->map('sin');
1350
    }
1351
1352
    public function asin(): matrix {
1353
        return $this->map('asin');
1354
    }
1355
1356
    public function cos(): matrix {
1357
        return $this->map('cos');
1358
    }
1359
1360
    public function acos(): matrix {
1361
        return $this->map('acos');
1362
    }
1363
1364
    public function tan(): matrix {
1365
        return $this->map('tan');
1366
    }
1367
1368
    public function atan(): matrix {
1369
        return $this->map('atan');
1370
    }
1371
1372
    public function radToDeg(): matrix {
1373
        return $this->map('rad2deg');
1374
    }
1375
1376
    public function degToRad(): matrix {
1377
        return $this->map('deg2rad');
1378
    }
1379
1380
    public function floor(): matrix {
1381
        return $this->map('floor');
1382
    }
1383
1384
    public function ceil(): matrix {
1385
        return $this->map('ceil');
1386
    }
1387
1388
    /**
1389
     * Compute the means of each row and return them in a vector.
1390
     *
1391
     * @return vector
1392
     */
1393
    public function mean(): vector {
1394
        return $this->sumRows()->divide($this->col);
1395
    }
1396
1397
    /**
1398
     * Compute the row variance of the matrix.
1399
     * 
1400
     * @param vector|null $mean
1401
     * @return vector
1402
     */
1403
    public function variance(vector|null $mean = null): vector {
1404
        if (isset($mean)) {
1405
            if (!$mean instanceof vector) {
1406
                self::_invalidArgument('mean must be a vector!');
1407
            }
1408
            if ($this->row !== $mean->col) {
1409
                self::_err('Err:: given mean vector dimensionality mismatched!');
1410
            }
1411
        } else {
1412
            $mean = $this->mean();
1413
        }
1414
        return $this->subtractColumnVector($mean)->square()
1415
                        ->sumRows()->divide($this->row);
1416
    }
1417
1418
    /**
1419
     *  Return the median vector of this matrix.
1420
     * @return vector
1421
     */
1422
    public function median(): vector {
1423
        $mid = intdiv($this->col, 2);
1424
        $odd = $this->col % 2 === 1;
1425
        $vr = vector::factory($this->row, $this->dtype);
1426
        for ($i = 0; $i < $this->row; ++$i) {
1427
            $a = $this->rowAsVector($i)->sort();
1428
            if ($odd) {
1429
                $median = $a->data[$mid];
1430
            } else {
1431
                $median = ($a->data[$mid - 1] + $a->data[$mid]) / 2.0;
1432
            }
1433
            $vr->data[$i] = $median;
1434
        }
1435
        unset($a);
1436
        return $vr;
1437
    }
1438
1439
    /**
1440
     * Compute the covariance matrix.
1441
     * 
1442
     * @param vector|null $mean
1443
     * @return matrix
1444
     */
1445
    public function covariance(vector|null $mean = null): matrix {
1446
        if (isset($mean)) {
1447
            if ($mean->col !== $this->row) {
1448
                self::_err('Err:: given mean vector dimensionality mismatched!');
1449
            }
1450
        } else {
1451
            $mean = $this->mean();
1452
        }
1453
1454
        $b = $this->subtractColumnVector($mean);
1455
1456
        return $b->dot($b->transpose())
1457
                        ->divideScalar($this->row);
1458
    }
1459
1460
    /**
1461
     * Clip the elements in the matrix to be between given minimum and maximum
1462
     * and return a new matrix.
1463
     * 
1464
     * @param float $min
1465
     * @param float $max
1466
     * @return matrix
1467
     */
1468
    public function clip(float $min, float $max): matrix {
1469
        $ar = self::factory($this->row, $this->col, $this->dtype);
1470
        for ($i = 0; $i < $this->ndim; ++$i) {
1471
            if ($this->data[$i] > $max) {
1472
                $ar->data[$i] = $max;
1473
                continue;
1474
            }
1475
            if ($this->data[$i] < $min) {
1476
                $ar->data[$i] = $min;
1477
                continue;
1478
            }
1479
            $ar->data[$i] = $this->data[$i];
1480
        }
1481
        return $ar;
1482
    }
1483
1484
    /**
1485
     * Clip the matrix to be lower bounded by a given minimum.
1486
     * @param float $min
1487
     * @return matrix
1488
     */
1489
    public function clipLower(float $min): matrix {
1490
        $ar = self::factory($this->row, $this->col, $this->dtype);
1491
        for ($i = 0; $i < $this->ndim; ++$i) {
1492
            if ($this->data[$i] < $min) {
1493
                $ar->data[$i] = $min;
1494
                continue;
1495
            }
1496
            $ar->data[$i] = $this->data[$i];
1497
        }
1498
        return $ar;
1499
    }
1500
1501
    /**
1502
     * Clip the matrix to be upper bounded by a given maximum.
1503
     *
1504
     * @param float $max
1505
     * @return matrix
1506
     */
1507
    public function clipUpper(float $max): matrix {
1508
        $ar = self::factory($this->row, $this->col, $this->dtype);
1509
        for ($i = 0; $i < $this->ndim; ++$i) {
1510
            if ($this->data[$i] > $max) {
1511
                $ar->data[$i] = $max;
1512
                continue;
1513
            }
1514
            $ar->data[$i] = $this->data[$i];
1515
        }
1516
        return $ar;
1517
    }
1518
1519
    /**
1520
     * Square of matrix
1521
     * @return matrix
1522
     */
1523
    public function square(): matrix {
1524
        return $this->multiplyMatrix($this);
1525
    }
1526
1527
    /**
1528
     * 
1529
     * @param int|float|matrix|vector $d
1530
     * @return matrix
1531
     */
1532
    public function equal(int|float|matrix|vector $d): matrix {
1533
        if ($d instanceof self) {
1534
            return $this->equalMatrix($d);
1535
        } elseif ($d instanceof vector) {
1536
            return $this->equalVector($d);
1537
        } else {
1538
            return $this->equalScalar($d);
1539
        }
1540
    }
1541
1542
    protected function equalMatrix(matrix $m): matrix {
1543
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
1544
            $ar = self::factory($this->row, $this->col, $this->dtype);
1545
            for ($i = 0; $i < $this->ndim; ++$i) {
1546
                $ar->data[$i] = $this->data[$i] == $m->data[$i] ? 1 : 0;
1547
            }
1548
            return $ar;
1549
        }
1550
    }
1551
1552
    protected function equalVector(vector $v): matrix {
1553
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this, $v)) {
1554
            $ar = self::factory($this->row, $this->col, $this->dtype);
1555
            for ($i = 0; $i < $this->row; ++$i) {
1556
                for ($j = 0; $j < $this->col; ++$j) {
1557
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] == $v->data[$j] ? 1 : 0;
1558
                }
1559
            }
1560
            return $ar;
1561
        }
1562
    }
1563
1564
    protected function equalScalar(int|float $s): matrix {
1565
        $ar = self::factory($this->row, $this->col, $this->dtype);
1566
        for ($i = 0; $i < $this->ndim; ++$i) {
1567
            $ar->data[$i] = $this->data[$i] == $s ? 1 : 0;
1568
        }
1569
        return $ar;
1570
    }
1571
1572
    /**
1573
     * 
1574
     * @param int|float|matrix|vector $d
1575
     * @return matrix
1576
     */
1577
    public function greater(int|float|matrix|vector $d): matrix {
1578
        if ($d instanceof self) {
1579
            return $this->greaterMatrix($d);
1580
        } elseif ($d instanceof vector) {
1581
            return $this->greaterVector($d);
1582
        } else {
1583
            return $this->greaterScalar($d);
1584
        }
1585
    }
1586
1587
    protected function greaterMatrix(matrix $m): matrix {
1588
        if ($this->checkShape($this, $m) && $this->checkDtype($this,$m)) {
1589
            $ar = self::factory($this->row, $this->col, $this->dtype);
1590
            for ($i = 0; $i < $this->ndim; ++$i) {
1591
                $ar->data[$i] = $this->data[$i] > $m->data[$i] ? 1 : 0;
1592
            }
1593
            return $ar;
1594
        }
1595
    }
1596
1597
    protected function greaterVector(vector $v): matrix {
1598
        if ($this->checkDimensions($v, $this) && $this->checkDtype($this,$v)) {
1599
            $ar = self::factory($this->row, $this->col, $this->dtype);
1600
            for ($i = 0; $i < $this->row; ++$i) {
1601
                for ($j = 0; $j < $this->col; ++$j) {
1602
                    $ar->data[$i * $this->col + $j] = $this->data[$i * $this->col + $j] > $v->data[$j] ? 1 : 0;
1603
                }
1604
            }
1605
            return $ar;
1606
        }
1607
    }
1608
1609
    protected function greaterScalar(int|float $s): matrix {
1610
        $ar = self::factory($this->row, $this->col, $this->dtype);
1611
        for ($i = 0; $i < $this->ndim; ++$i) {
1612
            $ar->data[$i] = $this->data[$i] > $s ? 1 : 0;
1613
        }
1614
        return $ar;
1615
    }
1616
1617
    /**
1618
     * 
1619
     * @param int|float|matrix $m
1620
     * @return matrix
1621
     */
1622
    public function less(int|float|matrix $m): matrix {
1623
        $ar = self::factory($this->row, $this->col, $this->dtype);
1624
        if ($m instanceof self) {
1625
            if ($this->checkShape($this,$m)) {
1626
                for ($i = 0; $i < $this->ndim; ++$i) {
1627
                    $ar->data[$i] = $this->data[$i] < $m->data[$i] ? 1 : 0;
1628
                }
1629
                return $ar;
1630
            }
1631
        } else {
1632
            for ($i = 0; $i < $this->ndim; ++$i) {
1633
                $ar->data[$i] = $this->data[$i] < $m ? 1 : 0;
1634
            }
1635
            return $ar;
1636
        }
1637
    }
1638
1639
    /**
1640
     * Is the matrix symmetric i.e. is it equal to its own transpose?
1641
     *
1642
     * @return bool
1643
     */
1644
    public function isSymmetric(): bool {
1645
        if (!$this->isSquare()) {
1646
            return false;
1647
        }
1648
        $ar = $this->transpose();
1649
        for ($i = 0; $i < $ar->ndim; ++$i) {
1650
            if ($ar->data[$i] != $this->data[$i]) {
1651
                unset($ar);
1652
                return false;
1653
            }
1654
        }
1655
        unset($ar);
1656
        return true;
1657
    }
1658
    
1659
    /**
1660
     * Reshape current matrix.
1661
     * @param int $row
1662
     * @param int $col
1663
     * @return matrix
1664
     */
1665
    public function reshape(int $row, int $col):matrix {
1666
        if($this->ndim != $row * $col) {
1667
            self::_dimensionaMisMatchErr('given dimenssion is not valid for current bufferData');
1668
        }
1669
        $this->row = $row;
1670
        $this->col = $col;
1671
        return $this;
1672
    }
1673
1674
    /**
1675
     * print the matrix in consol
1676
     */
1677
    public function printMatrix() {
1678
        echo __CLASS__ . PHP_EOL;
1679
        for ($i = 0; $i < $this->row; ++$i) {
1680
            for ($j = 0; $j < $this->col; ++$j) {
1681
                printf('%lf  ', $this->data[$i * $this->col + $j]);
1682
            }
1683
            echo PHP_EOL;
1684
        }
1685
    }
1686
1687
    public function __toString() {
1688
        return (string) $this->printMatrix();
1689
    }
1690
1691
    private function flattenArray(array $ar) {
1692
        if (is_array($ar) && is_array($ar[0])) {
1693
            $a = [];
1694
            foreach ($ar as $y => $value) {
1695
                foreach ($value as $k => $v) {
1696
                    $a[] = $v;
1697
                }
1698
            }
1699
            return $a;
1700
        }
1701
    }
1702
    
1703
    /**
1704
     * 
1705
     * @param int $row
1706
     * @param int $col
1707
     * @param int $dtype
1708
     * @return $this
1709
     */
1710
    protected function __construct(public int $row, public int $col, int $dtype = self::Float) {
1711
        if ($this->row < 1 || $this->col < 1) {
1712
            self::_invalidArgument('* To create Numphp/Matrix row & col must be greater than 0!, Op Failed! * ');
1713
        }
1714
        parent::__construct($this->row * $this->col, $dtype);
1715
        return $this;
1716
    }
1717
}
1718