-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNDArray.php
More file actions
250 lines (226 loc) · 6.4 KB
/
Copy pathNDArray.php
File metadata and controls
250 lines (226 loc) · 6.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
<?php
declare(strict_types=1);
namespace PhpMlKit\NDArray;
use FFI\CData;
use PhpMlKit\NDArray\Exceptions\IndexException;
use PhpMlKit\NDArray\FFI\Lib;
use PhpMlKit\NDArray\Traits\CanBePrinted;
use PhpMlKit\NDArray\Traits\CreatesArrays;
use PhpMlKit\NDArray\Traits\HasArrayAccess;
use PhpMlKit\NDArray\Traits\HasComparison;
use PhpMlKit\NDArray\Traits\HasConversion;
use PhpMlKit\NDArray\Traits\HasFourier;
use PhpMlKit\NDArray\Traits\HasIndexing;
use PhpMlKit\NDArray\Traits\HasLinearAlgebra;
use PhpMlKit\NDArray\Traits\HasLogical;
use PhpMlKit\NDArray\Traits\HasMath;
use PhpMlKit\NDArray\Traits\HasOps;
use PhpMlKit\NDArray\Traits\HasReductions;
use PhpMlKit\NDArray\Traits\HasShapeOps;
use PhpMlKit\NDArray\Traits\HasSlicing;
use PhpMlKit\NDArray\Traits\HasStacking;
use PhpMlKit\NDArray\Traits\HasWindowFunctions;
/**
* N-dimensional array class with PHP-managed view metadata and Rust-managed data.
*
* Views share the same Rust handle as their parent. Only root arrays (where
* $base is null) call ndarray_free() on destruction. Views keep their root
* alive through PHP's reference counting via the $base chain.
*
* @implements \ArrayAccess<int|string, bool|Complex|float|int|self>
* @implements \IteratorAggregate<int, bool|Complex|float|int|self>
*/
class NDArray implements \ArrayAccess, \Stringable, \IteratorAggregate
{
use CanBePrinted;
use CreatesArrays;
use HasArrayAccess;
use HasComparison;
use HasConversion;
use HasFourier;
use HasIndexing;
use HasLinearAlgebra;
use HasLogical;
use HasMath;
use HasOps;
use HasReductions;
use HasShapeOps;
use HasSlicing;
use HasStacking;
use HasWindowFunctions;
/**
* Private constructor — use factory methods.
*
* @param CData $handle Opaque pointer to Rust NDArrayWrapper
* @param ArrayMetadata $meta View metadata
* @param DType $dtype Data type
* @param null|self $base Parent array if this is a view
*/
protected function __construct(
protected readonly CData $handle,
protected readonly ArrayMetadata $meta,
protected readonly DType $dtype,
protected readonly ?self $base = null,
) {}
/**
* Destructor — only root arrays free Rust memory.
*/
public function __destruct()
{
if (null === $this->base) {
Lib::get()->ndarray_free($this->handle);
}
}
/**
* Shape tuple, or the length of one axis when `$axis` is given.
*
* With no argument (or `null`), returns the full shape as a list. With an integer, returns
* the size of that axis only. Negative `$axis` counts from the last dimension (`-1` is the
* last axis), consistent with axis arguments elsewhere on this class.
*
* @param null|int $axis axis index into the shape vector (`0` … `ndim - 1`, or negative)
*
* @return ($axis is null ? list<int> : int)
*
* @throws IndexException If `$axis` is out of bounds, or if a scalar length is requested on
* a zero-dimensional array
*/
public function shape(?int $axis = null): array|int
{
if (null === $axis) {
return $this->meta->shape;
}
$ndim = $this->meta->ndim;
if (0 === $ndim) {
throw new IndexException('Cannot index shape of a zero-dimensional array');
}
$resolved = $axis < 0 ? $ndim + $axis : $axis;
if ($resolved < 0 || $resolved >= $ndim) {
throw new IndexException(
"Shape axis {$axis} is out of bounds for array with {$ndim} dimensions"
);
}
return $this->meta->shape[$resolved];
}
/**
* Get strides.
*
* @return array<int>
*/
public function strides(): array
{
return $this->meta->strides;
}
/**
* Get number of dimensions.
*/
public function ndim(): int
{
return $this->meta->ndim;
}
/**
* Get total number of elements.
*/
public function size(): int
{
return $this->meta->size;
}
/**
* Get data type.
*/
public function dtype(): DType
{
return $this->dtype;
}
/**
* Get item size in bytes.
*/
public function itemsize(): int
{
return $this->dtype->itemSize();
}
/**
* Get total bytes consumed.
*/
public function nbytes(): int
{
return $this->meta->size * $this->itemsize();
}
/**
* Get the internal handle (for advanced FFI usage).
*
* @internal
*/
public function handle(): CData
{
return $this->handle;
}
/**
* Get view metadata (shape, strides, offset, ndim).
*
* @internal
*/
public function meta(): ArrayMetadata
{
return $this->meta;
}
/**
* Get view offset relative to root storage.
*
* @internal
*/
public function offset(): int
{
return $this->meta->offset;
}
/**
* Whether this array is a view of another array.
*/
public function isView(): bool
{
return null !== $this->base;
}
/**
* Check if the array is C-contiguous (row-major).
*/
public function isContiguous(): bool
{
$expected = ArrayMetadata::computeStrides($this->meta->shape);
return $this->meta->strides === $expected;
}
/**
* Get iterator for foreach loops.
*
* 1D arrays: yields scalar values
* 2D+ arrays: yields row views (along first axis)
*
* @return \Generator<int, bool|float|int|self>
*/
public function getIterator(): \Generator
{
if (1 === $this->ndim()) {
foreach ($this->flat() as $value) {
yield $value;
}
} else {
$shape = $this->shape();
for ($i = 0; $i < $shape[0]; ++$i) {
yield $this->slice([$i]);
}
}
}
/**
* Get a 1-D iterator over the array.
*
* Returns a FlatIterator that provides 1-D access to the array elements
* in C-contiguous (row-major) order. Uses hybrid approach:
* - Arrays < 100k elements: Batch extraction (fast)
* - Arrays >= 100k elements: Chunked extraction (memory efficient)
*
* @return FlatIterator Iterator over flattened array
*/
public function flat(): FlatIterator
{
return new FlatIterator($this);
}
}