1use std::ops::{Add, Div, Mul, Neg, Sub};
9
10use crate::buffer::Buffer;
11use crate::cast::{Canon, Scalar, with_type};
12use crate::primitive::{ArgReduceOp, BinaryOp, Primitive, ReduceOp, UnaryOp};
13use crate::scalar::{ScalarOperand, ScalarOrArray, resolve};
14use crate::shape::{self, Shape};
15use crate::view::{self, SliceArg};
16use crate::{
17 Array, Category, Complex64, DType, Error, Result, at_least_float, bf16, f16, promote_types,
18};
19
20fn single(primitive: Primitive, inputs: Vec<Array>) -> Result<Array> {
21 let mut outputs = Array::from_op(primitive, inputs)?;
22 Ok(outputs.swap_remove(0))
23}
24
25fn check_inputs(inputs: &[&Array]) -> Result<()> {
28 match inputs.iter().find_map(|a| a.error()) {
29 Some(err) => Err(err),
30 None => Ok(()),
31 }
32}
33
34fn binary(op: BinaryOp, a: &Array, b: &Array) -> Result<Array> {
37 check_inputs(&[a, b])?;
38 let mut dtype = promote_types(a.dtype(), b.dtype());
39 if matches!(op, BinaryOp::Divide | BinaryOp::LogAddExp) {
40 dtype = at_least_float(dtype);
41 }
42 let ordered = matches!(
43 op,
44 BinaryOp::Maximum
45 | BinaryOp::Minimum
46 | BinaryOp::LogAddExp
47 | BinaryOp::Less
48 | BinaryOp::LessEqual
49 | BinaryOp::Greater
50 | BinaryOp::GreaterEqual
51 );
52 if ordered && dtype == DType::Complex64 {
53 return Err(Error::UnsupportedDType {
54 op: op.name(),
55 dtype,
56 });
57 }
58 let shape = view::broadcast_shapes(op.name(), a.shape(), b.shape())?;
59 let a = broadcast_to(&astype(a, dtype)?, &shape)?;
60 let b = broadcast_to(&astype(b, dtype)?, &shape)?;
61 single(Primitive::Binary(op), vec![a, b])
62}
63
64macro_rules! binary_fn {
65 ($($(#[$doc:meta])* $name:ident => $op:ident;)*) => {$(
66 $(#[$doc])*
67 pub fn $name(a: impl Into<ScalarOrArray>, b: impl Into<ScalarOrArray>) -> Result<Array> {
68 let (a, b) = resolve(a.into(), b.into())?;
69 binary(BinaryOp::$op, &a, &b)
70 }
71 )*};
72}
73
74binary_fn! {
77 add => Add;
80 subtract => Subtract;
83 multiply => Multiply;
86 divide => Divide;
88 maximum => Maximum;
90 minimum => Minimum;
92 logaddexp => LogAddExp;
97 equal => Equal;
99 not_equal => NotEqual;
101 less => Less;
103 less_equal => LessEqual;
105 greater => Greater;
107 greater_equal => GreaterEqual;
109}
110
111fn unary(op: UnaryOp, a: &Array) -> Result<Array> {
114 check_inputs(&[a])?;
115 let dtype = if op.is_transcendental() {
116 at_least_float(a.dtype())
117 } else {
118 a.dtype()
119 };
120 let unsupported = match op {
121 UnaryOp::Negative => dtype == DType::Bool,
122 UnaryOp::Abs | UnaryOp::Conjugate => false,
123 _ => dtype == DType::Complex64,
124 };
125 if unsupported {
126 return Err(Error::UnsupportedDType {
127 op: op.name(),
128 dtype,
129 });
130 }
131 single(Primitive::Unary(op), vec![astype(a, dtype)?])
132}
133
134pub fn conjugate(a: &Array) -> Result<Array> {
136 check_inputs(&[a])?;
137 if a.dtype() != DType::Complex64 {
138 return Ok(a.clone());
139 }
140 unary(UnaryOp::Conjugate, a)
141}
142
143pub fn sign(a: &Array) -> Result<Array> {
147 check_inputs(&[a])?;
148 let dtype = a.dtype();
149 match dtype.category() {
150 Category::Complex => Err(Error::UnsupportedDType { op: "sign", dtype }),
151 Category::Bool | Category::Unsigned => astype(&greater(a, 0)?, dtype),
152 _ => subtract(
153 astype(&greater(a, 0)?, dtype)?,
154 astype(&less(a, 0)?, dtype)?,
155 ),
156 }
157}
158
159macro_rules! unary_fn {
160 ($($(#[$doc:meta])* $name:ident => $op:ident;)*) => {$(
161 $(#[$doc])*
162 pub fn $name(a: &Array) -> Result<Array> {
163 unary(UnaryOp::$op, a)
164 }
165 )*};
166}
167
168unary_fn! {
169 negative => Negative;
171 abs => Abs;
173 exp => Exp;
175 log => Log;
177 sqrt => Sqrt;
179 sin => Sin;
181 cos => Cos;
183 tanh => Tanh;
185 sigmoid => Sigmoid;
187 erfinv => ErfInv;
189}
190
191pub fn logsumexp(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
195 check_inputs(&[a])?;
196 let a = astype(a, at_least_float(a.dtype()))?;
197 if last_axis_only(&a, axes) && crate::fast::on_gpu(a.dtype(), a.size()) {
198 let out = Array::from_op(Primitive::Fast(crate::fast::FastOp::LogSumExp), vec![a])?;
199 let out = out.into_iter().next().expect("one output");
200 return if keepdims {
201 Ok(out)
202 } else {
203 squeeze(&out, axes)
204 };
205 }
206 let maxval = stop_gradient(&max(&a, axes, true)?)?;
207 let out = log(&sum(&exp(&subtract(&a, &maxval)?)?, axes, true)?)?;
208 let out = add(&out, &maxval)?;
209 let out = where_(equal(&abs(&maxval)?, f64::INFINITY)?, &maxval, &out)?;
210 if keepdims {
211 Ok(out)
212 } else {
213 squeeze(&out, axes)
214 }
215}
216
217pub fn softmax(a: &Array, axes: &[isize]) -> Result<Array> {
220 check_inputs(&[a])?;
221 let a = astype(a, at_least_float(a.dtype()))?;
222 if last_axis_only(&a, axes) && crate::fast::on_gpu(a.dtype(), a.size()) {
223 return single(Primitive::Fast(crate::fast::FastOp::Softmax), vec![a]);
224 }
225 let maxval = stop_gradient(&max(&a, axes, true)?)?;
226 let ex = exp(&subtract(&a, &maxval)?)?;
227 divide(&ex, sum(&ex, axes, true)?)
228}
229
230fn last_axis_only(a: &Array, axes: &[isize]) -> bool {
233 let n = a.ndim() as isize;
234 let all: Vec<isize> = if axes.is_empty() {
235 (0..n).collect()
236 } else {
237 axes.to_vec()
238 };
239 n > 0
240 && a.dtype() != DType::Complex64
241 && all.iter().all(|&ax| (-n..n).contains(&ax))
242 && all.iter().any(|&ax| ax.rem_euclid(n) == n - 1)
243 && all
244 .iter()
245 .all(|&ax| ax.rem_euclid(n) == n - 1 || a.shape()[ax.rem_euclid(n) as usize] == 1)
246}
247
248pub fn where_(
254 condition: impl Into<ScalarOrArray>,
255 a: impl Into<ScalarOrArray>,
256 b: impl Into<ScalarOrArray>,
257) -> Result<Array> {
258 let (a, b) = resolve(a.into(), b.into())?;
259 let condition = condition.into().into_array(None)?;
260 let (condition, a, b) = (&condition, &a, &b);
261 check_inputs(&[condition, a, b])?;
262 let dtype = promote_types(a.dtype(), b.dtype());
263 let shape = view::broadcast_shapes("where", condition.shape(), a.shape())?;
264 let shape = view::broadcast_shapes("where", &shape, b.shape())?;
265 let condition = broadcast_to(&astype(condition, DType::Bool)?, &shape)?;
266 let a = broadcast_to(&astype(a, dtype)?, &shape)?;
267 let b = broadcast_to(&astype(b, dtype)?, &shape)?;
268 single(Primitive::Select, vec![condition, a, b])
269}
270
271pub fn astype(a: &Array, dtype: DType) -> Result<Array> {
274 if a.dtype() == dtype {
275 return Ok(a.clone());
276 }
277 single(Primitive::AsType(dtype), vec![a.clone()])
278}
279
280pub fn reshape(a: &Array, shape: &[isize]) -> Result<Array> {
285 let shape = view::infer_shape(a.size(), shape)?;
286 reshape_to(a, shape)
287}
288
289fn reshape_to(a: &Array, shape: Shape) -> Result<Array> {
290 if a.shape() == shape.as_slice() {
291 return Ok(a.clone());
292 }
293 single(Primitive::Reshape(shape.to_vec()), vec![a.clone()])
294}
295
296pub fn transpose(a: &Array, axes: &[usize]) -> Result<Array> {
298 view::permute(a.shape(), a.strides(), axes)?;
299 if axes.iter().enumerate().all(|(i, &axis)| i == axis) {
300 return Ok(a.clone());
301 }
302 single(Primitive::Transpose(axes.to_vec()), vec![a.clone()])
303}
304
305pub fn slice(a: &Array, args: &[SliceArg]) -> Result<Array> {
308 let dims = view::normalize_slice(a.shape(), args)?;
309 let full = dims
310 .iter()
311 .zip(a.shape())
312 .all(|(d, &n)| d.start == 0 && d.step == 1 && d.len == n);
313 if full {
314 return Ok(a.clone());
315 }
316 let primitive = Primitive::Slice {
317 start: dims.iter().map(|d| d.start).collect(),
318 step: dims.iter().map(|d| d.step).collect(),
319 len: dims.iter().map(|d| d.len).collect(),
320 };
321 single(primitive, vec![a.clone()])
322}
323
324pub fn slice_update(a: &Array, update: &Array, args: &[SliceArg]) -> Result<Array> {
341 check_inputs(&[a, update])?;
342 let dims = view::normalize_slice(a.shape(), args)?;
343 let len: Vec<usize> = dims.iter().map(|d| d.len).collect();
344 let update = broadcast_to(&astype(update, a.dtype())?, &len)?;
345 single(
346 Primitive::SliceUpdate {
347 start: dims.iter().map(|d| d.start).collect(),
348 step: dims.iter().map(|d| d.step).collect(),
349 len,
350 },
351 vec![a.clone(), update],
352 )
353}
354
355pub fn stop_gradient(a: &Array) -> Result<Array> {
357 check_inputs(&[a])?;
358 single(Primitive::StopGradient, vec![a.clone()])
359}
360
361pub fn broadcast_to(a: &Array, shape: &[usize]) -> Result<Array> {
363 if a.shape() == shape {
364 return Ok(a.clone());
365 }
366 single(Primitive::Broadcast(shape.to_vec()), vec![a.clone()])
367}
368
369pub fn expand_dims(a: &Array, axis: isize) -> Result<Array> {
372 let axis = view::normalize_axis(axis, a.ndim() + 1)?;
373 let mut shape: Shape = a.shape().into();
374 shape.insert(axis, 1);
375 reshape_to(a, shape)
376}
377
378pub fn squeeze(a: &Array, axes: &[isize]) -> Result<Array> {
381 let mut remove = vec![axes.is_empty(); a.ndim()];
382 for &axis in axes {
383 let i = view::normalize_axis(axis, a.ndim())?;
384 if a.shape()[i] != 1 {
385 return Err(Error::InvalidArgument(format!(
386 "cannot squeeze axis {axis} of size {} in shape {:?}",
387 a.shape()[i],
388 a.shape()
389 )));
390 }
391 remove[i] = true;
392 }
393 let shape = a
394 .shape()
395 .iter()
396 .zip(&remove)
397 .filter(|&(&d, &r)| !(r && d == 1))
398 .map(|(&d, _)| d)
399 .collect();
400 reshape_to(a, shape)
401}
402
403fn normalize_axes(a: &Array, axes: &[isize]) -> Result<Vec<usize>> {
407 if axes.is_empty() {
408 return Ok((0..a.ndim()).collect());
409 }
410 let mut out = axes
411 .iter()
412 .map(|&axis| view::normalize_axis(axis, a.ndim()))
413 .collect::<Result<Vec<_>>>()?;
414 out.sort_unstable();
415 if out.windows(2).any(|w| w[0] == w[1]) {
416 return Err(Error::InvalidArgument(format!(
417 "duplicate axes in {axes:?}"
418 )));
419 }
420 Ok(out)
421}
422
423fn finish_reduction(out: Array, axes: &[usize], keepdims: bool) -> Result<Array> {
425 if keepdims {
426 return Ok(out);
427 }
428 let shape = out
429 .shape()
430 .iter()
431 .enumerate()
432 .filter(|(d, _)| !axes.contains(d))
433 .map(|(_, &n)| n)
434 .collect();
435 reshape_to(&out, shape)
436}
437
438fn reduction(
439 op: ReduceOp,
440 a: &Array,
441 axes: &[isize],
442 keepdims: bool,
443 dtype: DType,
444) -> Result<Array> {
445 check_inputs(&[a])?;
446 let axes = normalize_axes(a, axes)?;
447 if matches!(op, ReduceOp::Max | ReduceOp::Min) {
448 if a.dtype() == DType::Complex64 {
449 return Err(Error::UnsupportedDType {
450 op: op.name(),
451 dtype: a.dtype(),
452 });
453 }
454 if axes.iter().any(|&d| a.shape()[d] == 0) {
455 return Err(Error::InvalidArgument(format!(
456 "{}: cannot reduce a zero-length axis (no identity)",
457 op.name()
458 )));
459 }
460 }
461 let a = astype(a, dtype)?;
462 if axes.is_empty() {
463 return Ok(a); }
465 let out = single(
466 Primitive::Reduce {
467 op,
468 axes: axes.clone(),
469 },
470 vec![a],
471 )?;
472 finish_reduction(out, &axes, keepdims)
473}
474
475fn widened(dtype: DType) -> DType {
477 match (dtype.category(), dtype.size()) {
478 (Category::Bool, _) => DType::I32,
479 (Category::Signed, 1..=4) => DType::I32,
480 (Category::Signed, _) => DType::I64,
481 (Category::Unsigned, 1..=4) => DType::U32,
482 (Category::Unsigned, _) => DType::U64,
483 _ => dtype,
484 }
485}
486
487pub fn sum(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
490 reduction(ReduceOp::Sum, a, axes, keepdims, widened(a.dtype()))
491}
492
493pub fn prod(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
495 reduction(ReduceOp::Prod, a, axes, keepdims, widened(a.dtype()))
496}
497
498pub fn max(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
500 reduction(ReduceOp::Max, a, axes, keepdims, a.dtype())
501}
502
503pub fn min(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
505 reduction(ReduceOp::Min, a, axes, keepdims, a.dtype())
506}
507
508pub fn all(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
510 reduction(ReduceOp::And, a, axes, keepdims, DType::Bool)
511}
512
513pub fn any(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
515 reduction(ReduceOp::Or, a, axes, keepdims, DType::Bool)
516}
517
518pub fn mean(a: &Array, axes: &[isize], keepdims: bool) -> Result<Array> {
520 let dtype = at_least_float(a.dtype());
521 let count: usize = normalize_axes(a, axes)?
522 .iter()
523 .map(|&d| a.shape()[d])
524 .product();
525 let scale = astype(&Array::scalar(1.0 / count as f64), dtype)?;
526 multiply(&sum(a, axes, keepdims)?, &scale)
527}
528
529fn arg_reduction(op: ArgReduceOp, a: &Array, axis: Option<isize>, keepdims: bool) -> Result<Array> {
530 check_inputs(&[a])?;
531 if a.dtype() == DType::Complex64 {
532 return Err(Error::UnsupportedDType {
533 op: op.name(),
534 dtype: a.dtype(),
535 });
536 }
537 let Some(axis) = axis else {
538 let flat = arg_reduction(op, &reshape(a, &[-1])?, Some(0), false)?;
541 return if keepdims {
542 reshape_to(&flat, smallvec::smallvec![1; a.ndim()])
543 } else {
544 Ok(flat)
545 };
546 };
547 let axis = view::normalize_axis(axis, a.ndim())?;
548 if a.shape()[axis] == 0 {
549 return Err(Error::InvalidArgument(format!(
550 "{}: cannot reduce a zero-length axis",
551 op.name()
552 )));
553 }
554 let out = single(Primitive::ArgReduce { op, axis }, vec![a.clone()])?;
555 finish_reduction(out, &[axis], keepdims)
556}
557
558pub fn argmax(a: &Array, axis: Option<isize>, keepdims: bool) -> Result<Array> {
561 arg_reduction(ArgReduceOp::Max, a, axis, keepdims)
562}
563
564pub fn argmin(a: &Array, axis: Option<isize>, keepdims: bool) -> Result<Array> {
567 arg_reduction(ArgReduceOp::Min, a, axis, keepdims)
568}
569
570pub fn matmul(a: &Array, b: &Array) -> Result<Array> {
577 product(a, b, None)
578}
579
580pub fn addmm(c: &Array, a: &Array, b: &Array, alpha: f32, beta: f32) -> Result<Array> {
584 product(a, b, Some((c, alpha, beta)))
585}
586
587fn product(a: &Array, b: &Array, c: Option<(&Array, f32, f32)>) -> Result<Array> {
590 let op = if c.is_some() { "addmm" } else { "matmul" };
591 check_inputs(&[a, b])?;
592 if let Some((c, ..)) = c {
593 check_inputs(&[c])?;
594 }
595 if a.ndim() == 0 || b.ndim() == 0 {
596 return Err(Error::InvalidArgument(format!(
597 "{op}: operands need at least one dimension"
598 )));
599 }
600 let mut dtype = promote_types(a.dtype(), b.dtype());
601 if let Some((c, ..)) = c {
602 dtype = promote_types(dtype, c.dtype());
603 }
604 if !matches!(dtype.category(), Category::Float | Category::Complex) {
605 return Err(Error::UnsupportedDType { op, dtype });
606 }
607 let a2 = if a.ndim() == 1 {
608 expand_dims(a, 0)?
609 } else {
610 a.clone()
611 };
612 let b2 = if b.ndim() == 1 {
613 expand_dims(b, -1)?
614 } else {
615 b.clone()
616 };
617 let (sa, sb) = (a2.shape(), b2.shape());
618 if sa[sa.len() - 1] != sb[sb.len() - 2] {
619 return Err(Error::ShapeMismatch {
620 op,
621 lhs: a.shape().to_vec(),
622 rhs: b.shape().to_vec(),
623 });
624 }
625 let (a2, b2) = (astype(&a2, dtype)?, astype(&b2, dtype)?);
626 let (na, nb) = (a2.ndim(), b2.ndim());
627 let batch = view::broadcast_shapes(op, &a2.shape()[..na - 2], &b2.shape()[..nb - 2])?;
628 let (m, n) = (a2.shape()[na - 2], b2.shape()[nb - 1]);
629 let mut expanded: Shape = batch.clone();
630 expanded.extend_from_slice(&[m, n]);
631 let mut drop = Vec::new();
633 if a.ndim() == 1 {
634 drop.push(expanded.len() as isize - 2);
635 }
636 if b.ndim() == 1 {
637 drop.push(expanded.len() as isize - 1);
638 }
639 let c_full = match c {
641 Some((c, alpha, beta)) => {
642 let final_shape: Vec<usize> = expanded
643 .iter()
644 .enumerate()
645 .filter(|(i, _)| !drop.contains(&(*i as isize)))
646 .map(|(_, &d)| d)
647 .collect();
648 let mut cc = broadcast_to(&astype(c, dtype)?, &final_shape)?;
649 for &d in &drop {
650 cc = expand_dims(&cc, d)?;
651 }
652 Some((broadcast_to(&cc, &expanded)?, alpha, beta))
653 }
654 None => None,
655 };
656 let record = |x: Array, y: Array, z: Option<(Array, f32, f32)>| match z {
657 None => single(Primitive::Matmul, vec![x, y]),
658 Some((z, alpha, beta)) => single(
659 Primitive::AddMM {
660 alpha: alpha.to_bits(),
661 beta: beta.to_bits(),
662 },
663 vec![x, y, z],
664 ),
665 };
666 let out = if na > 2 && nb == 2 {
667 let k = a2.shape()[na - 1];
669 let rows = a2
670 .size()
671 .checked_div(k)
672 .unwrap_or_else(|| a2.shape()[..na - 1].iter().product());
673 let flat_c = match c_full {
674 Some((z, alpha, beta)) => {
675 Some((reshape_to(&z, smallvec::smallvec![rows, n])?, alpha, beta))
676 }
677 None => None,
678 };
679 let flat = record(
680 reshape_to(&a2, smallvec::smallvec![rows, k])?,
681 b2.clone(),
682 flat_c,
683 )?;
684 reshape_to(&flat, expanded)?
685 } else {
686 let full = |x: &Array, nd: usize| -> Result<Array> {
687 let mut shape = batch.clone();
688 shape.extend_from_slice(&x.shape()[nd - 2..]);
689 broadcast_to(x, &shape)
690 };
691 record(full(&a2, na)?, full(&b2, nb)?, c_full)?
692 };
693 if drop.is_empty() {
694 Ok(out)
695 } else {
696 squeeze(&out, &drop)
697 }
698}
699
700pub fn zeros(shape: &[usize], dtype: DType) -> Result<Array> {
704 let buffer = Buffer::try_zeroed(shape::checked_nbytes(shape, dtype.size())?)?;
705 Array::from_buffer(buffer, dtype, shape)
706}
707
708pub fn ones(shape: &[usize], dtype: DType) -> Result<Array> {
710 filled(shape, dtype, |_| Canon::Int(1))
711}
712
713pub fn arange(start: f64, stop: f64, step: f64, dtype: DType) -> Result<Array> {
716 let invalid = |msg: &str| Err(Error::InvalidArgument(format!("arange: {msg}")));
717 if dtype == DType::Bool {
718 return Err(Error::UnsupportedDType {
719 op: "arange",
720 dtype,
721 });
722 }
723 if start.is_nan() || stop.is_nan() || step.is_nan() || start.is_infinite() || stop.is_infinite()
724 {
725 return invalid("cannot compute the length from NaN or infinite bounds");
726 }
727 if step == 0.0 {
728 return invalid("step must be non-zero");
729 }
730 let len = if step.is_infinite() {
731 usize::from((step > 0.0 && start < stop) || (step < 0.0 && start > stop))
732 } else {
733 let real = ((stop - start) / step).ceil();
734 if real > i32::MAX as f64 {
735 return invalid("maximum size exceeded");
736 }
737 real.max(0.0) as usize
738 };
739 filled(&[len], dtype, |i| {
741 Canon::Float(if i == 0 {
742 start
743 } else {
744 start + i as f64 * step
745 })
746 })
747}
748
749pub(crate) fn filled(
751 shape: &[usize],
752 dtype: DType,
753 value: impl Fn(usize) -> Canon,
754) -> Result<Array> {
755 let mut buffer = Buffer::try_alloc(shape::checked_nbytes(shape, dtype.size())?)?;
756 with_type!(dtype, T => {
757 for (i, x) in buffer.typed_mut::<T>().iter_mut().enumerate() {
758 *x = T::from_canon(value(i));
759 }
760 });
761 Array::from_buffer(buffer, dtype, shape)
762}
763
764impl Array {
767 pub fn astype(&self, dtype: DType) -> Result<Array> {
769 astype(self, dtype)
770 }
771
772 pub fn reshape(&self, shape: &[isize]) -> Result<Array> {
774 reshape(self, shape)
775 }
776
777 pub fn t(&self) -> Result<Array> {
779 let axes: Vec<usize> = (0..self.ndim()).rev().collect();
780 transpose(self, &axes)
781 }
782}
783
784fn or_failed(result: Result<Array>, lhs: &Array) -> Array {
789 result.unwrap_or_else(|err| Array::failed(err, lhs.dtype(), lhs.shape()))
790}
791
792macro_rules! operator {
793 ($trait:ident, $method:ident, $op:ident) => {
794 impl $trait<&Array> for &Array {
795 type Output = Array;
796 fn $method(self, rhs: &Array) -> Array {
797 or_failed($op(self, rhs), self)
798 }
799 }
800 impl $trait<Array> for &Array {
801 type Output = Array;
802 fn $method(self, rhs: Array) -> Array {
803 or_failed($op(self, &rhs), self)
804 }
805 }
806 impl $trait<&Array> for Array {
807 type Output = Array;
808 fn $method(self, rhs: &Array) -> Array {
809 or_failed($op(&self, rhs), &self)
810 }
811 }
812 impl $trait<Array> for Array {
813 type Output = Array;
814 fn $method(self, rhs: Array) -> Array {
815 or_failed($op(&self, &rhs), &self)
816 }
817 }
818 };
819}
820
821operator!(Add, add, add);
822operator!(Sub, sub, subtract);
823operator!(Mul, mul, multiply);
824operator!(Div, div, divide);
825
826macro_rules! scalar_rhs_operator {
828 ($trait:ident, $method:ident, $op:ident) => {
829 impl<S: ScalarOperand> $trait<S> for &Array {
830 type Output = Array;
831 fn $method(self, rhs: S) -> Array {
832 or_failed($op(self, rhs.into()), self)
833 }
834 }
835 impl<S: ScalarOperand> $trait<S> for Array {
836 type Output = Array;
837 fn $method(self, rhs: S) -> Array {
838 or_failed($op(&self, rhs.into()), &self)
839 }
840 }
841 };
842}
843
844scalar_rhs_operator!(Add, add, add);
845scalar_rhs_operator!(Sub, sub, subtract);
846scalar_rhs_operator!(Mul, mul, multiply);
847scalar_rhs_operator!(Div, div, divide);
848
849macro_rules! scalar_lhs_operator {
856 ($trait:ident, $method:ident, $op:ident; $($t:ty),*) => {$(
857 impl $trait<&Array> for $t {
858 type Output = Array;
859 fn $method(self, rhs: &Array) -> Array {
860 or_failed($op(self, rhs), rhs)
861 }
862 }
863 impl $trait<Array> for $t {
864 type Output = Array;
865 fn $method(self, rhs: Array) -> Array {
866 or_failed($op(self, &rhs), &rhs)
867 }
868 }
869 )*};
870}
871
872macro_rules! scalar_lhs_operators {
873 ($($t:ty),*) => {
874 scalar_lhs_operator!(Add, add, add; $($t),*);
875 scalar_lhs_operator!(Sub, sub, subtract; $($t),*);
876 scalar_lhs_operator!(Mul, mul, multiply; $($t),*);
877 scalar_lhs_operator!(Div, div, divide; $($t),*);
878 };
879}
880
881scalar_lhs_operators!(bool, i32, f64, f16, bf16, Complex64);
882
883impl Neg for &Array {
884 type Output = Array;
885 fn neg(self) -> Array {
886 or_failed(negative(self), self)
887 }
888}
889
890impl Neg for Array {
891 type Output = Array;
892 fn neg(self) -> Array {
893 or_failed(negative(&self), &self)
894 }
895}
896
897pub fn repeat(a: &Array, repeats: usize, axis: Option<isize>) -> Result<Array> {
911 let (a, axis) = match axis {
912 Some(axis) => (a.clone(), view::normalize_axis(axis, a.ndim())?),
913 None => (reshape(a, &[-1])?, 0),
914 };
915 let mut wide: Vec<usize> = a.shape().to_vec();
916 wide.insert(axis + 1, repeats);
917 let mut out: Vec<isize> = a.shape().iter().map(|&d| d as isize).collect();
918 out[axis] *= repeats as isize;
919 reshape(
920 &broadcast_to(&expand_dims(&a, axis as isize + 1)?, &wide)?,
921 &out,
922 )
923}
924
925pub fn contiguous(a: &Array) -> Result<Array> {
927 if a.flags().row_contiguous {
928 return Ok(a.clone());
929 }
930 let shape: Vec<isize> = a.shape().iter().map(|&d| d as isize).collect();
931 reshape(&reshape(a, &[-1])?, &shape)
932}
933
934#[cfg(test)]
935mod tests {
936 use super::*;
937 use crate::buffer::live;
938
939 fn allocations(f: impl FnOnce() -> Array) -> isize {
941 let before = live::count();
942 let out = f();
943 out.eval().unwrap();
944 live::count() - before
945 }
946
947 #[test]
948 fn views_share_data() {
949 let base = arange(0.0, 24.0, 1.0, DType::F32)
950 .unwrap()
951 .reshape(&[2, 3, 4])
952 .unwrap();
953 base.eval().unwrap();
954 assert_eq!(allocations(|| base.reshape(&[6, 4]).unwrap()), 0);
955 assert_eq!(allocations(|| base.t().unwrap()), 0);
956 assert_eq!(
957 allocations(|| slice(
958 &base,
959 &[(..).into(), SliceArg::new(None, None, -2), (1..3).into()]
960 )
961 .unwrap()),
962 0
963 );
964 assert_eq!(
965 allocations(|| broadcast_to(&base, &[5, 2, 3, 4]).unwrap()),
966 0
967 );
968 assert_eq!(allocations(|| expand_dims(&base, 1).unwrap()), 0);
969 assert_eq!(
970 allocations(|| squeeze(&expand_dims(&base, -1).unwrap(), &[]).unwrap()),
971 0
972 );
973 assert_eq!(allocations(|| base.t().unwrap().reshape(&[-1]).unwrap()), 1);
975 }
976
977 #[test]
978 fn noop_views_record_nothing() {
979 let a = Array::from_slice(&[1u8, 2, 3, 4], &[2, 2]).unwrap();
980 assert!(a.reshape(&[2, 2]).unwrap().primitive().is_none());
981 assert!(transpose(&a, &[0, 1]).unwrap().primitive().is_none());
982 assert!(
983 slice(&a, &[SliceArg::full(), SliceArg::full()])
984 .unwrap()
985 .primitive()
986 .is_none()
987 );
988 assert!(astype(&a, DType::U8).unwrap().primitive().is_none());
989 assert!(broadcast_to(&a, &[2, 2]).unwrap().primitive().is_none());
990 }
991
992 #[test]
993 fn view_flags() {
994 let a = zeros(&[2, 3], DType::F32).unwrap();
995 let t = a.t().unwrap();
996 assert!(t.flags().col_contiguous && !t.flags().row_contiguous);
997 assert_eq!(t.strides(), &[1, 3]);
998 let b = broadcast_to(&a, &[4, 2, 3]).unwrap();
999 assert_eq!(b.strides(), &[0, 3, 1]);
1000 assert!(!b.flags().contiguous);
1001 }
1002}