1use std::fmt;
4use std::sync::{Arc, Mutex, MutexGuard, OnceLock, PoisonError};
5
6use smallvec::SmallVec;
7
8use crate::buffer::Buffer;
9use crate::primitive::Primitive;
10use crate::shape::{self, Flags, Shape, Strides};
11use crate::{DType, Device, Element, Error, Result, Stream, default_stream};
12
13#[derive(Clone)]
19pub struct Array {
20 pub(crate) node: Arc<Node>,
21 pub(crate) output: u32,
22}
23
24pub(crate) struct Node {
26 pub(crate) stream: Stream,
27 pub(crate) outputs: SmallVec<[Output; 1]>,
28 pub(crate) state: Mutex<State>,
29}
30
31pub(crate) struct Output {
33 pub(crate) dtype: DType,
34 pub(crate) shape: Shape,
35 pub(crate) strides: Strides,
36 pub(crate) flags: Flags,
37 pub(crate) size: usize,
38 pub(crate) slot: OnceLock<Result<Data>>,
40}
41
42#[derive(Clone)]
45pub(crate) struct Data {
46 pub(crate) buffer: Arc<Buffer>,
47 pub(crate) offset: isize,
49}
50
51impl Data {
52 pub(crate) fn new(buffer: Buffer) -> Self {
53 Self {
54 buffer: Arc::new(buffer),
55 offset: 0,
56 }
57 }
58}
59
60pub(crate) enum State {
62 Pending {
63 primitive: Primitive,
64 inputs: Vec<Array>,
65 },
66 Done,
67}
68
69const _: () = {
70 const fn assert_send_sync<T: Send + Sync>() {}
71 assert_send_sync::<Array>();
72};
73
74impl Node {
75 pub(crate) fn lock(&self) -> MutexGuard<'_, State> {
76 self.state.lock().unwrap_or_else(PoisonError::into_inner)
77 }
78}
79
80impl Output {
81 pub(crate) fn new(
82 dtype: DType,
83 shape: Shape,
84 strides: Option<Strides>,
85 slot: OnceLock<Result<Data>>,
86 ) -> Result<Self> {
87 shape::checked_nbytes(&shape, dtype.size())?;
88 let size = shape::checked_size(&shape)?;
89 let strides = strides.unwrap_or_else(|| shape::row_contiguous_strides(&shape));
90 let flags = shape::flags_for(&shape, &strides);
91 Ok(Self {
92 dtype,
93 shape,
94 strides,
95 flags,
96 size,
97 slot,
98 })
99 }
100}
101
102impl Array {
103 pub fn from_slice<T: Element>(data: &[T], shape: &[usize]) -> Result<Self> {
109 let expected = shape::checked_size(shape)?;
110 if data.len() != expected {
111 return Err(Error::SizeMismatch {
112 expected,
113 actual: data.len(),
114 });
115 }
116 let mut buffer = Buffer::try_alloc(shape::checked_nbytes(shape, T::DTYPE.size())?)?;
117 T::write_bytes(data, buffer.bytes_mut());
118 Self::leaf(Ok(Data::new(buffer)), T::DTYPE, shape.into())
119 }
120
121 pub fn full<T: Element>(shape: &[usize], value: T) -> Result<Self> {
123 let itemsize = T::DTYPE.size();
124 let mut buffer = Buffer::try_alloc(shape::checked_nbytes(shape, itemsize)?)?;
125 let mut element = vec![0u8; itemsize];
126 T::write_bytes(&[value], &mut element);
127 for chunk in buffer.bytes_mut().chunks_exact_mut(itemsize) {
128 chunk.copy_from_slice(&element);
129 }
130 Self::leaf(Ok(Data::new(buffer)), T::DTYPE, shape.into())
131 }
132
133 pub fn scalar<T: Element>(value: T) -> Self {
135 Self::from_slice(&[value], &[]).expect("a scalar always matches its shape")
136 }
137
138 pub(crate) fn from_buffer(buffer: Buffer, dtype: DType, shape: &[usize]) -> Result<Self> {
140 Self::leaf(Ok(Data::new(buffer)), dtype, shape.into())
141 }
142
143 pub(crate) fn leaf(data: Result<Data>, dtype: DType, shape: Shape) -> Result<Self> {
145 let output = Output::new(dtype, shape, None, OnceLock::from(data))?;
146 Ok(Self::from_node(Node {
147 stream: default_stream(),
148 outputs: SmallVec::from_buf([output]),
149 state: Mutex::new(State::Done),
150 }))
151 }
152
153 pub(crate) fn failed(error: Error, dtype: DType, shape: &[usize]) -> Self {
156 Self::leaf(Err(error.clone()), dtype, shape.into())
157 .unwrap_or_else(|_| Self::leaf(Err(error), dtype, Shape::new()).expect("scalar shape"))
158 }
159
160 pub(crate) fn from_op(primitive: Primitive, inputs: Vec<Array>) -> Result<Vec<Array>> {
164 if let Some(err) = inputs.iter().find_map(Array::error) {
165 return Err(err);
166 }
167 let outputs = primitive
168 .output_specs(&inputs)?
169 .into_iter()
170 .map(|spec| Output::new(spec.dtype, spec.shape, spec.strides, OnceLock::new()))
171 .collect::<Result<SmallVec<_>>>()?;
172 let count = outputs.len() as u32;
173 let node = Arc::new(Node {
174 stream: default_stream(),
175 outputs,
176 state: Mutex::new(State::Pending { primitive, inputs }),
177 });
178 Ok((0..count)
179 .map(|output| Self {
180 node: node.clone(),
181 output,
182 })
183 .collect())
184 }
185
186 fn from_node(node: Node) -> Self {
187 Self {
188 node: Arc::new(node),
189 output: 0,
190 }
191 }
192
193 pub(crate) fn out(&self) -> &Output {
194 &self.node.outputs[self.output as usize]
195 }
196
197 pub(crate) fn error(&self) -> Option<Error> {
199 match self.out().slot.get() {
200 Some(Err(e)) => Some(e.clone()),
201 _ => None,
202 }
203 }
204
205 pub fn shape(&self) -> &[usize] {
209 &self.out().shape
210 }
211
212 pub fn ndim(&self) -> usize {
214 self.out().shape.len()
215 }
216
217 pub fn size(&self) -> usize {
219 self.out().size
220 }
221
222 pub fn dtype(&self) -> DType {
224 self.out().dtype
225 }
226
227 pub fn itemsize(&self) -> usize {
229 self.dtype().size()
230 }
231
232 pub fn nbytes(&self) -> usize {
234 self.size() * self.itemsize()
235 }
236
237 pub fn strides(&self) -> &[isize] {
239 &self.out().strides
240 }
241
242 pub fn flags(&self) -> Flags {
244 self.out().flags
245 }
246
247 pub fn stream(&self) -> Stream {
249 self.node.stream
250 }
251
252 pub fn device(&self) -> Device {
254 self.node.stream.device
255 }
256
257 pub fn is_evaluated(&self) -> bool {
259 self.out().slot.get().is_some()
260 }
261
262 pub fn primitive(&self) -> Option<Primitive> {
265 match &*self.node.lock() {
266 State::Pending { primitive, .. } => Some(primitive.clone()),
267 State::Done => None,
268 }
269 }
270
271 pub fn eval(&self) -> Result<()> {
275 crate::eval(std::slice::from_ref(self))
276 }
277
278 pub fn to_vec<T: Element>(&self) -> Result<Vec<T>> {
280 if T::DTYPE != self.dtype() {
281 return Err(Error::DTypeMismatch {
282 op: "to_vec",
283 lhs: self.dtype(),
284 rhs: T::DTYPE,
285 });
286 }
287 self.eval()?;
288 let data = self.data().expect("eval succeeded, so data is set");
289 let out = self.out();
290 if out.size == 0 {
291 Ok(Vec::new())
292 } else if out.flags.row_contiguous || out.size == 1 {
293 let start = data.offset.max(0) as usize * out.dtype.size();
294 Ok(T::read_bytes(
295 &data.buffer.bytes()[start..start + self.nbytes()],
296 ))
297 } else {
298 let gathered = crate::kernels::contiguous(out, data)?;
299 Ok(T::read_bytes(gathered.bytes()))
300 }
301 }
302
303 pub fn item<T: Element>(&self) -> Result<T> {
305 if self.size() != 1 {
306 return Err(Error::InvalidArgument(format!(
307 "item() needs exactly one element, the array has shape {:?}",
308 self.shape()
309 )));
310 }
311 if T::DTYPE != self.dtype() {
312 return Err(Error::DTypeMismatch {
313 op: "item",
314 lhs: self.dtype(),
315 rhs: T::DTYPE,
316 });
317 }
318 Ok(self.to_vec::<T>()?[0])
319 }
320
321 pub(crate) fn data(&self) -> Option<&Data> {
323 self.out().slot.get().and_then(|r| r.as_ref().ok())
324 }
325}
326
327impl fmt::Debug for Array {
328 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329 let status = match self.out().slot.get() {
330 None => "unevaluated",
331 Some(Ok(_)) => "evaluated",
332 Some(Err(_)) => "failed",
333 };
334 f.debug_struct("Array")
335 .field("shape", &self.shape())
336 .field("dtype", &self.dtype())
337 .field("device", &self.device())
338 .field("status", &status)
339 .finish()
340 }
341}
342
343impl Drop for Node {
347 fn drop(&mut self) {
348 let mut stack = take_inputs(self);
349 while let Some(Array { node, .. }) = stack.pop() {
350 if let Some(mut node) = Arc::into_inner(node) {
351 stack.append(&mut take_inputs(&mut node));
352 }
353 }
354 }
355}
356
357fn take_inputs(node: &mut Node) -> Vec<Array> {
358 let state = node.state.get_mut().unwrap_or_else(PoisonError::into_inner);
359 match std::mem::replace(state, State::Done) {
360 State::Pending { inputs, .. } => inputs,
361 State::Done => Vec::new(),
362 }
363}
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368
369 #[test]
370 fn metadata_without_eval() {
371 let a = Array::from_slice(&[1i32, 2, 3, 4, 5, 6], &[2, 3]).unwrap();
372 let c = crate::add(&a, &a).unwrap();
373 assert!(!c.is_evaluated());
374 assert_eq!(c.shape(), &[2, 3]);
375 assert_eq!(c.ndim(), 2);
376 assert_eq!(c.size(), 6);
377 assert_eq!(c.dtype(), DType::I32);
378 assert_eq!(c.itemsize(), 4);
379 assert_eq!(c.nbytes(), 24);
380 assert_eq!(c.strides(), &[3, 1]);
381 assert!(c.flags().row_contiguous);
382 assert_eq!(c.device(), Device::cpu());
383 assert_eq!(c.primitive(), Some(Primitive::Binary(crate::BinaryOp::Add)));
384 assert!(!c.is_evaluated(), "metadata queries must not evaluate");
385 }
386
387 #[test]
388 fn item_on_scalar_and_non_scalar() {
389 assert_eq!(Array::scalar(7u16).item::<u16>().unwrap(), 7);
390 let a = Array::from_slice(&[1.0f32, 2.0], &[2]).unwrap();
391 assert!(matches!(a.item::<f32>(), Err(Error::InvalidArgument(_))));
392 assert!(matches!(
393 Array::scalar(1.0f32).item::<f64>(),
394 Err(Error::DTypeMismatch { .. })
395 ));
396 }
397
398 #[test]
399 fn debug_does_not_evaluate() {
400 let a = Array::scalar(1i8);
401 let c = crate::add(&a, &a).unwrap();
402 assert!(format!("{c:?}").contains("unevaluated"));
403 assert!(!c.is_evaluated());
404 }
405}