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

vector::checkDimensions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 5
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Np;
6
7
use Np\core\{
8
    nd,
9
    blas,
10
    lapack
11
};
12
use Np\exceptions\{
13
    invalidArgumentException,
14
};
15
16
/** A fast lite memory efficient Scientific Computing in php
17
 * Vector (rank-1)
18
 * 
19
 * @package NumPhp
20
 * @version V0.0.alpha
21
 * @category Php Scientific Library
22
 * @author ghost (Shubham Chaudhary)
23
 * @email [email protected]
24
 * @copyright (c) 2020-2021, Shubham Chaudhary
25
 * 
26
 */
27
class vector extends nd {
28
29
    /**
30
     * Factory method to build a new vector.
31
     * 
32
     * @param int $col
33
     * @param int $dtype
34
     * @return vector
35
     */
36
    public static function factory(int $col, int $dtype = self::FLOAT): vector {
37
        return new self($col, $dtype);
38
    }
39
40
    /**
41
     * Build a new vector from a php array.
42
     * 
43
     * @param array $data
44
     * @param int $dtype
45
     * @return vector
46
     */
47
    public static function ar(array $data, int $dtype = self::FLOAT): vector {
48
        if (is_array($data) && !is_array($data[0])) {
49
            $ar = self::factory(count($data), $dtype);
50
            $ar->setData($data);
51
            return $ar;
52
        } else {
53
            self::_err('data must be of same dimensions');
54
        }
55
    }
56
57
    /**
58
     * Return vector with random values
59
     * @param int $col
60
     * @param int $dtype
61
     * @return vector
62
     */
63
    public static function randn(int $col, int $dtype = self::FLOAT): vector {
64
        $ar = self::factory($col, $dtype);
65
        $max = getrandmax();
66
        for ($i = 0; $i < $ar->col; ++$i) {
67
            $ar->data[$i] = rand() / $max;
68
        }
69
        return $ar;
70
    }
71
72
    /**
73
     * Return vector with uniform values
74
     * @param int $col
75
     * @param int $dtype
76
     * @return vector
77
     */
78
    public static function uniform(int $col, int $dtype = self::FLOAT): vector {
79
        $ar = self::factory($col, $dtype);
80
        $max = getrandmax();
81
        for ($i = 0; $i < $col; ++$i) {
82
            $ar->data[$i] = rand(-$max, $max) / $max;
83
        }
84
        return $ar;
85
    }
86
87
    /**
88
     * Build a vector of zeros with n elements.
89
     * 
90
     * @param int $col
91
     * @param int $dtype
92
     * @return vector
93
     */
94
    public static function zeros(int $col, int $dtype = self::FLOAT): vector {
95
        $ar = self::factory($col, $dtype);
96
        for ($i = 0; $i < $col; ++$i) {
97
            $ar->data[$i] = 0;
98
        }
99
        return $ar;
100
    }
101
102
    /**
103
     * create one like vector
104
     * 
105
     * @param int $col
106
     * @return vector
107
     */
108
    public static function ones(int $col, int $dtype = self::FLOAT): vector {
109
        $ar = self::factory($col, $dtype);
110
        for ($i = 0; $i < $col; ++$i) {
111
            $ar->data[$i] = 1;
112
        }
113
        return $ar;
114
    }
115
116
    /**
117
     * create a null like vector
118
     * @param int $col
119
     * @return vector
120
     */
121
    public static function null(int $col, int $dtype = self::FLOAT): vector {
122
        $ar = self::factory($col, $dtype);
123
        for ($i = 0; $i < $col; ++$i) {
124
            $ar->data[$i] = null;
125
        }
126
        return $ar;
127
    }
128
129
    /**
130
     * create a vector with given scalar value
131
     * @param int $col
132
     * @param int|float|double $val
133
     * @param int $dtype
134
     * @return vector
135
     */
136
    public static function full(int $col, int|float $val, int $dtype = self::FLOAT): vector {
137
        $ar = self::factory($col, $dtype);
138
        for ($i = 0; $i < $col; ++$i) {
139
            $ar->data[$i] = $val;
140
        }
141
        return $ar;
142
    }
143
144
    /**
145
     * Return evenly spaced values within a given interval.
146
     *
147
     * @param int|float $start
148
     * @param int|float $end
149
     * @param int|float $interval
150
     * @param int $dtype 
151
     * @return vector
152
     */
153
    public static function range(int|float $start, int|float $end, int|float $interval = 1, int $dtype = self::FLOAT): vector {
154
        return self::ar(range($start, $end, $interval), $dtype);
155
    }
156
157
    /**
158
     * Return a Gaussian random vector with mean 0
159
     * and unit variance.
160
     *
161
     * @param int $n
162
     * @param int $dtype
163
     * @return self
164
     */
165
    public static function gaussian(int $n, int $dtype = self::FLOAT): vector {
166
        $max = getrandmax();
167
        $a = [];
168
        while (count($a) < $n) {
169
            $r = sqrt(-2.0 * log(rand() / $max));
170
            $phi = rand() / $max * (2. * M_PI);
171
            $a[] = $r * sin($phi);
172
            $a[] = $r * cos($phi);
173
        }
174
        if (count($a) > $n) {
175
            $a = array_slice($a, 0, $n);
176
        }
177
        return self::ar($a, $dtype);
178
    }
179
180
    /**
181
     * Generate a vector with n elements from a Poisson distribution.
182
     *
183
     * @param int $n
184
     * @param float $lambda
185
     * @param int $dtype 
186
     * @return vector
187
     */
188
    public static function poisson(int $n, float $lambda = 1.0, int $dtype = self::FLOAT): vector {
189
        $max = getrandmax();
190
        $l = exp(-$lambda);
191
        $a = new self($n, $dtype);
192
        for ($i = 0; $i < $n; ++$i) {
193
            $k = 0;
194
            $p = 1.0;
195
            while ($p > $l) {
196
                ++$k;
197
                $p *= rand() / $max;
198
            }
199
            $a->data[$i] = $k - 1;
200
        }
201
        return $a;
202
    }
203
204
    /**
205
     * Return a vector of n evenly spaced numbers between minimum and maximum.
206
     *
207
     * @param float $min
208
     * @param float $max
209
     * @param int $n
210
     * @param int $dtype
211
     * @throws invalidArgumentException
212
     * @return vector
213
     */
214
    public static function linspace(float $min, float $max, int $n, int $dtype = self::FLOAT): vector {
215
        if ($min > $max) {
216
            throw new invalidArgumentException('Minimum must be less than maximum.');
217
        }
218
        if ($n < 2) {
219
            throw new invalidArgumentException('Number of elements must be greater than 1.');
220
        }
221
        $k = $n - 1;
222
        $interval = abs($max - $min) / $k;
223
        $a = [$min];
224
        while (count($a) < $k) {
225
            $a[] = end($a) + $interval;
226
        }
227
        $a[] = $max;
228
        return self::ar($a,$dtype);
229
    }
230
231
    /**
232
     * make a copy of vector
233
     * @return vector
234
     */
235
    public function copyVector(): vector {
236
        return clone $this;
237
    }
238
239
    /**
240
     * Return the element-wise maximum of given vector with current vector
241
     * 
242
     * @param \Np\vector $vector
243
     * @return vector
244
     */
245
    public function maximum(\Np\vector $vector): vector {
246
        if ($this->checkShape($this,$vector) && $this->checkDtype($this,$vector)) {
247
            $v = new self($this->ndim, $this->dtype);
248
            for($i = 0; $i<$v->ndim; ++$i) {
249
                $v->data[$i] = max($this->data[$i],$vector->data[$i]);
250
            }
251
            return $v;
252
        }
253
    }
254
255
    /**
256
     * Return the element-wise minium of given vector with current vector
257
     * 
258
     * @param \Np\vector $vector
259
     * @return vector
260
     */
261
    public function minium(\Np\vector $vector): vector {
262
        if ($this->checkShape($this, $vector) && $this->checkDtype($this, $vector)) {
263
            $v = new self($this->ndim, $this->dtype);
264
            for($i = 0; $i<$v->ndim; ++$i) {
265
                $v->data[$i] = min($this->data[$i],$vector->data[$i]);
266
            }
267
            return $v;
268
        }
269
    }
270
    
271
    /**
272
     * Return the index of the minimum element in the vector.
273
     * 
274
     * @return int
275
     */
276
    public function argMin():int {
277
        return blas::min($this);
278
    }
279
    
280
    /**
281
     * Return the index of the maximum element in the vector.
282
     * 
283
     * @return int
284
     */
285
    public function argMx():int {
286
        return blas::max($this);
287
    }
288
289
    /**
290
     * vector-vector dot product
291
     * @param \Np\vector $vector
292
     * @param int $incX
293
     * @param int $incY
294
     * @return vector
295
     */
296
    public function dotVector(\Np\vector $v) {
297
        if ($this->checkDtype($this, $v)) {
298
            return blas::dot($this, $v);
299
        }
300
    }
301
302
    /**
303
     * 
304
     * @return float
305
     */
306
    public function sum(): float {
307
        return blas::asum($this);
308
    }
309
310
    /**
311
     * Return the product of the vector.
312
     * @return int|float
313
     */
314
    public function product(): float {
315
        $r = 1.0;
316
        for ($i = 0; $i < $this->col; ++$i) {
317
            $r *= $this->data[$i];
318
        }
319
        return $r;
320
    }
321
322
    /**
323
     * Compute the vector-matrix dot product of this vector and matrix .
324
     * @param \Np\matrix $m
325
     * @return vector
326
     */
327
    public function dotMatrix(\Np\matrix $m): vector {
328
        if ($this->checkDtype($this, $m)) {
329
            $mvr = self::factory($this->col, $this->dtype);
330
            core\blas::gemv($m, $this, $mvr);
331
            return $mvr;
332
        }
333
    }
334
335
    /**
336
     * 
337
     * @param int|float|matrix|vector $d
338
     * @return matrix|vector
339
     */
340
    public function divide(int|float|matrix|vector $d): matrix|vector {
341
        if ($d instanceof matrix) {
342
            return $this->divideMatrix($d);
343
        } elseif ($d instanceof self) {
344
            return $this->divideVector($d);
345
        } else {
346
            return $this->divideScalar($d);
347
        }
348
    }
349
350
    /**
351
     * 
352
     * @param \Np\matrix $m
353
     * @return matrix
354
     */
355
    protected function divideMatrix(\Np\matrix $m): matrix {
356
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
357
            $vr = matrix::factory($m->row, $m->col, $m->dtype);
358
            for ($i = 0; $i < $m->row; ++$i) {
359
                for ($j = 0; $j < $m->col; ++$j) {
360
                    $vr->data[$i * $m->col + $j] = $this->data[$j] / $m->data[$i * $m->col + $j];
361
                }
362
            }
363
            return $vr;
364
        }
365
    }
