1use std::ops::{Add, Div, Mul, Neg, Sub};
9use std::panic::Location;
10
11use crate::buffer::Buffer;
12use crate::cast::{Canon, Scalar, with_type};
13use crate::primitive::{ArgReduceOp, BinaryOp, Primitive, ReduceOp, UnaryOp};
14use crate::scalar::{ScalarOperand, ScalarOrArray, resolve};
15use crate::shape::{self, Shape};
16use crate::view::{self, SliceArg};
17use crate::{
18 Array, Category, Complex64, DType, Error, Result, at_least_float, bf16, f16, promote_types,
19};
20
21fn single(primitive: Primitive, inputs: Vec<Array>) -> Result<Array> {
22 let mut outputs = Array::from_op(primitive, inputs)?;
23 Ok(outputs.swap_remove(0))
24}
25
26pub(crate) fn binary(op: BinaryOp, a: &Array, b: &Array) -> Result<Array> {
29 let mut dtype = promote_types(a.dtype(), b.dtype());
30 if matches!(op, BinaryOp::Divide | BinaryOp::LogAddExp) {
31 dtype = at_least_float(dtype);
32 }
33 let ordered = matches!(
34 op,
35 BinaryOp::Maximum
36 | BinaryOp::Minimum
37 | BinaryOp::LogAddExp
38 | BinaryOp::FloorDivide
39 | BinaryOp::Power
40 | BinaryOp::Less
41 | BinaryOp::LessEqual
42 | BinaryOp::Greater
43 | BinaryOp::GreaterEqual
44 );
45 if ordered && dtype == DType::Complex64 {
46 return Err(Error::unsupported_dtype(op.name(), dtype));
47 }
48 let shape = view::broadcast_shapes(op.name(), a.dims(), b.dims())?;
49 let a = broadcast_to(&astype(a, dtype)?, &shape)?;
50 let b = broadcast_to(&astype(b, dtype)?, &shape)?;
51 single(Primitive::Binary(op), vec![a, b])
52}
53
54macro_rules! binary_fn {
55 ($($(#[$doc:meta])* $name:ident => $op:ident;)*) => {$(
56 $(#[$doc])*
57 pub(crate) fn $name(a: impl Into<ScalarOrArray>, b: impl Into<ScalarOrArray>) -> Result<Array> {
58 let (a, b) = resolve(a.into(), b.into())?;
59 binary(BinaryOp::$op, &a, &b)
60 }
61 )*};
62}
63
64binary_fn! {
67 add => Add;
70 subtract => Subtract;
73 multiply => Multiply;
76 divide => Divide;
78 maximum => Maximum;
80 minimum => Minimum;
82 equal => Equal;
84 not_equal => NotEqual;
86 less => Less;
88 less_equal => LessEqual;
90 greater => Greater;
92 greater_equal => GreaterEqual;
94}
95
96pub(crate) fn unary(op: UnaryOp, a: &Array) -> Result<Array> {
99 let dtype = if op.is_transcendental() {
100 at_least_float(a.dtype())
101 } else {
102 a.dtype()
103 };
104 let unsupported = match op {
105 UnaryOp::Negative => dtype == DType::Bool,
106 UnaryOp::Abs | UnaryOp::Conjugate => false,
107 _ => dtype == DType::Complex64,
108 };
109 if unsupported {
110 return Err(Error::unsupported_dtype(op.name(), dtype));
111 }
112 single(Primitive::Unary(op), vec![astype(a, dtype)?])
113}
114
115pub(crate) fn conjugate(a: &Array) -> Result<Array> {
117 if a.dtype() != DType::Complex64 {
118 return Ok(a.clone());
119 }
120 unary(UnaryOp::Conjugate, a)
121}
122
123pub(crate) fn sign(a: &Array) -> Result<Array> {
127 let dtype = a.dtype();
128 match dtype.category() {
129 Category::Complex => Err(Error::unsupported_dtype("sign", dtype)),
130 Category::Bool | Category::Unsigned => astype(&greater(a, 0)?, dtype),
131 _ => subtract(
132 astype(&greater(a, 0)?, dtype)?,
133 astype(&less(a, 0)?, dtype)?,
134 ),
135 }
136}
137
138macro_rules! unary_fn {
139 ($($(#[$doc:meta])* $name:ident => $op:ident;)*) => {$(
140 $(#[$doc])*
141 pub(crate) fn $name(a: &Array) -> Result<Array> {
142 unary(UnaryOp::$op, a)
143 }
144 )*};
145}
146
147unary_fn! {
148 negative => Negative;
150 abs => Abs;
152 exp => Exp;
154 log => Log;
156 sqrt => Sqrt;
158 sin => Sin;
160 cos => Cos;
162 tanh => Tanh;
164 sigmoid => Sigmoid;
166 erfinv => ErfInv;
168}
169
170fn rows_on_gpu(a: &Array) -> bool {
172 let axis = a.dims().last().copied().unwrap_or(1);
173 crate::fast::imp::rows_on_gpu(a.dtype(), a.size(), axis)
174}
175
176pub(crate) fn logsumexp(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
180 let a = astype(a, at_least_float(a.dtype()))?;
181 if last_axis_only(&a, axes) && rows_on_gpu(&a) {
182 let out = Array::from_op(
183 Primitive::Fast(crate::fast::imp::FastOp::LogSumExp),
184 vec![a],
185 )?;
186 let out = out.into_iter().next().expect("one output");
187 return if keepdims {
188 Ok(out)
189 } else {
190 squeeze(&out, axes)
191 };
192 }
193 let maxval = stop_gradient(&max(&a, axes, true)?)?;
194 let out = log(&sum(&exp(&subtract(&a, &maxval)?)?, axes, true)?)?;
195 let out = add(&out, &maxval)?;
196 let out = where_(equal(&abs(&maxval)?, f64::INFINITY)?, &maxval, &out)?;
197 if keepdims {
198 Ok(out)
199 } else {
200 squeeze(&out, axes)
201 }
202}
203
204pub(crate) fn softmax(a: &Array, axes: &[isize]) -> Result<Array> {
207 let a = astype(a, at_least_float(a.dtype()))?;
208 if last_axis_only(&a, axes) && rows_on_gpu(&a) {
209 return single(Primitive::Fast(crate::fast::imp::FastOp::Softmax), vec![a]);
210 }
211 let wide = match a.dtype() {
213 DType::F16 | DType::BF16 => astype(&a, DType::F32)?,
214 _ => a.clone(),
215 };
216 let maxval = stop_gradient(&max(&wide, axes, true)?)?;
217 let ex = exp(&subtract(&wide, &maxval)?)?;
218 astype(÷(&ex, sum(&ex, axes, true)?)?, a.dtype())
219}
220
221fn last_axis_only(a: &Array, axes: &[isize]) -> bool {
224 let n = a.ndim() as isize;
225 let all: Vec<isize> = if axes.is_empty() {
226 (0..n).collect()
227 } else {
228 axes.to_vec()
229 };
230 n > 0
231 && a.dtype() != DType::Complex64
232 && all.iter().all(|&ax| (-n..n).contains(&ax))
233 && all.iter().any(|&ax| ax.rem_euclid(n) == n - 1)
234 && all
235 .iter()
236 .all(|&ax| ax.rem_euclid(n) == n - 1 || a.dims()[ax.rem_euclid(n) as usize] == 1)
237}
238
239pub(crate) fn where_(
245 condition: impl Into<ScalarOrArray>,
246 a: impl Into<ScalarOrArray>,
247 b: impl Into<ScalarOrArray>,
248) -> Result<Array> {
249 let (a, b) = resolve(a.into(), b.into())?;
250 let condition = condition.into().into_array(None)?;
251 let (condition, a, b) = (&condition, &a, &b);
252 let dtype = promote_types(a.dtype(), b.dtype());
253 let shape = view::broadcast_shapes("where", condition.dims(), a.dims())?;
254 let shape = view::broadcast_shapes("where", &shape, b.dims())?;
255 let condition = broadcast_to(&astype(condition, DType::Bool)?, &shape)?;
256 let a = broadcast_to(&astype(a, dtype)?, &shape)?;
257 let b = broadcast_to(&astype(b, dtype)?, &shape)?;
258 single(Primitive::Select, vec![condition, a, b])
259}
260
261pub(crate) fn astype(a: &Array, dtype: DType) -> Result<Array> {
264 if a.dtype() == dtype {
265 return Ok(a.clone());
266 }
267 single(Primitive::AsType(dtype), vec![a.clone()])
268}
269
270pub(crate) fn reshape(a: &Array, shape: &[isize]) -> Result<Array> {
275 let shape = view::infer_shape(a.size(), shape)?;
276 reshape_to(a, shape)
277}
278
279pub(crate) fn reshape_to(a: &Array, shape: Shape) -> Result<Array> {
280 if a.dims() == shape.as_slice() {
281 return Ok(a.clone());
282 }
283 single(Primitive::Reshape(shape.to_vec()), vec![a.clone()])
284}
285
286pub(crate) fn transpose(a: &Array, axes: &[usize]) -> Result<Array> {
288 view::permute(a.dims(), a.strides(), axes)?;
289 if axes.iter().enumerate().all(|(i, &axis)| i == axis) {
290 return Ok(a.clone());
291 }
292 single(Primitive::Transpose(axes.to_vec()), vec![a.clone()])
293}
294
295pub(crate) fn slice(a: &Array, args: &[SliceArg]) -> Result<Array> {
298 let dims = view::normalize_slice(a.dims(), args)?;
299 let full = dims
300 .iter()
301 .zip(a.dims())
302 .all(|(d, &n)| d.start == 0 && d.step == 1 && d.len == n);
303 if full {
304 return Ok(a.clone());
305 }
306 let primitive = Primitive::Slice {
307 start: dims.iter().map(|d| d.start).collect(),
308 step: dims.iter().map(|d| d.step).collect(),
309 len: dims.iter().map(|d| d.len).collect(),
310 };
311 single(primitive, vec![a.clone()])
312}
313
314pub(crate) fn slice_update(a: &Array, update: &Array, args: &[SliceArg]) -> Result<Array> {
319 let dims = view::normalize_slice(a.dims(), args)?;
320 let len: Vec<usize> = dims.iter().map(|d| d.len).collect();
321 let update = broadcast_to(&astype(update, a.dtype())?, &len)?;
322 single(
323 Primitive::SliceUpdate {
324 start: dims.iter().map(|d| d.start).collect(),
325 step: dims.iter().map(|d| d.step).collect(),
326 len,
327 },
328 vec![a.clone(), update],
329 )
330}
331
332pub(crate) fn stop_gradient(a: &Array) -> Result<Array> {
334 single(Primitive::StopGradient, vec![a.clone()])
335}
336
337pub(crate) fn broadcast_to(a: &Array, shape: &[usize]) -> Result<Array> {
339 if a.dims() == shape {
340 return Ok(a.clone());
341 }
342 single(Primitive::Broadcast(shape.to_vec()), vec![a.clone()])
343}
344
345pub(crate) fn expand_dims(a: &Array, axis: isize) -> Result<Array> {
348 let axis = view::normalize_axis(axis, a.ndim() + 1)?;
349 single(Primitive::ExpandDims(vec![axis]), vec![a.clone()])
350}
351
352pub(crate) fn expand_dims_at(a: &Array, axes: Vec<usize>) -> Result<Array> {
354 if axes.is_empty() {
355 return Ok(a.clone());
356 }
357 single(Primitive::ExpandDims(axes), vec![a.clone()])
358}
359
360pub(crate) fn squeeze(a: &Array, axes: &[isize]) -> Result<Array> {
363 let mut remove = vec![axes.is_empty(); a.ndim()];
364 for &axis in axes {
365 let i = view::normalize_axis(axis, a.ndim())?;
366 if a.dims()[i] != 1 {
367 return Err(Error::InvalidArgument(format!(
368 "cannot squeeze axis {axis} of size {} in shape {:?}",
369 a.dims()[i],
370 a.dims()
371 )));
372 }
373 remove[i] = true;
374 }
375 let axes: Vec<usize> = (0..a.ndim())
376 .filter(|&i| remove[i] && a.dims()[i] == 1)
377 .collect();
378 if axes.is_empty() {
379 return Ok(a.clone());
380 }
381 single(Primitive::Squeeze(axes), vec![a.clone()])
382}
383
384fn normalize_axes(a: &Array, axes: &[isize]) -> Result<Vec<usize>> {
388 if axes.is_empty() {
389 return Ok((0..a.ndim()).collect());
390 }
391 let mut out = axes
392 .iter()
393 .map(|&axis| view::normalize_axis(axis, a.ndim()))
394 .collect::<Result<Vec<_>>>()?;
395 out.sort_unstable();
396 if let Some(w) = out.windows(2).find(|w| w[0] == w[1]) {
397 return Err(Error::invalid_axis("axes", w[0] as i64, a.ndim()));
398 }
399 Ok(out)
400}
401
402fn finish_reduction(out: Array, axes: &[usize], keepdims: bool) -> Result<Array> {
404 if keepdims || axes.is_empty() {
405 return Ok(out);
406 }
407 let mut axes = axes.to_vec();
408 axes.sort_unstable();
409 axes.dedup();
410 single(Primitive::Squeeze(axes), vec![out])
411}
412
413fn reduction(
414 op: ReduceOp,
415 a: &Array,
416 axes: &[isize],
417 keepdims: bool,
418 dtype: DType,
419) -> Result<Array> {
420 let axes = normalize_axes(a, axes)?;
421 if matches!(op, ReduceOp::Max | ReduceOp::Min) {
422 if a.dtype() == DType::Complex64 {
423 return Err(Error::unsupported_dtype(op.name(), a.dtype()));
424 }
425 if axes.iter().any(|&d| a.dims()[d] == 0) {
426 return Err(Error::InvalidArgument(format!(
427 "{}: cannot reduce a zero-length axis (no identity)",
428 op.name()
429 )));
430 }
431 }
432 let a = astype(a, dtype)?;
433 if axes.is_empty() {
434 return Ok(a); }
436 let out = single(
437 Primitive::Reduce {
438 op,
439 axes: axes.clone(),
440 },
441 vec![a],
442 )?;
443 finish_reduction(out, &axes, keepdims)
444}
445
446pub(crate) fn widened(dtype: DType) -> DType {
448 match (dtype.category(), dtype.size()) {
449 (Category::Bool, _) => DType::I32,
450 (Category::Signed, 1..=4) => DType::I32,
451 (Category::Signed, _) => DType::I64,
452 (Category::Unsigned, 1..=4) => DType::U32,
453 (Category::Unsigned, _) => DType::U64,
454 _ => dtype,
455 }
456}
457
458pub(crate) fn sum(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
461 reduction(ReduceOp::Sum, a, axes, keepdims, widened(a.dtype()))
462}
463
464pub(crate) fn prod(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
466 reduction(ReduceOp::Prod, a, axes, keepdims, widened(a.dtype()))
467}
468
469pub(crate) fn max(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
471 reduction(ReduceOp::Max, a, axes, keepdims, a.dtype())
472}
473
474pub(crate) fn min(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
476 reduction(ReduceOp::Min, a, axes, keepdims, a.dtype())
477}
478
479pub(crate) fn all(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
481 reduction(ReduceOp::And, a, axes, keepdims, DType::Bool)
482}
483
484pub(crate) fn any(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
486 reduction(ReduceOp::Or, a, axes, keepdims, DType::Bool)
487}
488
489pub(crate) fn mean(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
491 let dtype = at_least_float(a.dtype());
492 let count: usize = normalize_axes(a, axes)?
493 .iter()
494 .map(|&d| a.dims()[d])
495 .product();
496 let scale = astype(&Array::from(1.0 / count as f64), dtype)?;
497 multiply(&sum(a, axes, keepdims)?, &scale)
498}
499
500fn arg_reduction(op: ArgReduceOp, a: &Array, axis: Option<isize>, keepdims: bool) -> Result<Array> {
501 if a.dtype() == DType::Complex64 {
502 return Err(Error::unsupported_dtype(op.name(), a.dtype()));
503 }
504 let Some(axis) = axis else {
505 let flat = arg_reduction(op, &reshape(a, &[-1])?, Some(0), false)?;
508 return if keepdims {
509 reshape_to(&flat, smallvec::smallvec![1; a.ndim()])
510 } else {
511 Ok(flat)
512 };
513 };
514 let axis = view::normalize_axis(axis, a.ndim())?;
515 if a.dims()[axis] == 0 {
516 return Err(Error::InvalidArgument(format!(
517 "{}: cannot reduce a zero-length axis",
518 op.name()
519 )));
520 }
521 let out = single(Primitive::ArgReduce { op, axis }, vec![a.clone()])?;
522 finish_reduction(out, &[axis], keepdims)
523}
524
525pub(crate) fn argmax(a: &Array, axis: Option<isize>, keepdims: bool) -> Result<Array> {
528 arg_reduction(ArgReduceOp::Max, a, axis, keepdims)
529}
530
531pub(crate) fn argmin(a: &Array, axis: Option<isize>, keepdims: bool) -> Result<Array> {
534 arg_reduction(ArgReduceOp::Min, a, axis, keepdims)
535}
536
537pub(crate) fn matmul(a: &Array, b: &Array) -> Result<Array> {
544 product(a, b, None)
545}
546
547pub(crate) fn addmm(c: &Array, a: &Array, b: &Array, alpha: f32, beta: f32) -> Result<Array> {
551 product(a, b, Some((c, alpha, beta)))
552}
553
554fn product(a: &Array, b: &Array, c: Option<(&Array, f32, f32)>) -> Result<Array> {
557 let op = if c.is_some() { "addmm" } else { "matmul" };
558 if a.ndim() == 0 || b.ndim() == 0 {
559 return Err(Error::InvalidArgument(format!(
560 "{op}: operands need at least one dimension"
561 )));
562 }
563 let mut dtype = promote_types(a.dtype(), b.dtype());
564 if let Some((c, ..)) = c {
565 dtype = promote_types(dtype, c.dtype());
566 }
567 if !matches!(dtype.category(), Category::Float | Category::Complex) {
568 return Err(Error::unsupported_dtype(op, dtype));
569 }
570 let a2 = if a.ndim() == 1 {
571 expand_dims(a, 0)?
572 } else {
573 a.clone()
574 };
575 let b2 = if b.ndim() == 1 {
576 expand_dims(b, -1)?
577 } else {
578 b.clone()
579 };
580 let (sa, sb) = (a2.dims(), b2.dims());
581 if sa[sa.len() - 1] != sb[sb.len() - 2] {
582 return Err(Error::shape_mismatch(op, a.dims(), b.dims()));
583 }
584 let (a2, b2) = (astype(&a2, dtype)?, astype(&b2, dtype)?);
585 let (na, nb) = (a2.ndim(), b2.ndim());
586 let batch = view::broadcast_shapes(op, &a2.dims()[..na - 2], &b2.dims()[..nb - 2])?;
587 let (m, n) = (a2.dims()[na - 2], b2.dims()[nb - 1]);
588 let mut expanded: Shape = batch.clone();
589 expanded.extend_from_slice(&[m, n]);
590 let mut drop = Vec::new();
592 if a.ndim() == 1 {
593 drop.push(expanded.len() as isize - 2);
594 }
595 if b.ndim() == 1 {
596 drop.push(expanded.len() as isize - 1);
597 }
598 let c_full = match c {
600 Some((c, alpha, beta)) => {
601 let final_shape: Vec<usize> = expanded
602 .iter()
603 .enumerate()
604 .filter(|(i, _)| !drop.contains(&(*i as isize)))
605 .map(|(_, &d)| d)
606 .collect();
607 let mut cc = broadcast_to(&astype(c, dtype)?, &final_shape)?;
608 for &d in &drop {
609 cc = expand_dims(&cc, d)?;
610 }
611 Some((broadcast_to(&cc, &expanded)?, alpha, beta))
612 }
613 None => None,
614 };
615 let record = |x: Array, y: Array, z: Option<(Array, f32, f32)>| match z {
616 None => single(Primitive::Matmul, vec![x, y]),
617 Some((z, alpha, beta)) => single(
618 Primitive::AddMM {
619 alpha: alpha.to_bits(),
620 beta: beta.to_bits(),
621 },
622 vec![x, y, z],
623 ),
624 };
625 let out = if na > 2 && nb == 2 {
626 let k = a2.dims()[na - 1];
628 let rows = a2
629 .size()
630 .checked_div(k)
631 .unwrap_or_else(|| a2.dims()[..na - 1].iter().product());
632 let flat_c = match c_full {
633 Some((z, alpha, beta)) => {
634 Some((reshape_to(&z, smallvec::smallvec![rows, n])?, alpha, beta))
635 }
636 None => None,
637 };
638 let flat = record(
639 reshape_to(&a2, smallvec::smallvec![rows, k])?,
640 b2.clone(),
641 flat_c,
642 )?;
643 reshape_to(&flat, expanded)?
644 } else {
645 let full = |x: &Array, nd: usize| -> Result<Array> {
646 let mut shape = batch.clone();
647 shape.extend_from_slice(&x.dims()[nd - 2..]);
648 broadcast_to(x, &shape)
649 };
650 record(full(&a2, na)?, full(&b2, nb)?, c_full)?
651 };
652 if drop.is_empty() {
653 Ok(out)
654 } else {
655 squeeze(&out, &drop)
656 }
657}
658
659pub(crate) fn zeros(shape: &[usize], dtype: DType) -> Result<Array> {
663 let buffer = Buffer::try_zeroed(shape::checked_nbytes(shape, dtype.size())?)?;
664 Array::from_buffer(buffer, dtype, shape)
665}
666
667pub(crate) fn ones(shape: &[usize], dtype: DType) -> Result<Array> {
669 filled(shape, dtype, |_| Canon::Int(1))
670}
671
672pub(crate) fn arange(start: f64, stop: f64, step: f64, dtype: DType) -> Result<Array> {
675 let invalid = |msg: &str| Err(Error::InvalidArgument(format!("arange: {msg}")));
676 if dtype == DType::Bool {
677 return Err(Error::unsupported_dtype("arange", dtype));
678 }
679 if start.is_nan() || stop.is_nan() || step.is_nan() || start.is_infinite() || stop.is_infinite()
680 {
681 return invalid("cannot compute the length from NaN or infinite bounds");
682 }
683 if step == 0.0 {
684 return invalid("step must be non-zero");
685 }
686 let len = if step.is_infinite() {
687 usize::from((step > 0.0 && start < stop) || (step < 0.0 && start > stop))
688 } else {
689 let real = ((stop - start) / step).ceil();
690 if real > i32::MAX as f64 {
691 return invalid("maximum size exceeded");
692 }
693 real.max(0.0) as usize
694 };
695 filled(&[len], dtype, |i| {
697 Canon::Float(if i == 0 {
698 start
699 } else {
700 start + i as f64 * step
701 })
702 })
703}
704
705pub(crate) fn filled(
707 shape: &[usize],
708 dtype: DType,
709 value: impl Fn(usize) -> Canon,
710) -> Result<Array> {
711 let mut buffer = Buffer::try_alloc(shape::checked_nbytes(shape, dtype.size())?)?;
712 with_type!(dtype, T => {
713 for (i, x) in buffer.typed_mut::<T>().iter_mut().enumerate() {
714 *x = T::from_canon(value(i));
715 }
716 });
717 Array::from_buffer(buffer, dtype, shape)
718}
719
720impl Array {
723 pub(crate) fn try_astype(&self, dtype: DType) -> Result<Array> {
725 astype(self, dtype)
726 }
727
728 pub(crate) fn try_reshape(&self, shape: &[isize]) -> Result<Array> {
730 reshape(self, shape)
731 }
732}
733
734macro_rules! operator {
741 ($trait:ident, $method:ident, $op:ident) => {
742 impl $trait<&Array> for &Array {
743 type Output = Array;
744 #[track_caller]
745 fn $method(self, rhs: &Array) -> Array {
746 Array::sticky(stringify!($op), Location::caller(), &[self, rhs], || {
747 $op(self, rhs)
748 })
749 }
750 }
751 impl $trait<Array> for &Array {
752 type Output = Array;
753 #[track_caller]
754 fn $method(self, rhs: Array) -> Array {
755 Array::sticky(stringify!($op), Location::caller(), &[self, &rhs], || {
756 $op(self, &rhs)
757 })
758 }
759 }
760 impl $trait<&Array> for Array {
761 type Output = Array;
762 #[track_caller]
763 fn $method(self, rhs: &Array) -> Array {
764 Array::sticky(stringify!($op), Location::caller(), &[&self, rhs], || {
765 $op(&self, rhs)
766 })
767 }
768 }
769 impl $trait<Array> for Array {
770 type Output = Array;
771 #[track_caller]
772 fn $method(self, rhs: Array) -> Array {
773 Array::sticky(stringify!($op), Location::caller(), &[&self, &rhs], || {
774 $op(&self, &rhs)
775 })
776 }
777 }
778 };
779}
780
781operator!(Add, add, add);
782operator!(Sub, sub, subtract);
783operator!(Mul, mul, multiply);
784operator!(Div, div, divide);
785
786macro_rules! scalar_rhs_operator {
788 ($trait:ident, $method:ident, $op:ident) => {
789 impl<S: ScalarOperand> $trait<S> for &Array {
790 type Output = Array;
791 #[track_caller]
792 fn $method(self, rhs: S) -> Array {
793 Array::sticky(stringify!($op), Location::caller(), &[self], || {
794 $op(self, rhs.into())
795 })
796 }
797 }
798 impl<S: ScalarOperand> $trait<S> for Array {
799 type Output = Array;
800 #[track_caller]
801 fn $method(self, rhs: S) -> Array {
802 Array::sticky(stringify!($op), Location::caller(), &[&self], || {
803 $op(&self, rhs.into())
804 })
805 }
806 }
807 };
808}
809
810scalar_rhs_operator!(Add, add, add);
811scalar_rhs_operator!(Sub, sub, subtract);
812scalar_rhs_operator!(Mul, mul, multiply);
813scalar_rhs_operator!(Div, div, divide);
814
815macro_rules! scalar_lhs_operator {
822 ($trait:ident, $method:ident, $op:ident; $($t:ty),*) => {$(
823 impl $trait<&Array> for $t {
824 type Output = Array;
825 #[track_caller]
826 fn $method(self, rhs: &Array) -> Array {
827 Array::sticky(stringify!($op), Location::caller(), &[rhs], || $op(self, rhs))
828 }
829 }
830 impl $trait<Array> for $t {
831 type Output = Array;
832 #[track_caller]
833 fn $method(self, rhs: Array) -> Array {
834 Array::sticky(stringify!($op), Location::caller(), &[&rhs], || $op(self, &rhs))
835 }
836 }
837 )*};
838}
839
840macro_rules! scalar_lhs_operators {
841 ($($t:ty),*) => {
842 scalar_lhs_operator!(Add, add, add; $($t),*);
843 scalar_lhs_operator!(Sub, sub, subtract; $($t),*);
844 scalar_lhs_operator!(Mul, mul, multiply; $($t),*);
845 scalar_lhs_operator!(Div, div, divide; $($t),*);
846 };
847}
848
849scalar_lhs_operators!(bool, i32, f64, f16, bf16, Complex64);
850
851impl Neg for &Array {
852 type Output = Array;
853 #[track_caller]
854 fn neg(self) -> Array {
855 Array::sticky("negative", Location::caller(), &[self], || negative(self))
856 }
857}
858
859impl Neg for Array {
860 type Output = Array;
861 #[track_caller]
862 fn neg(self) -> Array {
863 Array::sticky("negative", Location::caller(), &[&self], || negative(&self))
864 }
865}
866
867pub(crate) fn repeat(a: &Array, repeats: usize, axis: Option<isize>) -> Result<Array> {
870 let (a, axis) = match axis {
871 Some(axis) => (a.clone(), view::normalize_axis(axis, a.ndim())?),
872 None => (reshape(a, &[-1])?, 0),
873 };
874 let mut wide: Vec<usize> = a.dims().to_vec();
875 wide.insert(axis + 1, repeats);
876 let mut out: Vec<isize> = a.dims().iter().map(|&d| d as isize).collect();
877 out[axis] *= repeats as isize;
878 reshape(
879 &broadcast_to(&expand_dims(&a, axis as isize + 1)?, &wide)?,
880 &out,
881 )
882}
883
884pub(crate) fn copy(a: &Array) -> Result<Array> {
887 crate::primitive::copy(a)
888}
889
890pub(crate) fn contiguous(a: &Array) -> Result<Array> {
891 if a.flags().row_contiguous {
892 return Ok(a.clone());
893 }
894 let shape: Vec<isize> = a.dims().iter().map(|&d| d as isize).collect();
895 reshape(&reshape(a, &[-1])?, &shape)
896}
897
898#[cfg(test)]
899mod tests {
900 use super::*;
901 use crate::buffer::live;
902
903 fn allocations(f: impl FnOnce() -> Array) -> isize {
905 let before = live::count();
906 let out = f();
907 out.eval().unwrap();
908 live::count() - before
909 }
910
911 #[test]
912 fn views_share_data() {
913 let base = arange(0.0, 24.0, 1.0, DType::F32)
914 .unwrap()
915 .try_reshape(&[2, 3, 4])
916 .unwrap();
917 base.eval().unwrap();
918 assert_eq!(allocations(|| base.try_reshape(&[6, 4]).unwrap()), 0);
919 assert_eq!(allocations(|| base.t()), 0);
920 assert_eq!(
921 allocations(|| slice(
922 &base,
923 &[(..).into(), SliceArg::new(None, None, -2), (1..3).into()]
924 )
925 .unwrap()),
926 0
927 );
928 assert_eq!(
929 allocations(|| broadcast_to(&base, &[5, 2, 3, 4]).unwrap()),
930 0
931 );
932 assert_eq!(allocations(|| expand_dims(&base, 1).unwrap()), 0);
933 assert_eq!(
934 allocations(|| squeeze(&expand_dims(&base, -1).unwrap(), &[]).unwrap()),
935 0
936 );
937 assert_eq!(allocations(|| base.t().try_reshape(&[-1]).unwrap()), 1);
939 }
940
941 #[test]
942 fn noop_views_record_nothing() {
943 let a = Array::from_slice(&[1u8, 2, 3, 4], [2, 2]);
944 assert!(a.try_reshape(&[2, 2]).unwrap().primitive_t().is_none());
945 assert!(transpose(&a, &[0, 1]).unwrap().primitive_t().is_none());
946 assert!(
947 slice(&a, &[SliceArg::full(), SliceArg::full()])
948 .unwrap()
949 .primitive_t()
950 .is_none()
951 );
952 assert!(astype(&a, DType::U8).unwrap().primitive_t().is_none());
953 assert!(broadcast_to(&a, &[2, 2]).unwrap().primitive_t().is_none());
954 }
955
956 #[test]
957 fn view_flags() {
958 let a = zeros(&[2, 3], DType::F32).unwrap();
959 let t = a.t();
960 assert!(t.flags().col_contiguous && !t.flags().row_contiguous);
961 assert_eq!(t.strides(), &[1, 3]);
962 let b = broadcast_to(&a, &[4, 2, 3]).unwrap();
963 assert_eq!(b.strides(), &[0, 3, 1]);
964 assert!(!b.flags().contiguous);
965 }
966}