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