366
367
    /**
368
     * 
369
     * @param vector $v
370
     * @return vector
371
     */
372
    protected function divideVector(vector $v): vector {
373
        if ($this->checkShape($this, $v) && $this->checkDtype($this, $v)) {
374
            $vr = self::factory($this->col, $this->dtype);
375
            for ($i = 0; $i < $this->col; ++$i) {
376
                $vr->data[$i] = $this->data[$i] / $v->data[$i];
377
            }
378
            return $vr;
379
        }
380
    }
381
382
    /**
383
     * 
384
     * @param int|float $s
385
     * @return vector
386
     */
387
    protected function divideScalar(int|float $s): vector {
388
        $vr = self::factory($this->col, $this->dtype);
389
        for ($i = 0; $i < $this->col; ++$i) {
390
            $vr->data[$i] = $this->data[$i] / $s;
391
        }
392
        return $vr;
393
    }
394
395
    /**
396
     * 
397
     * @param int|float|matrix|vector $d
398
     * @return matrix|vector
399
     */
400
    public function multiply(int|float|matrix|vector $d): matrix|vector {
401
        if ($d instanceof matrix) {
402
            return $this->multiplyMatrix($d);
403
        } elseif ($d instanceof self) {
404
            return $this->multiplyVector($d);
405
        } else {
406
            return $this->multiplyScalar($d);
407
        }
408
    }
