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
|
|
|
* The sum of the vector. |
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 int|float|\Np\matrix|\Np\vector $d |
516
|
|
|
* @return matrix|vector |
517
|
|
|
*/ |
518
|
|
|
public function pow(int|float|\Np\matrix|\Np\vector $d): matrix|vector { |
519
|
|
|
if ($d instanceof matrix) { |
520
|
|
|
return $this->powMatrix($d); |
521
|
|
|
} elseif ($d instanceof vector) { |
522
|
|
|
return $this->powVector($d); |
523
|
|
|
} else { |
524
|
|
|
return $this->powScalar($d); |
|
|
|
|
525
|
|
|
} |
526
|
|
|
} |
527
|
|
|
|
528
|
|
|
/** |
529
|
|
|
* |
530
|
|
|
* @param \Np\matrix $m |
531
|
|
|
* @return matrix |
532
|
|
|
*/ |
533
|
|
|
protected function powMatrix(\Np\matrix $m): matrix { |
534
|
|
|
if ($this->checkDimensions($this, $m) && $this->checkDtype($this, $m)) { |
535
|
|
|
$ar = matrix::factory($m->row, $m->col, $this->dtype); |
536
|
|
|
for ($i = 0; $i < $m->row; ++$i) { |
537
|
|
|
for ($j = 0; $j < $m - col; ++$j) { |
|
|
|
|
538
|
|
|
$ar->data[$i * $m->col + $j] = $m->data[$i * $m->col + $j] ** $this->data[$j]; |
539
|
|
|
} |
540
|
|
|
} |
541
|
|
|
return $ar; |
542
|
|
|
} |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
/** |
546
|
|
|
* |
547
|
|
|
* @param \Np\vector $vector |
548
|
|
|
* @return vector |
549
|
|
|
*/ |
550
|
|
|
protected function powVector(\Np\vector $vector): vector { |
551
|
|
|
if ($this->checkShape($this, $vector) && $this->checkDtype($this, $vector)) { |
552
|
|
|
$vr = self::factory($this->col, $this->dtype); |
553
|
|
|
for ($i = 0; $i < $this->col; ++$i) { |
554
|
|
|
$vr->data[$i] = $this->data[$i] ** $vector->data[$i]; |
555
|
|
|
} |
556
|
|
|
return $vr; |
557
|
|
|
} |
558
|
|
|
} |
559
|
|
|
|
560
|
|
|
/** |
561
|
|
|
* |
562
|
|
|
* @param int|float $s |
563
|
|
|
*/ |
564
|
|
|
protected function powScalar(int|float $s) { |
565
|
|
|
$v = $this->copyVector(); |
566
|
|
|
for ($i = 0; $i < $this->col; ++$i) { |
567
|
|
|
$v->data[$i] = $v->data[$i] ** $s; |
568
|
|
|
} |
569
|
|
|
} |
570
|
|
|
|
571
|
|
|
/** |
572
|
|
|
* |
573
|
|
|
* @param int|float|\Np\matrix|\Np\vector $d |
574
|
|
|
* @return matrix|vector |
575
|
|
|
*/ |
576
|
|
|
public function mod(int|float|\Np\matrix|\Np\vector $d): matrix|vector { |
577
|
|
|
if ($d instanceof matrix) { |
578
|
|
|
return $this->powMatrix($d); |
579
|
|
|
} elseif ($d instanceof vector) { |
580
|
|
|
return $this->powVector($d); |
581
|
|
|
} else { |
582
|
|
|
return $this->powScalar($d); |
|
|
|
|
583
|
|
|
} |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
/** |
587
|
|
|
* |
588
|
|
|
* @param \Np\matrix $m |
589
|
|
|
* @return matrix |
590
|
|
|
*/ |
591
|
|
|
protected function modMatrix(\Np\matrix $m): matrix { |
592
|
|
|
if ($this->checkDimensions($this, $m) && $this->checkDtype($this, $m)) { |
593
|
|
|
$ar = matrix::factory($m->row, $m->col, $this->dtype); |
594
|
|
|
for ($i = 0; $i < $m->row; ++$i) { |
595
|
|
|
for ($j = 0; $j < $m - col; ++$j) { |
|
|
|
|
596
|
|
|
$ar->data[$i * $m->col + $j] = $m->data[$i * $m->col + $j] % $this->data[$j]; |
597
|
|
|
} |
598
|
|
|
} |
599
|
|
|
return $ar; |
600
|
|
|
} |
601
|
|
|
} |
602
|
|
|
|
603
|
|
|
/** |
604
|
|
|
* |
605
|
|
|
* @param \Np\vector $vector |
606
|
|
|
* @return vector |
607
|
|
|
*/ |
608
|
|
|
protected function modVector(\Np\vector $vector): vector { |
609
|
|
|
if ($this->checkShape($this, $vector) && $this->checkDtype($this, $vector)) { |
610
|
|
|
$vr = self::factory($this->col, $this->dtype); |
611
|
|
|
for ($i = 0; $i < $this->col; ++$i) { |
612
|
|
|
$vr->data[$i] = $this->data[$i] % $vector->data[$i]; |
613
|
|
|
} |
614
|
|
|
return $vr; |
615
|
|
|
} |
616
|
|
|
} |
617
|
|
|
|
618
|
|
|
/** |
619
|
|
|
* |
620
|
|
|
* @param int|float $s |
621
|
|
|
*/ |
622
|
|
|
protected function modScalar(int|float $s) { |
623
|
|
|
$v = $this->copyVector(); |
624
|
|
|
for ($i = 0; $i < $this->col; ++$i) { |
625
|
|
|
$v->data[$i] = $v->data[$i] % $s; |
626
|
|
|
} |
627
|
|
|
} |
628
|
|
|
|
629
|
|
|
/** |
630
|
|
|
* |
631
|
|
|
* @param int|float|matrix|vector $d |
632
|
|
|
* @return matrix|vector |
633
|
|
|
*/ |
634
|
|
|
public function subtract(int|float|matrix|vector $d): matrix|vector { |
635
|
|
|
if ($d instanceof matrix) { |
636
|
|
|
return $this->subtractMatrix($d); |
637
|
|
|
} elseif ($d instanceof self) { |
638
|
|
|
return $this->subtractVector($d); |
639
|
|
|
} else { |
640
|
|
|
return $this->substractScalar($d); |
641
|
|
|
} |
642
|
|
|
} |
643
|
|
|
|
644
|
|
|
/** |
645
|
|
|
* |
646
|
|
|
* @param \Np\matrix $m |
647
|
|
|
* @return matrix |
648
|
|
|
*/ |
649
|
|
|
protected function subtractMatrix(\Np\matrix $m): matrix { |
650
|
|
|
if ($this->checkShape($this, $m) && $this->checkDtype($this, $m)) { |
651
|
|
|
$vr = matrix::factory($m->row, $m->col, $m->dtype); |
652
|
|
|
for ($i = 0; $i < $m->row; ++$i) { |
653
|
|
|
for ($j = 0; $j < $m->col; ++$j) { |
654
|
|
|
$vr->data[$i * $m->col + $j] = $this->data[$j] - $m->data[$i * $m->col + $j]; |
655
|
|
|
} |
656
|
|
|
} |
657
|
|
|
return $vr; |
658
|
|
|
} |
659
|
|
|
} |
660
|
|
|
|
661
|
|
|
/** |
662
|
|
|
* |
663
|
|
|
* @param \Np\vector $vector |
664
|
|
|
* @return vector |
665
|
|
|
*/ |
666
|
|
|
protected function subtractVector(\Np\vector $vector): vector { |
667
|
|
|
if ($this->checkShape($this, $vector) && $this->checkDtype($this, $vector)) { |
668
|
|
|
$vr = self::factory($this->col, $this->dtype); |
669
|
|
|
for ($i = 0; $i < $this->col; ++$i) { |
670
|
|
|
$vr->data[$i] = $this->data[$i] - $vector->data[$i]; |
671
|
|
|
} |
672
|
|
|
return $vr; |
673
|
|
|
} |
674
|
|
|
} |
675
|
|
|
|
676
|
|
|
/** |
677
|
|
|
* |
678
|
|
|
* @param \Np\vector $scalar |
679
|
|
|
* @return \Np\vector |
680
|
|
|
*/ |
681
|
|
|
protected function substractScalar(int|float $scalar): vector { |
682
|
|
|
$vr = self::factory($this->col, $this->dtype); |
683
|
|
|
for ($i = 0; $i < $this->col; ++$i) { |
684
|
|
|
$vr->data[$i] = $this->data[$i] - $scalar; |
685
|
|
|
} |
686
|
|
|
return $vr; |
687
|
|
|
} |
688
|
|
|
|
689
|
|
|
/** |
690
|
|
|
* |
691
|
|
|
* @param \Np\vector $v |
692
|
|
|
* @param int $stride |
693
|
|
|
* @return vector |
694
|
|
|
*/ |
695
|
|
|
public function convolve(\Np\vector $v, int $stride = 1): vector { |
696
|
|
|
return convolve::conv1D($this, $v, $stride); |
697
|
|
|
} |
698
|
|
|
|
699
|
|
|
/** |
700
|
|
|
* Run a function over all of the elements in the vector. |
701
|
|
|
* |
702
|
|
|
* @param callable $func |
703
|
|
|
* @return vector |
704
|
|
|
*/ |
705
|
|
|
public function map(callable $func): vector { |
706
|
|
|
$vr = self::factory($this->col, $this->dtype); |
707
|
|
|
for ($i = 0; $i < $this->col; ++$i) { |
708
|
|
|
$vr->data[$i] = $func($this->data[$i]); |
709
|
|
|
} |
710
|
|
|
return $vr; |
711
|
|
|
} |
712
|
|
|
|
713
|
|
|
public function log(float $b = M_E): vector { |
714
|
|
|
$vr = $this->copyVector(); |
715
|
|
|
for ($i = 0; $i < $vr->col; ++$i) { |
716
|
|
|
log($vr->data[$i], $b); |
717
|
|
|
} |
718
|
|
|
return $vr; |
719
|
|
|
} |
720
|
|
|
|
721
|
|
|
public function max() { |
722
|
|
|
return $this->data[blas::max($this)]; |
723
|
|
|
} |
724
|
|
|
|
725
|
|
|
public function min() { |
726
|
|
|
$this->data[blas::min($this)]; |
727
|
|
|
} |
728
|
|
|
|
729
|
|
|
public function abs(): vector { |
730
|
|
|
return $this->map('abs'); |
731
|
|
|
} |
732
|
|
|
|
733
|
|
|
public function sqrt(): vector { |
734
|
|
|
return $this->map('sqrt'); |
735
|
|
|
} |
736
|
|
|
|
737
|
|
|
public function exp(): vector { |
738
|
|
|
return $this->map('exp'); |
739
|
|
|
} |
740
|
|
|
|
741
|
|
|
public function exp1(): vector { |
742
|
|
|
return $this->map('exp1'); |
743
|
|
|
} |
744
|
|
|
|
745
|
|
|
public function log1p(): vector { |
746
|
|
|
return $this->map('log1p'); |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
public function sin(): vector { |
750
|
|
|
return $this->map('sin'); |
751
|
|
|
} |
752
|
|
|
|
753
|
|
|
public function asin(): vector { |
754
|
|
|
return $this->map('asin'); |
755
|
|
|
} |
756
|
|
|
|
757
|
|
|
public function cos(): vector { |
758
|
|
|
return $this->map('cos'); |
759
|
|
|
} |
760
|
|
|
|
761
|
|
|
public function acos(): vector { |
762
|
|
|
return $this->map('acos'); |
763
|
|
|
} |
764
|
|
|
|
765
|
|
|
public function tan(): vector { |
766
|
|
|
return $this->map('tan'); |
767
|
|
|
} |
768
|
|
|
|
769
|
|
|
public function atan(): vector { |
770
|
|
|
return $this->map('atan'); |
771
|
|
|
} |
772
|
|
|
|
773
|
|
|
public function radToDeg(): vector { |
774
|
|
|
return $this->map('rad2deg'); |
775
|
|
|
} |
776
|
|
|
|
777
|
|
|
public function degToRad(): vector { |
778
|
|
|
return $this->map('deg2rad'); |
779
|
|
|
} |
780
|
|
|
|
781
|
|
|
public function floor(): vector { |
782
|
|
|
return $this->map('floor'); |
783
|
|
|
} |
784
|
|
|
|
785
|
|
|
public function ceil(): vector { |
786
|
|
|
return $this->map('ceil'); |
787
|
|
|
} |
788
|
|
|
|
789
|
|
|
/** |
790
|
|
|
* |
791
|
|
|
* @param float $min |
792
|
|
|
* @param float $max |
793
|
|
|
* @return vector |
794
|
|
|
*/ |
795
|
|
|
public function clip(float $min, float $max) : vector { |
796
|
|
|
if ($min > $max) { |
797
|
|
|
self::_invalidArgument('Minimum cannot be greater than maximum.'); |
798
|
|
|
} |
799
|
|
|
|
800
|
|
|
$vr = self::factory($this->col, $this->dtype); |
801
|
|
|
|
802
|
|
|
for($i = 0; $i < $this->col; ++$i) { |
803
|
|
|
if ($this->data[$i] > $max) { |
804
|
|
|
$vr->data[$i] = $max; |
805
|
|
|
continue; |
806
|
|
|
} |
807
|
|
|
if ($this->data[$i] < $min) { |
808
|
|
|
$vr->data[$i] = $min; |
809
|
|
|
continue; |
810
|
|
|
} |
811
|
|
|
} |
812
|
|
|
|
813
|
|
|
return $vr; |
814
|
|
|
} |
815
|
|
|
|
816
|
|
|
public function clipUpper(float $min) : vector { |
817
|
|
|
$vr = self::factory($this->col, $this->dtype); |
818
|
|
|
for($i = 0; $i < $this->col; ++$i) { |
819
|
|
|
if ($this->data[$i] > $min) { |
820
|
|
|
$vr->data[$i] = $min; |
821
|
|
|
continue; |
822
|
|
|
} |
823
|
|
|
} |
824
|
|
|
return $vr; |
825
|
|
|
} |
826
|
|
|
|
827
|
|
|
public function clipLower(float $min) : vector { |
828
|
|
|
$vr = self::factory($this->col, $this->dtype); |
829
|
|
|
for($i = 0; $i < $this->col; ++$i) { |
830
|
|
|
if ($this->data[$i] < $min) { |
831
|
|
|
$vr->data[$i] = $min; |
832
|
|
|
continue; |
833
|
|
|
} |
834
|
|
|
} |
835
|
|
|
return $vr; |
836
|
|
|
} |
837
|
|
|
|
838
|
|
|
/** |
839
|
|
|
* Return the inner product of two vectors. |
840
|
|
|
* |
841
|
|
|
* @param \Np\vector $vector |
842
|
|
|
* |
843
|
|
|
*/ |
844
|
|
|
public function inner(\Np\vector $vector) { |
845
|
|
|
return $this->dotVector($vector); |
846
|
|
|
} |
847
|
|
|
|
848
|
|
|
/** |
849
|
|
|
* Calculate the L1 norm of the vector. |
850
|
|
|
* @return float |
851
|
|
|
*/ |
852
|
|
|
public function normL1(): float { |
853
|
|
|
return $this->abs()->sum(); |
854
|
|
|
} |
855
|
|
|
|
856
|
|
|
public function normL2() { |
857
|
|
|
return sqrt($this->square()->sum()); |
858
|
|
|
} |
859
|
|
|
|
860
|
|
|
public function normMax() { |
861
|
|
|
return $this->abs()->max(); |
862
|
|
|
} |
863
|
|
|
|
864
|
|
|
public function normP(float $p = 2.5) { |
865
|
|
|
if ($p <= 0.0) { |
866
|
|
|
self::_invalidArgument('P must be greater than 0.0 !'); |
867
|
|
|
} |
868
|
|
|
return $this->abs()->powScalar($p)->sum() ** (1.0 / $p); |
|
|
|
|
869
|
|
|
} |
870
|
|
|
|
871
|
|
|
/** |
872
|
|
|
* Return the reciprocal of the vector element-wise. |
873
|
|
|
* |
874
|
|
|
* @return self |
875
|
|
|
*/ |
876
|
|
|
public function reciprocal(): vector { |
877
|
|
|
return self::ones($this->col, $this->dtype) |
878
|
|
|
->divideVector($this); |
879
|
|
|
} |
880
|
|
|
|
881
|
|
|
/** |
882
|
|
|
* |
883
|
|
|
* @return int|float |
884
|
|
|
*/ |
885
|
|
|
public function mean():int|float { |
886
|
|
|
return $this->sum()/ $this->col; |
887
|
|
|
} |
888
|
|
|
|
889
|
|
|
/** |
890
|
|
|
* |
891
|
|
|
* @return int|float |
892
|
|
|
*/ |
893
|
|
|
public function median():int|float { |
894
|
|
|
$mid = intdiv($this->col, 2); |
895
|
|
|
|
896
|
|
|
$a = $this->copyVector()->sort(); |
897
|
|
|
if ($this->col % 2 === 1) { |
898
|
|
|
$median = $a->data[$mid]; |
899
|
|
|
} else { |
900
|
|
|
$median = ($a->data[$mid - 1] + $a->data[$mid]) / 2.; |
901
|
|
|
} |
902
|
|
|
return $median; |
903
|
|
|
} |
904
|
|
|
|
905
|
|
|
public function variance($mean = null) |
906
|
|
|
{ |
907
|
|
|
if (is_null($mean)) { |
908
|
|
|
$mean = $this->mean(); |
909
|
|
|
} |
910
|
|
|
|
911
|
|
|
$sd = $this->substractScalar($mean) |
912
|
|
|
->square() |
913
|
|
|
->sum(); |
914
|
|
|
|
915
|
|
|
return $sd / $this->col; |
916
|
|
|
} |
917
|
|
|
|
918
|
|
|
/** |
919
|
|
|
* |
920
|
|
|
* @return vector |
921
|
|
|
*/ |
922
|
|
|
public function square(): vector { |
923
|
|
|
return $this->multiplyVector($this); |
924
|
|
|
} |
925
|
|
|
|
926
|
|
|
/** |
927
|
|
|
* sort the vector |
928
|
|
|
* @param string $type i or d |
929
|
|
|
* |
930
|
|
|
*/ |
931
|
|
|
public function sort($type = 'i') { |
932
|
|
|
lapack::sort($this, $type); |
933
|
|
|
return $this; |
934
|
|
|
} |
935
|
|
|
|
936
|
|
|
/** |
937
|
|
|
* set data to vector |
938
|
|
|
* @param int|float|array $data |
939
|
|
|
*/ |
940
|
|
|
public function setData(int|float|array $data) { |
941
|
|
|
if (is_array($data) && !is_array($data[0])) { |
942
|
|
|
for ($i = 0; $i < $this->col; ++$i) { |
943
|
|
|
$this->data[$i] = $data[$i]; |
944
|
|
|
} |
945
|
|
|
} elseif (is_numeric($data)) { |
946
|
|
|
for ($i = 0; $i < $this->col; ++$i) { |
947
|
|
|
$this->data[$i] = $data; |
948
|
|
|
} |
949
|
|
|
} |
950
|
|
|
} |
951
|
|
|
|
952
|
|
|
public function asMatrix(): matrix { |
953
|
|
|
$size = (int) sqrt($this->col); |
954
|
|
|
$ar = matrix::factory($size, $size, $this->dtype); |
955
|
|
|
for ($i = 0; $i < $ar->ndim; ++$i) { |
956
|
|
|
$ar->data[$i] = $this->data[$i]; |
957
|
|
|
} |
958
|
|
|
return $ar; |
959
|
|
|
} |
960
|
|
|
|
961
|
|
|
/** |
962
|
|
|
* get the shape of matrix |
963
|
|
|
* @return int |
964
|
|
|
*/ |
965
|
|
|
public function getShape(): int { |
966
|
|
|
return $this->col; |
967
|
|
|
} |
968
|
|
|
|
969
|
|
|
public function getDtype() { |
970
|
|
|
return $this->dtype; |
971
|
|
|
} |
972
|
|
|
|
973
|
|
|
public function asArray() { |
974
|
|
|
$ar = array_fill(0, $this->col, null); |
975
|
|
|
for ($i = 0; $i < $this->col; ++$i) { |
976
|
|
|
$ar[$i] = $this->data[$i]; |
977
|
|
|
} |
978
|
|
|
return $ar; |
979
|
|
|
} |
980
|
|
|
|
981
|
|
|
public function printVector() { |
982
|
|
|
for ($j = 0; $j < $this->col; ++$j) { |
983
|
|
|
printf('%lf ', $this->data[$j]); |
984
|
|
|
} |
985
|
|
|
echo PHP_EOL; |
986
|
|
|
} |
987
|
|
|
|
988
|
|
|
public function __toString() { |
989
|
|
|
return (string) $this->printVector(); |
990
|
|
|
} |
991
|
|
|
|
992
|
|
|
protected function __construct(public int $col, int $dtype = self::FLOAT) { |
993
|
|
|
if ($this->col < 1) { |
994
|
|
|
throw new invalidArgumentException('* To create Numphp/Vector col must be greater than 0!, Op Failed! * '); |
995
|
|
|
} |
996
|
|
|
parent::__construct($this->col, $dtype); |
997
|
|
|
return $this; |
998
|
|
|
} |
999
|
|
|
|
1000
|
|
|
} |
1001
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.