1use std::collections::{HashMap, HashSet};
4use std::fmt;
5use std::hash::{BuildHasherDefault, Hasher};
6use std::panic::Location;
7use std::sync::{Arc, Condvar, Mutex, MutexGuard, OnceLock, PoisonError};
8use std::thread::ThreadId;
9
10use smallvec::SmallVec;
11
12use crate::buffer::Buffer;
13use crate::primitive::Primitive;
14use crate::shape::{self, Flags, Shape, Strides};
15use crate::transforms::TraceId;
16use crate::{DType, Device, Element, Error, Result, Stream, default_stream};
17
18#[derive(Clone)]
29#[must_use = "arrays are lazy: an unused result computes nothing (and may hold an error)"]
30pub struct Array {
31 pub(crate) node: Arc<Node>,
32 pub(crate) output: u32,
33}
34
35pub(crate) struct Node {
37 pub(crate) stream: Stream,
38 pub(crate) trace: Option<TraceId>,
41 pub(crate) outputs: SmallVec<[Output; 1]>,
42 pub(crate) state: Mutex<State>,
43 pub(crate) failed: bool,
46}
47
48pub(crate) struct Output {
50 pub(crate) dtype: DType,
51 pub(crate) shape: Shape,
52 pub(crate) ishape: SmallVec<[i32; 4]>,
54 pub(crate) strides: Strides,
55 pub(crate) flags: Flags,
56 pub(crate) size: usize,
57 pub(crate) slot: OnceLock<Result<Data>>,
59 pub(crate) inflight: Arc<InFlight>,
64}
65
66#[derive(Default)]
70pub(crate) struct InFlight {
71 state: Mutex<InFlightState>,
72 chunk: OnceLock<ChunkStatus>,
75}
76
77pub(crate) type ChunkStatus = Arc<OnceLock<Error>>;
79
80#[derive(Default)]
81pub(crate) enum InFlightState {
82 #[default]
83 Unset,
84 #[cfg_attr(not(all(target_os = "macos", not(miri))), allow(dead_code))]
85 Set(Data),
86 Donated,
88}
89
90impl InFlight {
91 pub(crate) fn lock(&self) -> MutexGuard<'_, InFlightState> {
92 self.state.lock().unwrap_or_else(PoisonError::into_inner)
93 }
94
95 #[cfg_attr(not(all(target_os = "macos", not(miri))), allow(dead_code))]
97 pub(crate) fn set_from(&self, data: Data, chunk: &ChunkStatus) {
98 let _ = self.chunk.set(chunk.clone());
99 self.set(data);
100 }
101
102 #[cfg_attr(not(all(target_os = "macos", not(miri))), allow(dead_code))]
104 pub(crate) fn chunk(&self) -> Option<ChunkStatus> {
105 self.chunk.get().cloned()
106 }
107
108 #[cfg_attr(not(all(target_os = "macos", not(miri))), allow(dead_code))]
110 pub(crate) fn set(&self, data: Data) {
111 let mut state = self.lock();
112 if matches!(*state, InFlightState::Unset) {
113 *state = InFlightState::Set(data);
114 }
115 }
116
117 pub(crate) fn get(&self) -> Option<Data> {
119 match &*self.lock() {
120 InFlightState::Set(data) => Some(data.clone()),
121 _ => None,
122 }
123 }
124}
125
126#[derive(Clone)]
129pub(crate) struct Data {
130 pub(crate) buffer: Arc<Buffer>,
131 pub(crate) offset: isize,
133}
134
135impl Data {
136 pub(crate) fn new(buffer: Buffer) -> Self {
137 Self {
138 buffer: Arc::new(buffer),
139 offset: 0,
140 }
141 }
142}
143
144pub(crate) enum State {
151 Pending {
152 primitive: Primitive,
153 inputs: Vec<Array>,
154 },
155 Running {
159 owner: ThreadId,
160 done: Arc<Completion>,
161 },
162 Retained {
163 primitive: Primitive,
164 inputs: Vec<Array>,
165 },
166 Done,
167 Failed {
169 error: Error,
170 },
171}
172
173#[derive(Default)]
175pub(crate) struct Completion {
176 done: Mutex<bool>,
177 cv: Condvar,
178}
179
180impl Completion {
181 pub(crate) fn finish(&self) {
183 *self.done.lock().unwrap_or_else(PoisonError::into_inner) = true;
184 self.cv.notify_all();
185 }
186
187 pub(crate) fn wait(&self) {
189 let mut done = self.done.lock().unwrap_or_else(PoisonError::into_inner);
190 while !*done {
191 done = self.cv.wait(done).unwrap_or_else(PoisonError::into_inner);
192 }
193 }
194}
195
196#[derive(Default, Clone, Copy)]
199pub(crate) struct NodeHasher(u64);
200
201impl Hasher for NodeHasher {
202 fn finish(&self) -> u64 {
203 self.0.rotate_left(26)
206 }
207 fn write(&mut self, bytes: &[u8]) {
208 for &b in bytes {
209 self.write_u64(u64::from(b));
210 }
211 }
212 fn write_u64(&mut self, n: u64) {
213 self.0 = (self.0.rotate_left(5) ^ n).wrapping_mul(0x9E37_79B9_7F4A_7C15);
214 }
215 fn write_u32(&mut self, n: u32) {
216 self.write_u64(u64::from(n));
217 }
218 fn write_usize(&mut self, n: usize) {
219 self.write_u64(n as u64);
220 }
221}
222
223pub(crate) type NodeSet = HashSet<*const Node, BuildHasherDefault<NodeHasher>>;
225pub(crate) type NodeMap<K, V> = HashMap<K, V, BuildHasherDefault<NodeHasher>>;
227
228const _: () = {
229 const fn assert_send_sync<T: Send + Sync>() {}
230 assert_send_sync::<Array>();
231};
232
233impl Node {
234 pub(crate) fn lock(&self) -> MutexGuard<'_, State> {
235 self.state.lock().unwrap_or_else(PoisonError::into_inner)
236 }
237
238 pub(crate) fn is_tracer(&self) -> bool {
240 self.trace.is_some_and(crate::transforms::is_live)
241 }
242}
243
244impl Output {
245 #[cold]
247 fn invalid(shape: &[usize], dtype: DType) -> Error {
248 if shape.iter().any(|&d| i32::try_from(d).is_err()) {
249 Error::invalid_shape("shape", shape.iter().map(|&d| d as i64))
250 } else {
251 shape::checked_nbytes(shape, dtype.size())
252 .err()
253 .unwrap_or_else(|| Error::invalid_shape("shape", shape.iter().map(|&d| d as i64)))
254 }
255 }
256
257 pub(crate) fn new(
258 dtype: DType,
259 shape: Shape,
260 strides: Option<Strides>,
261 slot: OnceLock<Result<Data>>,
262 ) -> Result<Self> {
263 let mut size = 1usize;
266 let mut ishape = SmallVec::<[i32; 4]>::new();
267 for &d in &shape {
268 let (Some(n), Ok(di)) = (size.checked_mul(d), i32::try_from(d)) else {
269 return Err(Self::invalid(&shape, dtype));
270 };
271 size = n;
272 ishape.push(di);
273 }
274 if size
275 .checked_mul(dtype.size())
276 .is_none_or(|n| n > isize::MAX as usize)
277 {
278 return Err(Self::invalid(&shape, dtype));
279 }
280 let strides = strides.unwrap_or_else(|| shape::row_contiguous_strides(&shape));
281 let flags = shape::flags_for(&shape, &strides);
282 Ok(Self {
283 dtype,
284 shape,
285 ishape,
286 strides,
287 flags,
288 size,
289 slot,
290 inflight: Arc::default(),
291 })
292 }
293}
294
295impl Array {
296 pub(crate) fn from_buffer(buffer: Buffer, dtype: DType, shape: &[usize]) -> Result<Self> {
300 Self::leaf(Ok(Data::new(buffer)), dtype, shape.into())
301 }
302
303 pub(crate) fn leaf(data: Result<Data>, dtype: DType, shape: Shape) -> Result<Self> {
305 let output = Output::new(dtype, shape, None, OnceLock::from(data))?;
306 Ok(Self::from_node(Node {
307 stream: default_stream(),
308 trace: None,
309 outputs: SmallVec::from_buf([output]),
310 state: Mutex::new(State::Done),
311 failed: false,
312 }))
313 }
314
315 #[cold]
318 pub(crate) fn failed(error: Error, dtype: DType, shape: &[usize]) -> Self {
319 let output = Output::new(
320 dtype,
321 shape.into(),
322 None,
323 OnceLock::from(Err(error.clone())),
324 )
325 .unwrap_or_else(|_| {
326 Output::new(dtype, Shape::new(), None, OnceLock::from(Err(error)))
327 .unwrap_or_else(|_| unreachable!("a 0-D output is always valid"))
328 });
329 Self::from_node(Node {
330 stream: default_stream(),
331 trace: None,
332 outputs: SmallVec::from_buf([output]),
333 state: Mutex::new(State::Done),
334 failed: true,
335 })
336 }
337
338 #[inline]
344 pub(crate) fn sticky(
345 op: &'static str,
346 at: &'static Location<'static>,
347 inputs: &[&Array],
348 f: impl FnOnce() -> Result<Array>,
349 ) -> Array {
350 match f() {
351 Ok(out) => out,
352 Err(error) => Self::failed_like(Self::sticky_error(op, at, inputs, error), inputs),
353 }
354 }
355
356 #[inline]
358 pub(crate) fn sticky_n<const N: usize>(
359 op: &'static str,
360 at: &'static Location<'static>,
361 inputs: &[&Array],
362 f: impl FnOnce() -> Result<[Array; N]>,
363 ) -> [Array; N] {
364 match f() {
365 Ok(out) => out,
366 Err(error) => {
367 let error = Self::sticky_error(op, at, inputs, error);
368 std::array::from_fn(|_| Self::failed_like(error.clone(), inputs))
369 }
370 }
371 }
372
373 #[inline]
376 pub(crate) fn sticky_vec(
377 op: &'static str,
378 at: &'static Location<'static>,
379 inputs: &[&Array],
380 count: usize,
381 f: impl FnOnce() -> Result<Vec<Array>>,
382 ) -> Vec<Array> {
383 match f() {
384 Ok(out) => out,
385 Err(error) => {
386 let error = Self::sticky_error(op, at, inputs, error);
387 (0..count)
388 .map(|_| Self::failed_like(error.clone(), inputs))
389 .collect()
390 }
391 }
392 }
393
394 #[cold]
397 fn sticky_error(
398 op: &'static str,
399 at: &'static Location<'static>,
400 inputs: &[&Array],
401 error: Error,
402 ) -> Error {
403 inputs
404 .iter()
405 .find_map(|a| a.slot_error())
406 .unwrap_or_else(|| error.locate(op, at))
407 }
408
409 #[cold]
411 pub(crate) fn failed_like(error: Error, inputs: &[&Array]) -> Self {
412 match inputs.first() {
413 Some(a) => Self::failed(error, a.dtype(), a.dims()),
414 None => Self::failed(error, DType::F32, &[]),
415 }
416 }
417
418 pub(crate) fn from_op(primitive: Primitive, inputs: Vec<Array>) -> Result<Vec<Array>> {
422 let trace = crate::transforms::inherited(&inputs);
423 Self::record(primitive, inputs, trace)
424 }
425
426 pub(crate) fn record(
433 primitive: Primitive,
434 inputs: Vec<Array>,
435 trace: Option<TraceId>,
436 ) -> Result<Vec<Array>> {
437 if let Some(err) = inputs.iter().find_map(Array::slot_error) {
438 return Self::record_failed(&primitive, &inputs, err);
439 }
440 Self::record_unchecked(primitive, inputs, trace)
441 }
442
443 #[cold]
444 fn record_failed(primitive: &Primitive, inputs: &[Array], err: Error) -> Result<Vec<Array>> {
445 let specs = primitive.output_specs(inputs).map_err(|_| err.clone())?;
446 Ok(specs
447 .into_iter()
448 .map(|spec| Self::failed(err.clone(), spec.dtype, &spec.shape))
449 .collect())
450 }
451
452 pub(crate) fn record_unchecked(
455 primitive: Primitive,
456 inputs: Vec<Array>,
457 trace: Option<TraceId>,
458 ) -> Result<Vec<Array>> {
459 let outputs = primitive
460 .output_specs(&inputs)?
461 .into_iter()
462 .map(|spec| Output::new(spec.dtype, spec.shape, spec.strides, OnceLock::new()))
463 .collect::<Result<SmallVec<_>>>()?;
464 let count = outputs.len() as u32;
465 let node = Arc::new(Node {
466 stream: default_stream(),
467 trace,
468 outputs,
469 state: Mutex::new(State::Pending { primitive, inputs }),
470 failed: false,
471 });
472 Ok((0..count)
473 .map(|output| Self {
474 node: node.clone(),
475 output,
476 })
477 .collect())
478 }
479
480 fn from_node(node: Node) -> Self {
481 Self {
482 node: Arc::new(node),
483 output: 0,
484 }
485 }
486
487 pub(crate) fn placeholder(dtype: DType, shape: &[usize]) -> Result<Array> {
493 let failed = Self::failed(
494 Error::InvalidArgument(
495 "arrays cannot be evaluated while a function is traced (compile)".into(),
496 ),
497 dtype,
498 &[],
499 );
500 let primitive = Primitive::Broadcast(shape.to_vec());
501 let trace = Some(crate::transforms::PLACEHOLDER);
502 Ok(Self::record_unchecked(primitive, vec![failed], trace)?.swap_remove(0))
503 }
504
505 pub(crate) fn out(&self) -> &Output {
506 &self.node.outputs[self.output as usize]
507 }
508
509 pub(crate) fn slot_error(&self) -> Option<Error> {
511 match self.out().slot.get() {
512 Some(Err(e)) => Some(e.clone()),
513 _ => None,
514 }
515 }
516
517 pub fn is_failed(&self) -> bool {
523 self.node.failed
524 }
525
526 #[track_caller]
537 pub fn failure(op: &'static str, kind: crate::OpErrorKind) -> Array {
538 Self::failed(
539 Error::op(op, kind).locate(op, Location::caller()),
540 DType::F32,
541 &[],
542 )
543 }
544
545 pub fn error(&self) -> Option<&Error> {
547 match (self.node.failed, self.out().slot.get()) {
548 (true, Some(Err(e))) => Some(e),
549 _ => None,
550 }
551 }
552
553 pub fn check(self) -> Result<Array> {
557 if self.node.failed
558 && let Some(error) = self.slot_error()
559 {
560 return Err(error);
561 }
562 Ok(self)
563 }
564
565 pub fn shape(&self) -> &[i32] {
573 &self.out().ishape
574 }
575
576 #[track_caller]
589 pub fn shape_n<const N: usize>(&self) -> Result<[i32; N]> {
590 if let Some(e) = self.error() {
591 return Err(e.clone());
592 }
593 <[i32; N]>::try_from(self.shape()).map_err(|_| {
594 Error::InvalidArgument(format!(
595 "shape_n: expected {N} dimensions, the array has shape {:?}",
596 self.shape()
597 ))
598 .locate("shape_n", Location::caller())
599 })
600 }
601
602 pub(crate) fn dims(&self) -> &[usize] {
604 &self.out().shape
605 }
606
607 pub fn ndim(&self) -> usize {
609 self.out().shape.len()
610 }
611
612 pub fn size(&self) -> usize {
614 self.out().size
615 }
616
617 pub fn dtype(&self) -> DType {
619 self.out().dtype
620 }
621
622 pub fn itemsize(&self) -> usize {
624 self.dtype().size()
625 }
626
627 pub fn nbytes(&self) -> usize {
629 self.size() * self.itemsize()
630 }
631
632 pub(crate) fn flags(&self) -> Flags {
634 self.out().flags
635 }
636
637 pub(crate) fn strides(&self) -> &[isize] {
639 &self.out().strides
640 }
641
642 pub fn stream(&self) -> Stream {
644 self.node.stream
645 }
646
647 pub fn device(&self) -> Device {
649 self.node.stream.device
650 }
651
652 pub fn is_evaluated(&self) -> bool {
654 self.out().slot.get().is_some()
655 }
656
657 pub fn eval(&self) -> Result<()> {
661 crate::eval(std::slice::from_ref(self))
662 }
663
664 pub fn to_vec<T: Element>(&self) -> Result<Vec<T>> {
666 if let Some(e) = self.error() {
667 return Err(e.clone());
668 }
669 if T::DTYPE != self.dtype() {
670 return Err(Error::dtype_mismatch("to_vec", self.dtype(), T::DTYPE));
671 }
672 self.eval()?;
673 let data = self.data().expect("eval succeeded, so data is set");
674 let out = self.out();
675 if out.size == 0 {
676 Ok(Vec::new())
677 } else if out.flags.row_contiguous || out.size == 1 {
678 let start = data.offset.max(0) as usize * out.dtype.size();
679 Ok(T::read_bytes(
680 &data.buffer.bytes()[start..start + self.nbytes()],
681 ))
682 } else {
683 let gathered = crate::kernels::contiguous(out, data)?;
684 Ok(T::read_bytes(gathered.bytes()))
685 }
686 }
687
688 pub fn item<T: Element>(&self) -> Result<T> {
690 if let Some(e) = self.error() {
691 return Err(e.clone());
692 }
693 if self.size() != 1 {
694 return Err(Error::InvalidArgument(format!(
695 "item() needs exactly one element, the array has shape {:?}",
696 self.dims()
697 )));
698 }
699 if T::DTYPE != self.dtype() {
700 return Err(Error::dtype_mismatch("item", self.dtype(), T::DTYPE));
701 }
702 Ok(self.to_vec::<T>()?[0])
703 }
704
705 pub fn buffer_id(&self) -> Option<usize> {
724 self.data().map(|d| Arc::as_ptr(&d.buffer) as usize)
725 }
726
727 pub(crate) fn data(&self) -> Option<&Data> {
729 self.out().slot.get().and_then(|r| r.as_ref().ok())
730 }
731}
732
733impl fmt::Debug for Array {
736 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
737 let mut d = f.debug_struct("Array");
738 d.field("shape", &self.shape())
739 .field("dtype", &self.dtype())
740 .field("device", &self.device());
741 match self.out().slot.get() {
742 None => d.field("status", &"unevaluated"),
743 Some(Ok(_)) => match crate::display::values(self) {
744 Ok(values) => d.field("values", &format_args!("{values}")),
745 Err(_) => d.field("status", &"evaluated"),
746 },
747 Some(Err(e)) if self.node.failed => d.field("failed", &format_args!("{e}")),
748 Some(Err(e)) => d.field("error", &format_args!("{e}")),
749 };
750 d.finish()
751 }
752}
753
754impl fmt::Display for Array {
758 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
759 match crate::display::values(self) {
760 Ok(values) => write!(f, "array({values}, dtype={})", self.dtype()),
761 Err(e) => write!(f, "<failed: {e}>"),
762 }
763 }
764}
765
766impl Default for Array {
770 fn default() -> Self {
771 crate::creation::from_data(&[0.0f32], &[])
772 .unwrap_or_else(|e| Self::failed(e, DType::F32, &[]))
773 }
774}
775
776impl Drop for Node {
780 fn drop(&mut self) {
781 let mut stack = take_inputs(self);
782 while let Some(Array { node, .. }) = stack.pop() {
783 if let Some(mut node) = Arc::into_inner(node) {
784 stack.append(&mut take_inputs(&mut node));
785 }
786 }
787 }
788}
789
790fn take_inputs(node: &mut Node) -> Vec<Array> {
791 let state = node.state.get_mut().unwrap_or_else(PoisonError::into_inner);
792 match std::mem::replace(state, State::Done) {
793 State::Pending { inputs, .. } | State::Retained { inputs, .. } => inputs,
794 State::Running { .. } | State::Done | State::Failed { .. } => Vec::new(),
795 }
796}
797
798#[cfg(test)]
800impl Array {
801 pub(crate) fn primitive_t(&self) -> Option<Primitive> {
802 crate::graph::primitive(self)
803 }
804 pub(crate) fn inputs_t(&self) -> Vec<Array> {
805 crate::graph::inputs(self)
806 }
807 pub(crate) fn id_t(&self) -> usize {
808 crate::graph::id(self)
809 }
810 pub(crate) fn siblings_t(&self) -> Vec<Array> {
811 crate::graph::siblings(self)
812 }
813 pub(crate) fn output_index_t(&self) -> usize {
814 crate::graph::output_index(self)
815 }
816 pub(crate) fn is_tracer_t(&self) -> bool {
817 crate::graph::is_tracer(self)
818 }
819}
820
821#[cfg(test)]
822mod tests {
823 use super::*;
824 use crate::OpErrorKind;
825
826 #[test]
827 fn graph_accessors_describe_pending_nodes() {
828 let a = Array::from_slice(&[1i32, 2], [2]);
829 let b = Array::from_slice(&[5i32, 7], [2]);
830 let outs =
831 crate::graph::from_primitive(Primitive::AddMultiply, vec![a.clone(), b]).unwrap();
832 let (sum, product) = (&outs[0], &outs[1]);
833 assert_eq!(sum.id_t(), product.id_t(), "siblings share their operation");
834 assert_ne!(sum.id_t(), a.id_t());
835 assert_eq!((sum.output_index_t(), product.output_index_t()), (0, 1));
836 assert_eq!(product.siblings_t().len(), 2);
837 assert_eq!(sum.inputs_t()[0].id_t(), a.id_t());
838 assert!(a.inputs_t().is_empty(), "host data has no inputs");
839 assert!(!sum.is_tracer_t());
840 product.eval().unwrap();
841 assert!(sum.inputs_t().is_empty(), "evaluated: detached");
842
843 let p = Array::placeholder(DType::F32, &[3]).unwrap();
844 let q = &p + 1.0f32;
845 assert!(p.is_tracer_t() && q.is_tracer_t());
846 assert_eq!(q.dims(), [3]);
847 assert!(q.eval().is_err(), "placeholders cannot be evaluated");
848 }
849
850 #[test]
851 fn failed_leaves_are_sticky_and_typed() {
852 let error = Error::size_mismatch("from_slice", 6, 5);
853 let f = Array::failed(error.clone(), DType::F16, &[2, 3]);
854 assert!(f.is_failed());
855 assert_eq!(f.shape(), [2, 3]);
856 assert_eq!(f.dtype(), DType::F16);
857 assert_eq!(f.eval(), Err(error.clone()));
858 assert_eq!(f.clone().check().unwrap_err(), error);
859 let g = crate::ops::imp::reshape(&f, &[3, 2]).unwrap();
861 assert!(g.is_failed() && g.primitive_t().is_none());
862 assert_eq!(g.shape(), [3, 2]);
863 assert_eq!(g.eval(), Err(error));
864 let ok = &Array::from(1.0f32) + 1.0;
866 assert!(!ok.is_failed());
867 let ok = ok.check().unwrap();
868 assert!(!ok.is_evaluated());
869 }
870
871 #[test]
872 fn shapes_are_i32_and_bounded() {
873 let a = crate::ops::imp::zeros(&[2, 3, 4], DType::U8).unwrap();
874 let shape: &[i32] = a.shape();
875 assert_eq!(shape, [2, 3, 4]);
876 assert_eq!(a.shape_n::<3>().unwrap(), [2, 3, 4]);
877 assert!(a.shape_n::<2>().is_err(), "rank mismatch");
878 let big = crate::ops::imp::broadcast_to(&Array::from(0u8), &[1usize << 31]).unwrap_err();
880 assert!(
881 matches!(big.kind(), Some(OpErrorKind::InvalidShape(_))),
882 "{big}"
883 );
884 let fits = crate::ops::imp::broadcast_to(&Array::from(0u8), &[i32::MAX as usize]).unwrap();
885 assert_eq!(fits.shape(), [i32::MAX]);
886 }
887
888 #[test]
889 fn node_hasher_spreads_aligned_addresses() {
890 use std::hash::BuildHasher;
891 let build = BuildHasherDefault::<NodeHasher>::default();
892 let buckets: HashSet<u64> = (0..1024u64)
894 .map(|i| {
895 build.hash_one(std::ptr::without_provenance::<Node>(
896 0x6000_0000_0000 + i as usize * 336,
897 )) & 1023
898 })
899 .collect();
900 assert!(
902 buckets.len() > 600,
903 "only {} of 1024 buckets used",
904 buckets.len()
905 );
906 }
907
908 #[test]
909 fn metadata_without_eval() {
910 let a = Array::from_slice(&[1i32, 2, 3, 4, 5, 6], [2, 3]);
911 let c = crate::ops::imp::add(&a, &a).unwrap();
912 assert!(!c.is_evaluated());
913 assert_eq!(c.dims(), &[2, 3]);
914 assert_eq!(c.ndim(), 2);
915 assert_eq!(c.size(), 6);
916 assert_eq!(c.dtype(), DType::I32);
917 assert_eq!(c.itemsize(), 4);
918 assert_eq!(c.nbytes(), 24);
919 assert_eq!(c.strides(), &[3, 1]);
920 assert!(c.flags().row_contiguous);
921 assert_eq!(c.device(), crate::default_device());
922 assert_eq!(
923 c.primitive_t(),
924 Some(Primitive::Binary(crate::graph::BinaryOp::Add))
925 );
926 assert!(!c.is_evaluated(), "metadata queries must not evaluate");
927 }
928
929 #[test]
930 fn item_on_scalar_and_non_scalar() {
931 assert_eq!(Array::from(7u16).item::<u16>().unwrap(), 7);
932 let a = Array::from_slice(&[1.0f32, 2.0], [2]);
933 assert!(matches!(a.item::<f32>(), Err(Error::InvalidArgument(_))));
934 assert!(matches!(
935 Array::from(1.0f32).item::<f64>(),
936 Err(Error::Op {
937 kind: OpErrorKind::DTypeMismatch { .. },
938 ..
939 })
940 ));
941 }
942
943 #[test]
944 fn debug_does_not_evaluate() {
945 let a = Array::from(1i8);
946 let c = crate::ops::imp::add(&a, &a).unwrap();
947 assert!(format!("{c:?}").contains("unevaluated"));
948 assert!(!c.is_evaluated());
949 }
950}