409
410
    /**
411
     * 
412
     * @param \Np\matrix $m
413
     * @return matrix
414
     */
415
    protected function multiplyMatrix(\Np\matrix $m): matrix {
416
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
417
            $vr = matrix::factory($m->row, $m->col, $m->dtype);
418
            for ($i = 0; $i < $m->row; ++$i) {
419
                for ($j = 0; $j < $m->col; ++$j) {
420
                    $vr->data[$i * $m->col + $j] = $this->data[$j] * $m->data[$i * $m->col + $j];
421
                }
422
            }
423
            return $vr;
424
        }
425
    }
426
427
    /**
428
     * 
429
     * @param \Np\vector $vector
430
     * @return vector
431
     */
432
    protected function multiplyVector(\Np\vector $vector): vector {
433
        if ($this->checkShape($this,$vector) && $this->checkDtype($this,$vector)) {
434
            $vr = self::factory($this->col, $this->dtype);
435
            for ($i = 0; $i < $this->col; ++$i) {
436
                $vr->data[$i] = $this->data[$i] * $vector->data[$i];
437
            }
438
            return $vr;
439
        }
440
    }
441
442
    /**
443
     * 
444
     * @param int|float $s
445
     * @return vector
446
     */
447
    protected function multiplyScalar(int|float $s): vector {
448
        $vr = $this->copyVector();
449
        blas::scale($s, $vr);
450
        return $vr;
451
    }
452
453
    /**
454
     * 
455
     * @param int|float|matrix|vector $d
456
     * @return matrix|vector
457
     */
458
    public function add(int|float|matrix|vector $d): matrix|vector {
459
        if ($d instanceof matrix) {
460
            return $this->addMatrix($d);
461
        } elseif ($d instanceof self) {
462
            return $this->addVector($d);
463
        } else {
464
            return $this->addScalar($d);
465
        }
466
    }
467
468
    /**
469
     * 
470
     * @param \Np\matrix $m
471
     * @return matrix
472
     */
473
    protected function addMatrix(\Np\matrix $m): matrix {
474
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
475
            $vr = matrix::factory($m->row, $m->col, $m->dtype);
476
            for ($i = 0; $i < $m->row; ++$i) {
477
                for ($j = 0; $j < $m->col; ++$j) {
478
                    $vr->data[$i * $m->col + $j] = $this->data[$j] + $m->data[$i * $m->col + $j];
479
                }
480
            }
481
            return $vr;
482
        }
483
    }
484
485
    /**
486
     * 
487
     * @param \Np\vector $vector
488
     * @return vector
489
     */
490
    protected function addVector(\Np\vector $vector): vector {
491
        if ($this->checkShape($this, $vector) && $this->checkDtype($this, $vector)) {
492
            $vr = self::factory($this->col, $this->dtype);
493
            for ($i = 0; $i < $this->col; ++$i) {
494
                $vr->data[$i] = $this->data[$i] + $vector->data[$i];
495
            }
496
            return $vr;
497
        }
498
    }
499
500
    /**
501
     * 
502
     * @param int|float $s
503
     * @return vector
504
     */
505
    protected function addScalar(int|float $s): vector {
506
        $vr = $this->copyVector();
507
        for ($i = 0; $i < $this->col; ++$i) {
508
            $vr->data[$i] += $s;
509
        }
510
        return $vr;
511
    }
512
513
    /**
514
     * 
515
     * @param \Np\vector $vector
516
     * @return vector
517
     */
518
    public function powVector(\Np\vector $vector): vector {
519
        if ($this->checkShape($this, $vector) && $this->checkDtype($this,  $vector)) {
520
            $vr = self::factory($this->col, $this->dtype);
521
            for ($i = 0; $i < $this->col; ++$i) {
522
                $vr->data[$i] = $this->data[$i] ** $vector->data[$i];
523
            }
524
            return $vr;
525
        }
526
    }
527
528
    /**
529
     * 
530
     * @param \Np\vector $vector
531
     * @return vector
532
     */
533
    public function modVector(\Np\vector $vector): vector {
534
        if ($this->checkShape($this, $vector) && $this->checkDtype($this, $vector)) {
535
            $vr = self::factory($this->col, $this->dtype);
536
            for ($i = 0; $i < $this->col; ++$i) {
537
                $vr->data[$i] = $this->data[$i] % $vector->data[$i];
538
            }
539
            return $vr;
540
        }
541
    }
542
543
    /**
544
     * 
545
     * @param int|float|matrix|vector $d
546
     * @return matrix|vector
547
     */
548
    public function subtract(int|float|matrix|vector $d): matrix|vector {
549
        if ($d instanceof matrix) {
550
            return $this->subtractMatrix($d);
551
        } elseif ($d instanceof self) {
552
            return $this->subtractVector($d);
553
        } else {
554
            return $this->substractScalar($d);
555
        }
556
    }
557
558
    /**
559
     * 
560
     * @param \Np\matrix $m
561
     * @return matrix
562
     */
563
    protected function subtractMatrix(\Np\matrix $m): matrix {
564
        if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) {
565
            $vr = matrix::factory($m->row, $m->col, $m->dtype);
566
            for ($i = 0; $i < $m->row; ++$i) {
567
                for ($j = 0; $j < $m->col; ++$j) {
568
                    $vr->data[$i * $m->col + $j] = $this->data[$j] - $m->data[$i * $m->col + $j];
569
                }
570
            }
571
            return $vr;
572
        }
573
    }
574
575
    /**
576
     * 
577
     * @param \Np\vector $vector
578
     * @return vector
579
     */
580
    protected function subtractVector(\Np\vector $vector): vector {
581
        if ($this->checkShape($this,$vector) && $this->checkDtype($this,$vector)) {
582
            $vr = self::factory($this->col, $this->dtype);
583
            for ($i = 0; $i < $this->col; ++$i) {
584
                $vr->data[$i] = $this->data[$i] - $vector->data[$i];
585
            }
586
            return $vr;
587
        }
588
    }
589
590
    /**
591
     * 
592
     * @param \Np\vector $scalar
593
     * @return \Np\vector
594
     */
595
    protected function substractScalar(int|float $scalar): vector {
596
        $vr = self::factory($this->col, $this->dtype);
597
        for ($i = 0; $i < $this->col; ++$i) {
598
            $vr->data[$i] = $this->data[$i] - $scalar;
599
        }
600
        return $vr;
601
    }
602
603
    /**
604
     * 
605
     * @param \Np\vector $v
606
     * @param int $stride
607
     * @return vector
608
     */
609
    public function convolve(\Np\vector $v, int $stride = 1): vector {
610
        return convolve::conv1D($this, $v, $stride);
611
    }
612
613
    /**
614
     * Return the inner product of two vectors.
615
     *
616
     * @param \Np\vector $vector
617
     * 
618
     */
619
    public function inner(\Np\vector $vector) {
620
        return $this->dotVector($vector);
621
    }
622
623
    public function l1_norm() {
624
        
625
    }
626
627
    public function l2_norm() {
628
        
629
    }
630
631
    /**
632
     * sort the vector 
633
     * @param string $type i or d
634
     * 
635
     */
636
    public function sort($type = 'i') {
637
        lapack::sort($this, $type);
638
        return $this;
639
    }
640
641
    /**
642
     * set data to vector
643
     * @param int|float|array $data
644
     */
645
    public function setData(int|float|array $data) {
646
        if (is_array($data) && !is_array($data[0])) {
647
            for ($i = 0; $i < $this->col; ++$i) {
648
                $this->data[$i] = $data[$i];
649
            }
650
        } elseif (is_numeric($data)) {
651
            for ($i = 0; $i < $this->col; ++$i) {
652
                $this->data[$i] = $data;
653
            }
654
        }
655
    }
656
657
    public function asMatrix(): matrix {
658
        $size = (int) sqrt($this->col);
659
        $ar = matrix::factory($size, $size, $this->dtype);
660
        for ($i = 0; $i < $ar->ndim; ++$i) {
661
            $ar->data[$i] = $this->data[$i];
662
        }
663
        return $ar;
664
    }
665
666
    /**
667
     * get the shape of matrix
668
     * @return int
669
     */
670
    public function getShape(): int {
671
        return $this->col;
672
    }
673
674
    public function getDtype() {
675
        return $this->dtype;
676
    }
677
678
    public function asArray() {
679
        $ar = array_fill(0, $this->col, null);
680
        for ($i = 0; $i < $this->col; ++$i) {
681
            $ar[$i] = $this->data[$i];
682
        }
683
        return $ar;
684
    }
685
686
    public function printVector() {
687
        for ($j = 0; $j < $this->col; ++$j) {
688
            printf('%lf  ', $this->data[$j]);
689
        }
690
        echo PHP_EOL;
691
    }
692
693
    public function __toString() {
694
        return (string) $this->printVector();
695
    }
696
    
697
    protected function __construct(public int $col, int $dtype = self::FLOAT) {
698
        if ($this->col < 1) {
699
            throw new invalidArgumentException('* To create Numphp/Vector col must be greater than 0!, Op Failed! * ');
700
        }
701
        parent::__construct($this->col, $dtype);
702
        return $this;
703
    }
704
705
}
706