use crate::array::Array;
use crate::error::Result;
use crate::sealed::Sealed;
use crate::utils::guard::Guarded;
use crate::utils::{IntoOption, ScalarOrArray, VectorArray};
use crate::Stream;
use mlx_internal_macros::generate_macro;
use smallvec::SmallVec;
impl Array {
pub fn diff(&self, n: i32, axis: i32) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_diff(res, self.as_ptr(), n, axis, stream.as_ref().as_ptr())
})
}
pub fn trunc(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_trunc(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
pub fn abs(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_abs(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `abs`"
)]
pub fn abs_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.abs())
}
pub fn add(&self, other: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_add(
res,
self.as_ptr(),
other.as_ref().as_ptr(),
stream.as_ref().as_ptr(),
)
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `add`"
)]
pub fn add_device(
&self,
other: impl AsRef<Array>,
stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.add(other))
}
pub fn subtract(&self, other: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_subtract(
res,
self.as_ptr(),
other.as_ref().as_ptr(),
stream.as_ref().as_ptr(),
)
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `subtract`"
)]
pub fn subtract_device(
&self,
other: impl AsRef<Array>,
stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.subtract(other))
}
pub fn negative(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_negative(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `negative`"
)]
pub fn negative_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.negative())
}
pub fn multiply(&self, other: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_multiply(
res,
self.as_ptr(),
other.as_ref().as_ptr(),
stream.as_ref().as_ptr(),
)
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `multiply`"
)]
pub fn multiply_device(
&self,
other: impl AsRef<Array>,
stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.multiply(other))
}
pub fn nan_to_num(
&self,
nan: impl IntoOption<f32>,
pos_inf: impl IntoOption<f32>,
neg_inf: impl IntoOption<f32>,
) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let pos_inf = pos_inf.into_option();
let neg_inf = neg_inf.into_option();
let pos_inf = mlx_sys::mlx_optional_float {
value: pos_inf.unwrap_or(0.0),
has_value: pos_inf.is_some(),
};
let neg_inf = mlx_sys::mlx_optional_float {
value: neg_inf.unwrap_or(0.0),
has_value: neg_inf.is_some(),
};
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_nan_to_num(
res,
self.as_ptr(),
nan.into_option().unwrap_or(0.),
pos_inf,
neg_inf,
stream.as_ref().as_ptr(),
)
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `nan_to_num`"
)]
pub fn nan_to_num_device(
&self,
nan: impl IntoOption<f32>,
pos_inf: impl IntoOption<f32>,
neg_inf: impl IntoOption<f32>,
stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.nan_to_num(nan, pos_inf, neg_inf))
}
pub fn divide(&self, other: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_divide(
res,
self.as_ptr(),
other.as_ref().as_ptr(),
stream.as_ref().as_ptr(),
)
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `divide`"
)]
pub fn divide_device(
&self,
other: impl AsRef<Array>,
stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.divide(other))
}
pub fn power(&self, other: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_power(
res,
self.as_ptr(),
other.as_ref().as_ptr(),
stream.as_ref().as_ptr(),
)
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `power`"
)]
pub fn power_device(
&self,
other: impl AsRef<Array>,
stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.power(other))
}
pub fn remainder(&self, other: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_remainder(
res,
self.as_ptr(),
other.as_ref().as_ptr(),
stream.as_ref().as_ptr(),
)
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `remainder`"
)]
pub fn remainder_device(
&self,
other: impl AsRef<Array>,
stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.remainder(other))
}
pub fn sqrt(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_sqrt(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `sqrt`"
)]
pub fn sqrt_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.sqrt())
}
pub fn cos(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_cos(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `cos`"
)]
pub fn cos_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.cos())
}
pub fn exp(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_exp(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `exp`"
)]
pub fn exp_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.exp())
}
pub fn floor(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_floor(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `floor`"
)]
pub fn floor_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.floor())
}
pub fn floor_divide(&self, other: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_floor_divide(
res,
self.as_ptr(),
other.as_ref().as_ptr(),
stream.as_ref().as_ptr(),
)
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `floor_divide`"
)]
pub fn floor_divide_device(
&self,
other: impl AsRef<Array>,
stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.floor_divide(other))
}
pub fn is_nan(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_isnan(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `is_nan`"
)]
pub fn is_nan_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.is_nan())
}
pub fn is_inf(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_isinf(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `is_inf`"
)]
pub fn is_inf_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.is_inf())
}
pub fn is_finite(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_isfinite(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `is_finite`"
)]
pub fn is_finite_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.is_finite())
}
pub fn is_neg_inf(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_isneginf(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `is_neg_inf`"
)]
pub fn is_neg_inf_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.is_neg_inf())
}
pub fn is_pos_inf(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_isposinf(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `is_pos_inf`"
)]
pub fn is_pos_inf_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.is_pos_inf())
}
pub fn log(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_log(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `log`"
)]
pub fn log_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.log())
}
pub fn log2(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_log2(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `log2`"
)]
pub fn log2_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.log2())
}
pub fn log10(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_log10(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `log10`"
)]
pub fn log10_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.log10())
}
pub fn log1p(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_log1p(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `log1p`"
)]
pub fn log1p_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.log1p())
}
pub fn matmul(&self, other: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_matmul(
res,
self.as_ptr(),
other.as_ref().as_ptr(),
stream.as_ref().as_ptr(),
)
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `matmul`"
)]
pub fn matmul_device(
&self,
other: impl AsRef<Array>,
stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.matmul(other))
}
pub fn reciprocal(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_reciprocal(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `reciprocal`"
)]
pub fn reciprocal_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.reciprocal())
}
pub fn round(&self, decimals: impl Into<Option<i32>>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_round(
res,
self.as_ptr(),
decimals.into().unwrap_or(0),
stream.as_ref().as_ptr(),
)
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `round`"
)]
pub fn round_device(
&self,
decimals: impl Into<Option<i32>>,
stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.round(decimals))
}
pub fn rsqrt(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_rsqrt(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `rsqrt`"
)]
pub fn rsqrt_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.rsqrt())
}
pub fn sin(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_sin(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `sin`"
)]
pub fn sin_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.sin())
}
pub fn square(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_square(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `square`"
)]
pub fn square_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.square())
}
pub fn real(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_real(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `real`"
)]
pub fn real_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.real())
}
pub fn imag(&self) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_imag(res, self.as_ptr(), stream.as_ref().as_ptr())
})
}
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `imag`"
)]
pub fn imag_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || self.imag())
}
}
pub fn abs(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().abs()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `abs`"
)]
pub fn abs_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || abs(a))
}
pub fn acos(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_arccos(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `acos`"
)]
pub fn acos_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || acos(a))
}
pub fn acosh(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_arccosh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `acosh`"
)]
pub fn acosh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || acosh(a))
}
pub fn vecdot(lhs: impl AsRef<Array>, rhs: impl AsRef<Array>, axis: i32) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_vecdot(
res,
lhs.as_ref().as_ptr(),
rhs.as_ref().as_ptr(),
axis,
stream.as_ref().as_ptr(),
)
})
}
pub fn add(lhs: impl AsRef<Array>, rhs: impl AsRef<Array>) -> Result<Array> {
lhs.as_ref().add(rhs)
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `add`"
)]
pub fn add_device(
lhs: impl AsRef<Array>,
rhs: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || add(lhs, rhs))
}
pub fn asin(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_arcsin(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `asin`"
)]
pub fn asin_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || asin(a))
}
pub fn asinh(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_arcsinh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `asinh`"
)]
pub fn asinh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || asinh(a))
}
pub fn atan(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_arctan(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `atan`"
)]
pub fn atan_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || atan(a))
}
pub fn atan2(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let a = a.as_ref();
let b = b.as_ref();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_arctan2(res, a.as_ptr(), b.as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `atan2`"
)]
pub fn atan2_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || atan2(a, b))
}
pub fn atanh(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_arctanh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `atanh`"
)]
pub fn atanh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || atanh(a))
}
pub fn ceil(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_ceil(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `ceil`"
)]
pub fn ceil_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || ceil(a))
}
pub trait ClipBound<'min, 'max>: Sealed {
fn into_min_max(
self,
) -> (
Option<impl ScalarOrArray<'min>>,
Option<impl ScalarOrArray<'max>>,
);
}
impl<'min, Min> ClipBound<'min, 'min> for (Min, ())
where
Min: ScalarOrArray<'min> + Sealed,
{
fn into_min_max(
self,
) -> (
Option<impl ScalarOrArray<'min>>,
Option<impl ScalarOrArray<'min>>,
) {
(Some(self.0), Option::<Min>::None)
}
}
impl<'max, Max> ClipBound<'max, 'max> for ((), Max)
where
Max: ScalarOrArray<'max> + Sealed,
{
fn into_min_max(
self,
) -> (
Option<impl ScalarOrArray<'max>>,
Option<impl ScalarOrArray<'max>>,
) {
(Option::<Max>::None, Some(self.1))
}
}
impl<'min, 'max, Min, Max> ClipBound<'min, 'max> for (Min, Max)
where
Min: ScalarOrArray<'min> + Sealed,
Max: ScalarOrArray<'max> + Sealed,
{
fn into_min_max(
self,
) -> (
Option<impl ScalarOrArray<'min>>,
Option<impl ScalarOrArray<'max>>,
) {
(Some(self.0), Some(self.1))
}
}
pub fn clip<'min, 'max>(a: impl AsRef<Array>, bound: impl ClipBound<'min, 'max>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let (a_min, a_max) = bound.into_min_max();
let a_min = a_min.map(|min| min.into_owned_or_ref_array());
let a_max = a_max.map(|max| max.into_owned_or_ref_array());
unsafe {
let min_ptr = match &a_min {
Some(a_min) => a_min.as_ref().as_ptr(),
None => mlx_sys::mlx_array_new(),
};
let max_ptr = match &a_max {
Some(a_max) => a_max.as_ref().as_ptr(),
None => mlx_sys::mlx_array_new(),
};
Array::try_from_op(|res| {
mlx_sys::mlx_clip(
res,
a.as_ref().as_ptr(),
min_ptr,
max_ptr,
stream.as_ref().as_ptr(),
)
})
}
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `clip`"
)]
pub fn clip_device<'min, 'max>(
a: impl AsRef<Array>,
bound: impl ClipBound<'min, 'max>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || clip(a, bound))
}
pub fn cos(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().cos()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `cos`"
)]
pub fn cos_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || cos(a))
}
pub fn cosh(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_cosh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `cosh`"
)]
pub fn cosh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || cosh(a))
}
pub fn degrees(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_degrees(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `degrees`"
)]
pub fn degrees_device(
a: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || degrees(a))
}
pub fn divide(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
a.as_ref().divide(b)
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `divide`"
)]
pub fn divide_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || divide(a, b))
}
pub fn divmod(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<(Array, Array)> {
let stream = Stream::thread_local_or_default();
let a_ptr = a.as_ref().as_ptr();
let b_ptr = b.as_ref().as_ptr();
let vec = VectorArray::try_from_op(|res| unsafe {
mlx_sys::mlx_divmod(res, a_ptr, b_ptr, stream.as_ref().as_ptr())
})?;
let vals: SmallVec<[_; 2]> = vec.try_into_values()?;
let mut iter = vals.into_iter();
let quotient = iter.next().unwrap();
let remainder = iter.next().unwrap();
Ok((quotient, remainder))
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `divmod`"
)]
pub fn divmod_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<(Array, Array)> {
crate::with_stream(stream.as_ref(), || divmod(a, b))
}
pub fn erf(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_erf(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `erf`"
)]
pub fn erf_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || erf(a))
}
pub fn erfinv(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_erfinv(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `erfinv`"
)]
pub fn erfinv_device(
a: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || erfinv(a))
}
pub fn exp(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().exp()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `exp`"
)]
pub fn exp_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || exp(a))
}
pub fn expm1(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_expm1(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `expm1`"
)]
pub fn expm1_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || expm1(a))
}
pub fn floor(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().floor()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `floor`"
)]
pub fn floor_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || floor(a))
}
pub fn floor_divide(a: impl AsRef<Array>, other: impl AsRef<Array>) -> Result<Array> {
a.as_ref().floor_divide(other)
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `floor_divide`"
)]
pub fn floor_divide_device(
a: impl AsRef<Array>,
other: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || floor_divide(a, other))
}
pub fn log(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().log()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `log`"
)]
pub fn log_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || log(a))
}
pub fn log10(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().log10()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `log10`"
)]
pub fn log10_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || log10(a))
}
pub fn log1p(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().log1p()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `log1p`"
)]
pub fn log1p_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || log1p(a))
}
pub fn log2(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().log2()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `log2`"
)]
pub fn log2_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || log2(a))
}
pub fn logaddexp(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let a_ptr = a.as_ref().as_ptr();
let b_ptr = b.as_ref().as_ptr();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_logaddexp(res, a_ptr, b_ptr, stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `logaddexp`"
)]
pub fn logaddexp_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || logaddexp(a, b))
}
pub fn matmul(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
a.as_ref().matmul(b)
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `matmul`"
)]
pub fn matmul_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || matmul(a, b))
}
pub fn segmented_mm(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
segments: impl AsRef<Array>,
) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_segmented_mm(
res,
a.as_ref().as_ptr(),
b.as_ref().as_ptr(),
segments.as_ref().as_ptr(),
stream.as_ref().as_ptr(),
)
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `segmented_mm`"
)]
pub fn segmented_mm_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
segments: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || segmented_mm(a, b, segments))
}
pub fn maximum(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let a_ptr = a.as_ref().as_ptr();
let b_ptr = b.as_ref().as_ptr();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_maximum(res, a_ptr, b_ptr, stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `maximum`"
)]
pub fn maximum_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || maximum(a, b))
}
pub fn minimum(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let a_ptr = a.as_ref().as_ptr();
let b_ptr = b.as_ref().as_ptr();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_minimum(res, a_ptr, b_ptr, stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `minimum`"
)]
pub fn minimum_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || minimum(a, b))
}
pub fn multiply(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
a.as_ref().multiply(b)
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `multiply`"
)]
pub fn multiply_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || multiply(a, b))
}
pub fn negative(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().negative()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `negative`"
)]
pub fn negative_device(
a: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || negative(a))
}
pub fn power(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
a.as_ref().power(b)
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `power`"
)]
pub fn power_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || power(a, b))
}
pub fn radians(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_radians(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `radians`"
)]
pub fn radians_device(
a: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || radians(a))
}
pub fn reciprocal(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().reciprocal()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `reciprocal`"
)]
pub fn reciprocal_device(
a: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || reciprocal(a))
}
pub fn remainder(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
a.as_ref().remainder(b)
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `remainder`"
)]
pub fn remainder_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || remainder(a, b))
}
pub fn round(a: impl AsRef<Array>, decimals: impl Into<Option<i32>>) -> Result<Array> {
a.as_ref().round(decimals)
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `round`"
)]
pub fn round_device(
a: impl AsRef<Array>,
decimals: impl Into<Option<i32>>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || round(a, decimals))
}
pub fn rsqrt(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().rsqrt()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `rsqrt`"
)]
pub fn rsqrt_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || rsqrt(a))
}
pub fn sigmoid(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_sigmoid(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `sigmoid`"
)]
pub fn sigmoid_device(
a: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || sigmoid(a))
}
pub fn sign(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_sign(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `sign`"
)]
pub fn sign_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || sign(a))
}
pub fn sin(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().sin()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `sin`"
)]
pub fn sin_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || sin(a))
}
pub fn sinh(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_sinh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `sinh`"
)]
pub fn sinh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || sinh(a))
}
pub fn softmax_axes(
a: impl AsRef<Array>,
axes: &[i32],
precise: impl Into<Option<bool>>,
) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let precise = precise.into().unwrap_or(false);
let s = stream.as_ref().as_ptr();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_softmax_axes(
res,
a.as_ref().as_ptr(),
axes.as_ptr(),
axes.len(),
precise,
s,
)
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `softmax_axes`"
)]
pub fn softmax_axes_device(
a: impl AsRef<Array>,
axes: &[i32],
precise: impl Into<Option<bool>>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || softmax_axes(a, axes, precise))
}
pub fn softmax_axis(
a: impl AsRef<Array>,
axis: i32,
precise: impl Into<Option<bool>>,
) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let precise = precise.into().unwrap_or(false);
let s = stream.as_ref().as_ptr();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_softmax_axis(res, a.as_ref().as_ptr(), axis, precise, s)
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `softmax_axis`"
)]
pub fn softmax_axis_device(
a: impl AsRef<Array>,
axis: i32,
precise: impl Into<Option<bool>>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || softmax_axis(a, axis, precise))
}
pub fn softmax(a: impl AsRef<Array>, precise: impl Into<Option<bool>>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let precise = precise.into().unwrap_or(false);
let s = stream.as_ref().as_ptr();
Array::try_from_op(|res| unsafe { mlx_sys::mlx_softmax(res, a.as_ref().as_ptr(), precise, s) })
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `softmax`"
)]
pub fn softmax_device(
a: impl AsRef<Array>,
precise: impl Into<Option<bool>>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || softmax(a, precise))
}
pub fn sqrt(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().sqrt()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `sqrt`"
)]
pub fn sqrt_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || sqrt(a))
}
pub fn square(a: impl AsRef<Array>) -> Result<Array> {
a.as_ref().square()
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `square`"
)]
pub fn square_device(
a: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || square(a))
}
pub fn subtract(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
a.as_ref().subtract(b)
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `subtract`"
)]
pub fn subtract_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || subtract(a, b))
}
pub fn tan(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_tan(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `tan`"
)]
pub fn tan_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || tan(a))
}
pub fn tanh(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_tanh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `tanh`"
)]
pub fn tanh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || tanh(a))
}
pub fn real(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_real(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `real`"
)]
pub fn real_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || real(a))
}
pub fn imag(a: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_imag(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `imag`"
)]
pub fn imag_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
crate::with_stream(stream.as_ref(), || imag(a))
}
pub fn block_masked_mm<'mo, 'lhs, 'rhs>(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
block_size: impl Into<Option<i32>>,
mask_out: impl Into<Option<&'mo Array>>,
mask_lhs: impl Into<Option<&'lhs Array>>,
mask_rhs: impl Into<Option<&'rhs Array>>,
) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let a_ptr = a.as_ref().as_ptr();
let b_ptr = b.as_ref().as_ptr();
unsafe {
let mask_out_ptr = mask_out
.into()
.map(|m| m.as_ptr())
.unwrap_or(mlx_sys::mlx_array_new());
let mask_lhs_ptr = mask_lhs
.into()
.map(|m| m.as_ptr())
.unwrap_or(mlx_sys::mlx_array_new());
let mask_rhs_ptr = mask_rhs
.into()
.map(|m| m.as_ptr())
.unwrap_or(mlx_sys::mlx_array_new());
Array::try_from_op(|res| {
mlx_sys::mlx_block_masked_mm(
res,
a_ptr,
b_ptr,
block_size.into().unwrap_or(32),
mask_out_ptr,
mask_lhs_ptr,
mask_rhs_ptr,
stream.as_ref().as_ptr(),
)
})
}
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `block_masked_mm`"
)]
pub fn block_masked_mm_device<'mo, 'lhs, 'rhs>(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] block_size: impl Into<Option<i32>>,
#[optional] mask_out: impl Into<Option<&'mo Array>>,
#[optional] mask_lhs: impl Into<Option<&'lhs Array>>,
#[optional] mask_rhs: impl Into<Option<&'rhs Array>>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || {
block_masked_mm(a, b, block_size, mask_out, mask_lhs, mask_rhs)
})
}
pub fn addmm(
c: impl AsRef<Array>,
a: impl AsRef<Array>,
b: impl AsRef<Array>,
alpha: impl Into<Option<f32>>,
beta: impl Into<Option<f32>>,
) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let c_ptr = c.as_ref().as_ptr();
let a_ptr = a.as_ref().as_ptr();
let b_ptr = b.as_ref().as_ptr();
let alpha = alpha.into().unwrap_or(1.0);
let beta = beta.into().unwrap_or(1.0);
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_addmm(
res,
c_ptr,
a_ptr,
b_ptr,
alpha,
beta,
stream.as_ref().as_ptr(),
)
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `addmm`"
)]
pub fn addmm_device(
c: impl AsRef<Array>,
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] alpha: impl Into<Option<f32>>,
#[optional] beta: impl Into<Option<f32>>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || addmm(c, a, b, alpha, beta))
}
pub fn inner(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let a = a.as_ref();
let b = b.as_ref();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_inner(res, a.as_ptr(), b.as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `inner`"
)]
pub fn inner_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || inner(a, b))
}
pub fn outer(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let a = a.as_ref();
let b = b.as_ref();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_outer(res, a.as_ptr(), b.as_ptr(), stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `outer`"
)]
pub fn outer_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || outer(a, b))
}
pub fn tensordot_axes(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
axes_a: &[i32],
axes_b: &[i32],
) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let a = a.as_ref();
let b = b.as_ref();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_tensordot(
res,
a.as_ptr(),
b.as_ptr(),
axes_a.as_ptr(),
axes_a.len(),
axes_b.as_ptr(),
axes_b.len(),
stream.as_ref().as_ptr(),
)
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `tensordot_axes`"
)]
pub fn tensordot_axes_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
axes_a: &[i32],
axes_b: &[i32],
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || tensordot_axes(a, b, axes_a, axes_b))
}
pub fn tensordot_axis(a: impl AsRef<Array>, b: impl AsRef<Array>, axis: i32) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let a = a.as_ref();
let b = b.as_ref();
Array::try_from_op(|res| unsafe {
mlx_sys::mlx_tensordot_axis(res, a.as_ptr(), b.as_ptr(), axis, stream.as_ref().as_ptr())
})
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `tensordot_axis`"
)]
pub fn tensordot_axis_device(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
axis: i32,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || tensordot_axis(a, b, axis))
}
pub fn gather_mm<'lhs, 'rhs>(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
lhs_indices: impl Into<Option<&'lhs Array>>,
rhs_indices: impl Into<Option<&'rhs Array>>,
sorted_indices: impl Into<Option<bool>>,
) -> Result<Array> {
let stream = Stream::thread_local_or_default();
let a_ptr = a.as_ref().as_ptr();
let b_ptr = b.as_ref().as_ptr();
let sorted = sorted_indices.into().unwrap_or(false);
unsafe {
let lhs_ptr = lhs_indices
.into()
.map(|i| i.as_ptr())
.unwrap_or(mlx_sys::mlx_array_new());
let rhs_ptr = rhs_indices
.into()
.map(|i| i.as_ptr())
.unwrap_or(mlx_sys::mlx_array_new());
Array::try_from_op(|res| {
mlx_sys::mlx_gather_mm(
res,
a_ptr,
b_ptr,
lhs_ptr,
rhs_ptr,
sorted,
stream.as_ref().as_ptr(),
)
})
}
}
#[generate_macro(customize(forwarding_shim = true))]
#[deprecated(
since = "0.26.0",
note = "use `with_stream` or `with_device` around `gather_mm`"
)]
pub fn gather_mm_device<'lhs, 'rhs>(
a: impl AsRef<Array>,
b: impl AsRef<Array>,
#[optional] lhs_indices: impl Into<Option<&'lhs Array>>,
#[optional] rhs_indices: impl Into<Option<&'rhs Array>>,
#[optional] sorted_indices: impl Into<Option<bool>>,
#[optional] stream: impl AsRef<Stream>,
) -> Result<Array> {
crate::with_stream(stream.as_ref(), || {
gather_mm(a, b, lhs_indices, rhs_indices, sorted_indices)
})
}
#[cfg(test)]
mod tests {
use std::f32::consts::PI;
use super::*;
use crate::{
array, complex64,
ops::{all_close, arange, broadcast_to, eye, full, linspace, ones, reshape, split_equal},
test_utils::{assert_array_eq, assert_array_eq_with_context, tolerances},
transforms::eval,
Dtype,
};
use float_eq::assert_float_eq;
use pretty_assertions::assert_eq;
#[test]
fn test_abs() {
let data = [1i32, 2, -3, -4, -5];
let array = Array::from_slice(&data, &[5]);
let result = array.abs().unwrap();
let data: &[i32] = result.as_slice();
assert_eq!(data, [1, 2, 3, 4, 5]);
let data: &[i32] = array.as_slice();
assert_eq!(data, [1, 2, -3, -4, -5]);
}
#[test]
fn test_add() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
let c = &a + &b;
let c_data: &[f32] = c.as_slice();
assert_eq!(c_data, &[5.0, 7.0, 9.0]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 3.0]);
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[4.0, 5.0, 6.0]);
}
#[test]
fn test_add_invalid_broadcast() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = Array::from_slice(&[4.0, 5.0], &[2]);
let c = a.add(&b);
assert!(c.is_err());
}
#[test]
fn test_sub() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
let c = &a - &b;
let c_data: &[f32] = c.as_slice();
assert_eq!(c_data, &[-3.0, -3.0, -3.0]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 3.0]);
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[4.0, 5.0, 6.0]);
}
#[test]
fn test_sub_invalid_broadcast() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = Array::from_slice(&[4.0, 5.0], &[2]);
let c = a.subtract(&b);
assert!(c.is_err());
}
#[test]
fn test_neg() {
let a = Array::from_slice::<f32>(&[1.0, 2.0, 3.0], &[3]);
let b = a.negative().unwrap();
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[-1.0, -2.0, -3.0]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 3.0]);
}
#[test]
fn test_neg_bool() {
let a = Array::from_slice(&[true, false, true], &[3]);
let b = a.negative();
assert!(b.is_err());
}
#[test]
fn test_logical_not() {
let a: Array = false.into();
let b = a.logical_not().unwrap();
let b_data: &[bool] = b.as_slice();
assert_eq!(b_data, [true]);
}
#[test]
fn test_mul() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
let c = &a * &b;
let c_data: &[f32] = c.as_slice();
assert_eq!(c_data, &[4.0, 10.0, 18.0]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 3.0]);
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[4.0, 5.0, 6.0]);
}
#[test]
fn test_mul_invalid_broadcast() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = Array::from_slice(&[4.0, 5.0], &[2]);
let c = a.multiply(&b);
assert!(c.is_err());
}
#[test]
fn test_nan_to_num() {
let a = array!([1.0, 2.0, f32::NAN, 4.0, 5.0]);
let b = a.nan_to_num(0.0, 1.0, 0.0).unwrap();
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[1.0, 2.0, 0.0, 4.0, 5.0]);
}
#[test]
fn test_div() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
let c = &a / &b;
let c_data: &[f32] = c.as_slice();
assert_eq!(c_data, &[0.25, 0.4, 0.5]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 3.0]);
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[4.0, 5.0, 6.0]);
}
#[test]
fn test_div_invalid_broadcast() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = Array::from_slice(&[4.0, 5.0], &[2]);
let c = a.divide(&b);
assert!(c.is_err());
}
#[test]
fn test_pow() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = Array::from_slice(&[2.0, 3.0, 4.0], &[3]);
let c = a.power(&b).unwrap();
assert_array_eq(
&c,
array!([1.0, 8.0, 81.0]),
tolerances::MLX_DEFAULT.rtol,
tolerances::MLX_DEFAULT.atol,
);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 3.0]);
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[2.0, 3.0, 4.0]);
}
#[test]
fn test_pow_invalid_broadcast() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = Array::from_slice(&[2.0, 3.0], &[2]);
let c = a.power(&b);
assert!(c.is_err());
}
#[test]
fn test_rem() {
let a = Array::from_slice(&[10.0, 11.0, 12.0], &[3]);
let b = Array::from_slice(&[3.0, 4.0, 5.0], &[3]);
let c = &a % &b;
let c_data: &[f32] = c.as_slice();
assert_eq!(c_data, &[1.0, 3.0, 2.0]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[10.0, 11.0, 12.0]);
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[3.0, 4.0, 5.0]);
}
#[test]
fn test_rem_invalid_broadcast() {
let a = Array::from_slice(&[10.0, 11.0, 12.0], &[3]);
let b = Array::from_slice(&[3.0, 4.0], &[2]);
let c = a.remainder(&b);
assert!(c.is_err());
}
#[test]
fn test_sqrt() {
let a = Array::from_slice(&[1.0, 4.0, 9.0], &[3]);
let b = a.sqrt().unwrap();
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[1.0, 2.0, 3.0]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 4.0, 9.0]);
}
#[test]
fn test_cos() {
let a = Array::from_slice(&[0.0, 1.0, 2.0], &[3]);
let b = a.cos().unwrap();
let b_expected = array!([1.0, 0.54030234, -0.41614687]);
assert_array_all_close!(b, b_expected);
let a_expected = array!([0.0, 1.0, 2.0]);
assert_array_all_close!(a, a_expected);
}
#[test]
fn test_exp() {
let a = Array::from_slice(&[0.0, 1.0, 2.0], &[3]);
let b = a.exp().unwrap();
let b_expected = array!([1.0, 2.7182817, 7.389056]);
assert_array_all_close!(b, b_expected);
let a_expected = array!([0.0, 1.0, 2.0]);
assert_array_all_close!(a, a_expected);
}
#[test]
fn test_floor() {
let a = Array::from_slice(&[0.1, 1.9, 2.5], &[3]);
let b = a.floor().unwrap();
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[0.0, 1.0, 2.0]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[0.1, 1.9, 2.5]);
}
#[test]
fn test_floor_complex64() {
let val = complex64::new(1.0, 2.0);
let a = Array::from_complex(val);
let b = a.floor();
assert!(b.is_err());
}
#[test]
fn test_floor_divide() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
let c = a.floor_divide(&b).unwrap();
let c_data: &[f32] = c.as_slice();
assert_eq!(c_data, &[0.0, 0.0, 0.0]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 3.0]);
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[4.0, 5.0, 6.0]);
}
#[test]
fn test_floor_divide_complex64() {
let val = complex64::new(1.0, 2.0);
let a = Array::from_complex(val);
let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
let c = a.floor_divide(&b);
assert!(c.is_err());
}
#[test]
fn test_floor_divide_invalid_broadcast() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = Array::from_slice(&[4.0, 5.0], &[2]);
let c = a.floor_divide(&b);
assert!(c.is_err());
}
#[test]
fn test_is_nan() {
let a = Array::from_slice(&[1.0, f32::NAN, 3.0], &[3]);
let b = a.is_nan().unwrap();
let b_data: &[bool] = b.as_slice();
assert_eq!(b_data, &[false, true, false]);
}
#[test]
fn test_is_inf() {
let a = Array::from_slice(&[1.0, f32::INFINITY, 3.0], &[3]);
let b = a.is_inf().unwrap();
let b_data: &[bool] = b.as_slice();
assert_eq!(b_data, &[false, true, false]);
}
#[test]
fn test_is_finite() {
let a = Array::from_slice(&[1.0, f32::INFINITY, 3.0], &[3]);
let b = a.is_finite().unwrap();
let b_data: &[bool] = b.as_slice();
assert_eq!(b_data, &[true, false, true]);
}
#[test]
fn test_is_neg_inf() {
let a = Array::from_slice(&[1.0, f32::NEG_INFINITY, 3.0], &[3]);
let b = a.is_neg_inf().unwrap();
let b_data: &[bool] = b.as_slice();
assert_eq!(b_data, &[false, true, false]);
}
#[test]
fn test_is_pos_inf() {
let a = Array::from_slice(&[1.0, f32::INFINITY, 3.0], &[3]);
let b = a.is_pos_inf().unwrap();
let b_data: &[bool] = b.as_slice();
assert_eq!(b_data, &[false, true, false]);
}
#[test]
fn test_log() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = a.log().unwrap();
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[0.0, 0.6931472, 1.0986123]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 3.0]);
}
#[test]
fn test_log2() {
let a = Array::from_slice(&[1.0, 2.0, 4.0, 8.0], &[4]);
let b = a.log2().unwrap();
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[0.0, 1.0, 2.0, 3.0]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 4.0, 8.0]);
}
#[test]
fn test_log10() {
let a = Array::from_slice(&[1.0, 10.0, 100.0], &[3]);
let b = a.log10().unwrap();
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[0.0, 1.0, 2.0]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 10.0, 100.0]);
}
#[test]
fn test_log1p() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = a.log1p().unwrap();
assert_array_eq(
&b,
array!([0.6931472, 1.0986123, 1.3862944]),
tolerances::MLX_DEFAULT.rtol,
tolerances::MLX_DEFAULT.atol,
);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 3.0]);
}
#[test]
fn test_matmul() {
let a = Array::from_slice(&[1, 2, 3, 4], &[2, 2]);
let b = Array::from_slice(&[-5.0, 37.5, 4., 7., 1., 0.], &[2, 3]);
let c = a.matmul(&b).unwrap();
assert_eq!(c.shape(), &[2, 3]);
let c_data: &[f32] = c.as_slice();
assert_eq!(c_data, &[9.0, 39.5, 4.0, 13.0, 116.5, 12.0]);
let a_data: &[i32] = a.as_slice();
assert_eq!(a_data, &[1, 2, 3, 4]);
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[-5.0, 37.5, 4., 7., 1., 0.]);
}
#[test]
fn test_matmul_ndim_zero() {
let a: Array = 1.0.into();
let b = Array::from_slice::<i32>(&[1], &[1]);
let c = a.matmul(&b);
assert!(c.is_err());
}
#[test]
fn test_matmul_ndim_one() {
let a = Array::from_slice(&[1.0, 2.0, 3.0, 4.0], &[4]);
let b = Array::from_slice(&[1.0, 2.0, 3.0, 4.0], &[4]);
let c = a.matmul(&b);
assert!(c.is_ok());
}
#[test]
fn test_matmul_dim_mismatch() {
let a = Array::from_slice(&[1, 2, 3, 4, 5, 6], &[2, 3]);
let b = Array::from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], &[2, 5]);
let c = a.matmul(&b);
assert!(c.is_err());
}
#[test]
fn test_matmul_non_float_output_type() {
let a = Array::from_slice(&[1, 2, 3, 4], &[2, 2]);
let b = Array::from_slice(&[5, 37, 4, 7, 1, 0], &[2, 3]);
let c = a.matmul(&b);
assert!(c.is_err());
}
#[test]
fn test_reciprocal() {
let a = Array::from_slice(&[1.0, 2.0, 4.0], &[3]);
let b = a.reciprocal().unwrap();
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[1.0, 0.5, 0.25]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 4.0]);
}
#[test]
fn test_round() {
let a = Array::from_slice(&[1.1, 2.9, 3.5], &[3]);
let b = a.round(None).unwrap();
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[1.0, 3.0, 4.0]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.1, 2.9, 3.5]);
}
#[test]
fn test_rsqrt() {
let a = Array::from_slice(&[1.0, 2.0, 4.0], &[3]);
let b = a.rsqrt().unwrap();
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[1.0, 0.70710677, 0.5]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 4.0]);
}
#[test]
fn test_sin() {
let a = Array::from_slice(&[0.0, 1.0, 2.0], &[3]);
let b = a.sin().unwrap();
assert_array_eq(
&b,
array!([0.0, 0.841471, 0.9092974]),
tolerances::MLX_DEFAULT.rtol,
tolerances::MLX_DEFAULT.atol,
);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[0.0, 1.0, 2.0]);
}
#[test]
fn test_square() {
let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
let b = a.square().unwrap();
let b_data: &[f32] = b.as_slice();
assert_eq!(b_data, &[1.0, 4.0, 9.0]);
let a_data: &[f32] = a.as_slice();
assert_eq!(a_data, &[1.0, 2.0, 3.0]);
}
#[test]
fn test_unary_neg() {
let x = array!(1.0);
assert_eq!(negative(&x).unwrap().item_exact::<f32>(), -1.0);
assert_eq!((-x).item_exact::<f32>(), -1.0);
assert_array_eq(
-array!(),
array!(),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array!(true);
assert!(negative(&x).is_err());
}
#[test]
fn test_unary_abs() {
let x = array!([-1.0, 0.0, 1.0]);
assert_array_eq(
abs(&x).unwrap(),
array!([1.0, 0.0, 1.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
assert_array_eq(
abs(array!()).unwrap(),
array!(),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array!([-1, 0, 1]);
assert_array_eq(
abs(&x).unwrap(),
array!([1, 0, 1]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array!([1u32, 0, 1]);
assert_array_eq(
abs(&x).unwrap(),
array!([1u32, 0, 1]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array!([false, true]);
assert_array_eq(
abs(&x).unwrap(),
array!([false, true]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
}
#[test]
fn test_unary_sign() {
let x = array!([-1.0, 0.0, 1.0]);
assert_array_eq(
sign(&x).unwrap(),
x,
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
assert_array_eq(
sign(array!()).unwrap(),
array!(),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array!([-1, 0, 1]);
assert_array_eq(
sign(&x).unwrap(),
x,
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array!([1u32, 0, 1]);
assert_array_eq(
sign(&x).unwrap(),
x,
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array!([false, true]);
assert_array_eq(
sign(&x).unwrap(),
x,
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
}
const NEG_INF: f32 = f32::NEG_INFINITY;
#[test]
fn test_unary_floor_ceil() {
let x = array![1.0];
assert_eq!(floor(&x).unwrap().item_exact::<f32>(), 1.0);
assert_eq!(ceil(&x).unwrap().item_exact::<f32>(), 1.0);
let x = array![1.5];
assert_eq!(floor(&x).unwrap().item_exact::<f32>(), 1.0);
assert_eq!(ceil(&x).unwrap().item_exact::<f32>(), 2.0);
let x = array![-1.5];
assert_eq!(floor(&x).unwrap().item_exact::<f32>(), -2.0);
assert_eq!(ceil(&x).unwrap().item_exact::<f32>(), -1.0);
let x = array![NEG_INF];
assert_eq!(floor(&x).unwrap().item_exact::<f32>(), NEG_INF);
assert_eq!(ceil(&x).unwrap().item_exact::<f32>(), NEG_INF);
let x = array!([1.0, 1.0]).as_type::<complex64>().unwrap();
assert!(floor(&x).is_err());
assert!(ceil(&x).is_err());
}
#[test]
fn test_unary_round() {
let x = array!([0.5, -0.5, 1.5, -1.5, 2.3, 2.6]);
assert_array_eq_with_context(
round(&x, None).unwrap(),
array!([0.0_f32, 0.0, 2.0, -2.0, 2.0, 3.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
"float32 half-to-even rounding",
);
let x = array!([11, 222, 32]);
assert_array_eq_with_context(
round(&x, -1).unwrap(),
array!([10, 220, 30]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
"int32 negative-decimal rounding",
);
}
#[test]
fn test_unary_exp() {
let x = array![0.0];
assert_eq!(exp(&x).unwrap().item_exact::<f32>(), 1.0);
let x = array![2.0];
assert_float_eq! {
exp(&x).unwrap().item_exact::<f32>(),
2.0f32.exp(),
abs <= 1e-5
};
assert_array_eq(
exp(array!()).unwrap(),
array!(),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array![NEG_INF];
assert_eq!(exp(&x).unwrap().item_exact::<f32>(), 0.0);
let x = array![2];
assert_eq!(x.dtype(), Dtype::Int32);
assert_float_eq! {
exp(&x).unwrap().item_exact::<f32>(),
2.0f32.exp(),
abs <= 1e-5
};
let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
let res = exp(&x).unwrap();
let expected = Array::full::<f32>(&[2, 2, 2], array!(1.0f32.exp())).unwrap();
assert!(all_close(&res, &expected, None, None, None).unwrap());
let data = Array::from_slice(&[0.0, 1.0, 2.0, 3.0], &[2, 2]);
let x = split_equal(&data, 2, 1).unwrap();
let expected = Array::from_slice(&[0.0f32.exp(), 2.0f32.exp()], &[2, 1]);
assert!(all_close(exp(&x[0]).unwrap(), &expected, None, None, None).unwrap());
}
#[test]
fn test_unary_expm1() {
let x = array![-1.0];
assert_float_eq! {
expm1(&x).unwrap().item_exact::<f32>(),
(-1.0f32).exp_m1(),
abs <= 1e-5
};
let x = array![1.0];
assert_float_eq! {
expm1(&x).unwrap().item_exact::<f32>(),
1.0f32.exp_m1(),
abs <= 1e-5
};
let x = array![1];
assert_eq!(expm1(&x).unwrap().dtype(), Dtype::Float32);
assert_float_eq! {
expm1(&x).unwrap().item_exact::<f32>(),
1.0f32.exp_m1(),
abs <= 1e-5
};
}
#[test]
fn test_unary_sin() {
let x = array![0.0];
assert_eq!(sin(&x).unwrap().item_exact::<f32>(), 0.0);
let x = array![std::f32::consts::PI / 2.0];
assert_float_eq! {
sin(&x).unwrap().item_exact::<f32>(),
(std::f32::consts::PI / 2.0f32).sin(),
abs <= 1e-5
};
assert_array_eq(
sin(array!()).unwrap(),
array!(),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array![0];
assert_eq!(x.dtype(), Dtype::Int32);
assert_float_eq! {
sin(&x).unwrap().item_exact::<f32>(),
0.0f32.sin(),
abs <= 1e-5
};
let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
let res = sin(&x).unwrap();
let expected = Array::full::<f32>(&[2, 2, 2], array!(1.0f32.sin())).unwrap();
assert!(all_close(&res, &expected, None, None, None).unwrap());
let data = Array::from_slice(&[0.0, 1.0, 2.0, 3.0], &[2, 2]);
let x = split_equal(&data, 2, 1).unwrap();
let expected = Array::from_slice(&[0.0f32.sin(), 2.0f32.sin()], &[2, 1]);
assert!(all_close(sin(&x[0]).unwrap(), &expected, None, None, None).unwrap());
}
#[test]
fn test_unary_cos() {
let x = array![0.0];
assert_float_eq! {
cos(&x).unwrap().item_exact::<f32>(),
0.0f32.cos(),
abs <= 1e-5
};
let x = array![std::f32::consts::PI / 2.0];
assert_float_eq! {
cos(&x).unwrap().item_exact::<f32>(),
(std::f32::consts::PI / 2.0f32).cos(),
abs <= 1e-5
};
assert_array_eq(
cos(array!()).unwrap(),
array!(),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array![0];
assert_eq!(x.dtype(), Dtype::Int32);
assert_float_eq! {
cos(&x).unwrap().item_exact::<f32>(),
0.0f32.cos(),
abs <= 1e-5
};
let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
let res = cos(&x).unwrap();
let expected = Array::full::<f32>(&[2, 2, 2], array!(1.0f32.cos())).unwrap();
assert!(all_close(&res, &expected, None, None, None).unwrap());
let data = Array::from_slice(&[0.0, 1.0, 2.0, 3.0], &[2, 2]);
let x = split_equal(&data, 2, 1).unwrap();
let expected = Array::from_slice(&[0.0f32.cos(), 2.0f32.cos()], &[2, 1]);
assert!(all_close(cos(&x[0]).unwrap(), &expected, None, None, None).unwrap());
}
#[test]
fn test_unary_degrees() {
let x = array![0.0];
assert_eq!(degrees(&x).unwrap().item_exact::<f32>(), 0.0);
let x = array![std::f32::consts::PI / 2.0];
assert_eq!(degrees(&x).unwrap().item_exact::<f32>(), 90.0);
assert_array_eq(
degrees(array!()).unwrap(),
array!(),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array![0];
assert_eq!(x.dtype(), Dtype::Int32);
assert_eq!(degrees(&x).unwrap().item_exact::<f32>(), 0.0);
let x = broadcast_to(&array!(std::f32::consts::PI / 2.0), &[2, 2, 2]).unwrap();
let res = degrees(&x).unwrap();
let expected = Array::full::<f32>(&[2, 2, 2], array!(90.0)).unwrap();
assert!(all_close(&res, &expected, None, None, None).unwrap());
let angles = Array::from_slice(&[0.0, PI / 2.0, PI, 1.5 * PI], &[2, 2]);
let x = split_equal(&angles, 2, 1).unwrap();
let expected = Array::from_slice(&[0.0, 180.0], &[2, 1]);
assert!(all_close(degrees(&x[0]).unwrap(), &expected, None, None, None).unwrap());
}
#[test]
fn test_unary_radians() {
let x = array![0.0];
assert_eq!(radians(&x).unwrap().item_exact::<f32>(), 0.0);
let x = array![90.0];
assert_eq!(
radians(&x).unwrap().item_exact::<f32>(),
std::f32::consts::PI / 2.0
);
assert_array_eq(
radians(array!()).unwrap(),
array!(),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array![90];
assert_eq!(x.dtype(), Dtype::Int32);
assert_eq!(
radians(&x).unwrap().item_exact::<f32>(),
std::f32::consts::PI / 2.0
);
let x = broadcast_to(&array!(90.0), &[2, 2, 2]).unwrap();
let res = radians(&x).unwrap();
let expected = Array::full::<f32>(&[2, 2, 2], array!(std::f32::consts::PI / 2.0)).unwrap();
assert!(all_close(&res, &expected, None, None, None).unwrap());
let angles = Array::from_slice(&[0.0, 90.0, 180.0, 270.0], &[2, 2]);
let x = split_equal(&angles, 2, 1).unwrap();
let expected = Array::from_slice(&[0.0, PI], &[2, 1]);
assert!(all_close(radians(&x[0]).unwrap(), &expected, None, None, None).unwrap());
}
#[test]
fn test_unary_log() {
let x = array![0.0];
assert_eq!(log(&x).unwrap().item_exact::<f32>(), NEG_INF);
let x = array![1.0];
assert_eq!(log(&x).unwrap().item_exact::<f32>(), 0.0);
let x = array![1];
assert_eq!(log(&x).unwrap().dtype(), Dtype::Float32);
assert_eq!(log(&x).unwrap().item_exact::<f32>(), 0.0);
let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
let res = log(&x).unwrap();
let expected = Array::full::<f32>(&[2, 2, 2], array!(0.0)).unwrap();
assert!(all_close(&res, &expected, None, None, None).unwrap());
let data = Array::from_slice(&[1.0, 2.0, 3.0, 4.0], &[2, 2]);
let x = split_equal(&data, 2, 1).unwrap();
let expected = Array::from_slice(&[1.0f32.ln(), 3.0f32.ln()], &[2, 1]);
assert!(all_close(log(&x[0]).unwrap(), &expected, None, None, None).unwrap());
}
#[test]
fn test_unary_log2() {
let x = array![0.0];
assert_eq!(log2(&x).unwrap().item_exact::<f32>(), NEG_INF);
let x = array![1.0];
assert_eq!(log2(&x).unwrap().item_exact::<f32>(), 0.0);
let x = array![1024.0];
assert_eq!(log2(&x).unwrap().item_exact::<f32>(), 10.0);
}
#[test]
fn test_unary_log10() {
let x = array![0.0];
assert_eq!(log10(&x).unwrap().item_exact::<f32>(), NEG_INF);
let x = array![1.0];
assert_eq!(log10(&x).unwrap().item_exact::<f32>(), 0.0);
let x = array![1000.0];
assert_eq!(log10(&x).unwrap().item_exact::<f32>(), 3.0);
}
#[test]
fn test_unary_log1p() {
let x = array![-1.0];
assert_float_eq! {
log1p(&x).unwrap().item_exact::<f32>(),
(-1.0f32).ln_1p(),
abs <= 1e-5
};
let x = array![1.0];
assert_float_eq! {
log1p(&x).unwrap().item_exact::<f32>(),
1.0f32.ln_1p(),
abs <= 1e-5
};
let x = array![1];
assert_eq!(log1p(&x).unwrap().dtype(), Dtype::Float32);
assert_float_eq! {
log1p(&x).unwrap().item_exact::<f32>(),
1.0f32.ln_1p(),
abs <= 1e-5
};
let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
let res = log1p(&x).unwrap();
let expected = Array::full::<f32>(&[2, 2, 2], array!(1.0f32.ln_1p())).unwrap();
assert!(all_close(&res, &expected, None, None, None).unwrap());
let data = Array::from_slice(&[1.0, 2.0, 3.0, 4.0], &[2, 2]);
let x = split_equal(&data, 2, 1).unwrap();
let expected = Array::from_slice(&[1.0f32.ln_1p(), 3.0f32.ln_1p()], &[2, 1]);
assert!(all_close(log1p(&x[0]).unwrap(), &expected, None, None, None).unwrap());
}
#[test]
fn test_unary_sigmoid() {
let x = array![0.0];
assert_float_eq! {
sigmoid(&x).unwrap().item_exact::<f32>(),
0.5,
abs <= 1e-5
};
let x = array![0];
assert_eq!(sigmoid(&x).unwrap().dtype(), Dtype::Float32);
assert_float_eq! {
sigmoid(&x).unwrap().item_exact::<f32>(),
0.5,
abs <= 1e-5
};
let inf = f32::INFINITY;
let x = array![inf];
assert_eq!(sigmoid(&x).unwrap().item_exact::<f32>(), 1.0);
let x = array![-inf];
assert_eq!(sigmoid(&x).unwrap().item_exact::<f32>(), 0.0);
}
#[test]
fn test_unary_square() {
let x = array![3.0];
assert_eq!(square(&x).unwrap().item_exact::<f32>(), 9.0);
let x = array![2];
assert_eq!(square(&x).unwrap().item_exact::<i32>(), 4);
let x = Array::full::<f32>(&[3, 3], array!(2.0)).unwrap();
assert!(all_close(
square(&x).unwrap(),
Array::full::<f32>(&[3, 3], array!(4.0)).unwrap(),
None,
None,
None
)
.unwrap());
}
#[test]
fn test_unary_sqrt_rsqrt() {
let x = array![4.0];
assert_eq!(sqrt(&x).unwrap().item_exact::<f32>(), 2.0);
assert_eq!(rsqrt(&x).unwrap().item_exact::<f32>(), 0.5);
let x = Array::full::<f32>(&[3, 3], array!(9.0)).unwrap();
assert!(all_close(
sqrt(&x).unwrap(),
Array::full::<f32>(&[3, 3], array!(3.0)).unwrap(),
None,
None,
None
)
.unwrap());
let x = array![4i32];
assert_eq!(sqrt(&x).unwrap().item_exact::<f32>(), 2.0);
assert_eq!(rsqrt(&x).unwrap().item_exact::<f32>(), 0.5);
}
#[test]
fn test_unary_reciprocal() {
let x = array![8.0];
assert_eq!(reciprocal(&x).unwrap().item_exact::<f32>(), 0.125);
let x = array![2];
let out = reciprocal(&x).unwrap();
assert_eq!(out.dtype(), Dtype::Float32);
assert_eq!(out.item_exact::<f32>(), 0.5);
let x = Array::full::<f32>(&[3, 3], array!(2.0)).unwrap();
assert!(all_close(
reciprocal(&x).unwrap(),
Array::full::<f32>(&[3, 3], array!(0.5)).unwrap(),
None,
None,
None
)
.unwrap());
}
#[test]
fn test_unary_real_imag() {
let x = Array::from_complex(complex64::new(0.0, 1.0));
assert_array_eq(
real(&x).unwrap(),
Array::from_f32(0.0),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
assert_array_eq(
imag(&x).unwrap(),
Array::from_f32(1.0),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
}
#[test]
fn test_binary_add() {
let x = array![1.0];
let y = array![1.0];
let z = add(&x, &y).unwrap();
assert_eq!(z.item_exact::<f32>(), 2.0);
let z = &x + y;
assert_eq!(z.item_exact::<f32>(), 2.0);
let z = add(z, &x).unwrap();
assert_eq!(z.item_exact::<f32>(), 3.0);
let mut out = x.deep_clone();
for _ in 0..10 {
out = add(&out, &x).unwrap();
}
assert_eq!(out.item_exact::<f32>(), 11.0);
let x = array!([1.0, 2.0, 3.0]);
let y = array!([1.0, 2.0, 3.0]);
let z = add(&x, &y).unwrap();
assert_eq!(z.shape(), &[3]);
assert_array_eq(
z,
array!([2.0, 4.0, 6.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array!([1.0, 2.0, 3.0]);
let y = &x + 2.0;
assert_eq!(y.dtype(), Dtype::Float32);
assert_array_eq(
y,
array!([3.0, 4.0, 5.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let y = &x + 2.0;
assert_eq!(y.dtype(), Dtype::Float32);
assert_array_eq(
y,
array!([3.0, 4.0, 5.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let y = x + 2;
assert_eq!(y.dtype(), Dtype::Float32);
let y = array!([1, 2, 3]) + 2.0;
assert_eq!(y.dtype(), Dtype::Float32);
assert_array_eq(
y,
array!([3.0, 4.0, 5.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = broadcast_to(&array!(1.0), &[10]).unwrap();
let y = broadcast_to(&array!(2.0), &[10]).unwrap();
let z = add(&x, &y).unwrap();
assert_array_eq(
z,
full::<f32>(&[10], array!(3.0)).unwrap(),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = Array::from_slice(&[1.0, 2.0], &[1, 2]);
let y = Array::from_slice(&[1.0, 2.0], &[2, 1]);
let z = add(&x, &y).unwrap();
assert_eq!(z.shape(), &[2, 2]);
assert_array_eq(
z,
Array::from_slice(&[2.0, 3.0, 3.0, 4.0], &[2, 2]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = ones::<f32>(&[3, 2, 1]).unwrap();
let z = x + 2.0;
assert_eq!(z.shape(), &[3, 2, 1]);
let expected = Array::from_slice(&[3.0, 3.0, 3.0, 3.0, 3.0, 3.0], &[3, 2, 1]);
assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
let x = array!();
let y = array!();
let z = x + y;
z.eval().unwrap();
assert_eq!(z.size(), 0);
assert_eq!(z.shape(), &[0]);
}
#[test]
fn test_binary_sub() {
let x = array!([3.0, 2.0, 1.0]);
let y = array!([1.0, 1.0, 1.0]);
assert_array_eq(
x - y,
array!([2.0, 1.0, 0.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
}
#[test]
fn test_binary_mul() {
let x = array!([1.0, 2.0, 3.0]);
let y = array!([2.0, 2.0, 2.0]);
assert_array_eq(
x * y,
array!([2.0, 4.0, 6.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
}
#[test]
fn test_binary_div() {
let x = array![1.0];
let y = array![1.0];
assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 1.0);
let x = array![1.0];
let y = array![0.5];
assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 2.0);
let x = array![1.0];
let y = array![4.0];
assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 0.25);
let x = array![true];
let y = array![true];
assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 1.0);
let x = array![false];
let y = array![true];
assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 0.0);
let x = array![true];
let y = array![false];
assert!(divide(&x, &y).unwrap().item_exact::<f32>().is_infinite());
let x = array![false];
let y = array![false];
assert!(divide(&x, &y).unwrap().item_exact::<f32>().is_nan());
}
#[test]
fn test_binary_maximum_minimum() {
let x = array![1.0];
let y = array![0.0];
assert_eq!(maximum(&x, &y).unwrap().item_exact::<f32>(), 1.0);
assert_eq!(minimum(&x, &y).unwrap().item_exact::<f32>(), 0.0);
let y = array![2.0];
assert_eq!(maximum(&x, &y).unwrap().item_exact::<f32>(), 2.0);
assert_eq!(minimum(&x, &y).unwrap().item_exact::<f32>(), 1.0);
}
#[test]
fn test_binary_logaddexp() {
let x = array![0.0];
let y = array![0.0];
assert_float_eq! {
logaddexp(&x, &y).unwrap().item_exact::<f32>(),
2.0f32.ln(),
abs <= 1e-5
};
let x = array!([0u32]);
let y = array!([10000u32]);
assert_eq!(logaddexp(&x, &y).unwrap().item_exact::<f32>(), 10000.0);
let x = array![f32::INFINITY];
let y = array![3.0];
assert_eq!(
logaddexp(&x, &y).unwrap().item_exact::<f32>(),
f32::INFINITY
);
let x = array![f32::NEG_INFINITY];
let y = array![3.0];
assert_eq!(logaddexp(&x, &y).unwrap().item_exact::<f32>(), 3.0);
let x = array![f32::NEG_INFINITY];
let y = array![f32::NEG_INFINITY];
assert_eq!(
logaddexp(&x, &y).unwrap().item_exact::<f32>(),
f32::NEG_INFINITY
);
let x = array![f32::INFINITY];
let y = array![f32::INFINITY];
assert_eq!(
logaddexp(&x, &y).unwrap().item_exact::<f32>(),
f32::INFINITY
);
let x = array![f32::NEG_INFINITY];
let y = array![f32::INFINITY];
assert_eq!(
logaddexp(&x, &y).unwrap().item_exact::<f32>(),
f32::INFINITY
);
}
#[test]
fn test_basic_clip() {
let a = array!([1.0, 4.0, 3.0, 8.0, 5.0]);
let expected = array!([2.0, 4.0, 3.0, 6.0, 5.0]);
let clipped = clip(&a, (array!(2.0), array!(6.0))).unwrap();
assert_array_eq(
clipped,
&expected,
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let clipped = clip(&a, (2.0, 6.0)).unwrap();
assert_array_eq(
clipped,
&expected,
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
}
#[test]
fn test_clip_with_only_min() {
let a = array!([-1.0, 1.0, 0.0, 5.0]);
let expected = array!([0.0, 1.0, 0.0, 5.0]);
let clipped = clip(&a, (array!(0.0), ())).unwrap();
assert_array_eq(
clipped,
&expected,
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let clipped = clip(&a, (0.0, ())).unwrap();
assert_array_eq(
clipped,
expected,
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
}
#[test]
fn test_clip_with_only_max() {
let a = array!([2.0, 3.0, 4.0, 5.0]);
let expected = array!([2.0, 3.0, 4.0, 4.0]);
let clipped = clip(&a, ((), array!(4.0))).unwrap();
assert_array_eq(
clipped,
&expected,
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let clipped = clip(&a, ((), 4.0)).unwrap();
assert_array_eq(
clipped,
expected,
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
}
#[test]
fn test_tensordot() {
let x = reshape(arange::<_, f32>(None, 60.0, None).unwrap(), &[3, 4, 5]).unwrap();
let y = reshape(arange::<_, f32>(None, 24.0, None).unwrap(), &[4, 3, 2]).unwrap();
let z = tensordot_axes(&x, &y, &[1i32, 0], &[0i32, 1]).unwrap();
let expected = Array::from_slice(
&[
4400.0_f32, 4730.0, 4532.0, 4874.0, 4664.0, 5018.0, 4796.0, 5162.0, 4928.0, 5306.0,
],
&[5, 2],
);
assert_array_eq_with_context(
z,
expected,
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
"float32 explicit-axis contraction",
);
let x = reshape(arange::<_, f32>(None, 360.0, None).unwrap(), &[3, 4, 5, 6]).unwrap();
let y = reshape(arange::<_, f32>(None, 360.0, None).unwrap(), &[6, 4, 5, 3]).unwrap();
assert!(tensordot_axes(&x, &y, &[2, 1, 3], &[1, 2, 0]).is_err());
let x = reshape(arange::<_, f32>(None, 60.0, None).unwrap(), &[3, 4, 5]).unwrap();
let y = reshape(arange::<_, f32>(None, 120.0, None).unwrap(), &[4, 5, 6]).unwrap();
let z = tensordot_axis(&x, &y, 2).unwrap();
let expected = Array::from_slice(
&[
14820.0, 15010.0, 15200.0, 15390.0, 15580.0, 15770.0, 37620.0, 38210.0, 38800.0,
39390.0, 39980.0, 40570.0, 60420.0, 61410.0, 62400.0, 63390.0, 64380.0, 65370.0,
],
&[3, 6],
);
assert_array_eq_with_context(
z,
expected,
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
"float32 axis-count contraction",
);
}
#[test]
fn test_outer() {
let x = arange::<_, f32>(1.0, 5.0, None).unwrap();
let y = arange::<_, f32>(1.0, 4.0, None).unwrap();
let z = outer(&x, &y).unwrap();
let expected = Array::from_slice(
&[1.0, 2.0, 3.0, 2.0, 4.0, 6.0, 3.0, 6.0, 9.0, 4.0, 8.0, 12.0],
&[4, 3],
);
assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
let x = ones::<f32>(&[5]).unwrap();
let y = linspace::<_, f32>(
-2.0,
2.0,
crate::ops::LinspaceOptions {
count: 5,
endpoint: true,
},
)
.unwrap();
let z = outer(&x, &y).unwrap();
let expected = Array::from_slice(
&[
-2.0, -1.0, 0.0, 1.0, 2.0, -2.0, -1.0, 0.0, 1.0, 2.0, -2.0, -1.0, 0.0, 1.0, 2.0,
-2.0, -1.0, 0.0, 1.0, 2.0, -2.0, -1.0, 0.0, 1.0, 2.0,
],
&[5, 5],
);
assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
}
#[test]
fn test_inner() {
let x = reshape(arange::<_, f32>(None, 5.0, None).unwrap(), &[1, 5]).unwrap();
let y = reshape(arange::<_, f32>(None, 6.0, None).unwrap(), &[2, 3]).unwrap();
assert!(inner(&x, &y).is_err());
let x = array!([1.0, 2.0, 3.0]);
let y = array!([0.0, 1.0, 0.0]);
let z = inner(&x, &y).unwrap();
assert_eq!(z.item_exact::<f32>(), 2.0);
let x = reshape(arange::<_, f32>(None, 24.0, None).unwrap(), &[2, 3, 4]).unwrap();
let y = arange::<_, f32>(None, 4.0, None).unwrap();
let z = inner(&x, &y).unwrap();
let expected = Array::from_slice(&[14.0, 38.0, 62.0, 86.0, 110.0, 134.0], &[2, 3]);
assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
let x = reshape(arange::<_, f32>(None, 2.0, None).unwrap(), &[1, 1, 2]).unwrap();
let y = reshape(arange::<_, f32>(None, 6.0, None).unwrap(), &[3, 2]).unwrap();
let z = inner(&x, &y).unwrap();
let expected = Array::from_slice(&[1.0, 3.0, 5.0], &[1, 1, 3]);
assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
let x = eye::<f32>(2, None, None).unwrap();
let y = Array::from_f32(7.0);
let z = inner(&x, &y).unwrap();
let expected = Array::from_slice(&[7.0, 0.0, 0.0, 7.0], &[2, 2]);
assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
}
#[test]
fn test_divmod() {
let x = array!([1.0, 2.0, 3.0]);
let y = array!([1.0, 1.0, 1.0]);
let out = divmod(&x, &y).unwrap();
assert_array_eq(
out.0,
array!([1.0, 2.0, 3.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
assert_array_eq(
out.1,
array!([0.0, 0.0, 0.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array!([5.0, 6.0, 7.0]);
let y = array!([2.0, 2.0, 2.0]);
let out = divmod(&x, &y).unwrap();
assert_array_eq(
out.0,
array!([2.0, 3.0, 3.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
assert_array_eq(
out.1,
array!([1.0, 0.0, 1.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array!([5.0, 6.0, 7.0]);
let y = array!([2.0, 2.0, 2.0]);
let out = divmod(&x, &y).unwrap();
assert_array_eq(
out.0,
array!([2.0, 3.0, 3.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
assert_array_eq(
out.1,
array!([1.0, 0.0, 1.0]),
tolerances::EXACT.rtol,
tolerances::EXACT.atol,
);
let x = array![complex64::new(1.0, 0.0)];
let y = array![complex64::new(2.0, 0.0)];
assert!(divmod(&x, &y).is_err());
let x = array![1.0];
let y = array![2.0];
let (quo, rem) = divmod(&x, &y).unwrap();
eval([&quo, &rem]).unwrap();
assert_eq!(quo.item_exact::<f32>(), 0.0);
assert_eq!(rem.item_exact::<f32>(), 1.0);
let x = array![1.0];
let y = array![2.0];
let (quo, rem) = divmod(&x, &y).unwrap();
let z = quo + rem;
assert_eq!(z.item_exact::<f32>(), 1.0);
let mut out_holder = {
let (quo, _) = divmod(&x, &y).unwrap();
vec![quo]
};
eval(out_holder.iter()).unwrap();
assert_eq!(out_holder[0].item_exact::<f32>(), 0.0);
out_holder.clear();
let out_holder = {
let (_, rem) = divmod(&x, &y).unwrap();
vec![rem]
};
eval(out_holder.iter()).unwrap();
assert_eq!(out_holder[0].item_exact::<f32>(), 1.0);
}
#[test]
fn test_segmented_mm() {
use crate::ops::{indexing::*, stack};
use crate::random;
fn segmented_mm_ref(a: &Array, b: &Array, segments: &Array) -> Array {
let segments_data: Vec<Vec<u32>> = (0..segments.shape()[0])
.map(|i| {
let row = segments.index(i);
vec![
row.index(0).item_exact::<u32>(),
row.index(1).item_exact::<u32>(),
]
})
.collect();
let results: Vec<Array> = segments_data
.iter()
.map(|seg| {
let s1 = seg[0] as i32;
let s2 = seg[1] as i32;
let a_slice = a.index((.., s1..s2));
let b_slice = b.index((s1..s2, ..));
a_slice.matmul(&b_slice).unwrap()
})
.collect();
stack(&results, 0).unwrap()
}
let shapes = [(10, 10, 10), (10, 10, 100), (100, 100, 100)];
let all_segments: Vec<Vec<f32>> = vec![
vec![0.0, 0.0, 1.0],
vec![0.0, 0.5, 1.0],
(0..10).map(|r| r as f32 / 9.0).collect(),
];
random::seed(42).unwrap();
for (m, n, k) in shapes {
for s in &all_segments {
let mut segments_vec: Vec<[u32; 2]> = Vec::new();
for i in 0..s.len() - 1 {
let s1 = ((k as f32 * s[i]) as u32).min(k as u32 - 1);
let s2 = ((k as f32 * s[i + 1]) as u32).min(k as u32 - 1);
segments_vec.push([s1, s2]);
}
let segments_flat: Vec<u32> = segments_vec.iter().flat_map(|x| *x).collect();
let segments = Array::from_slice(&segments_flat, &[segments_vec.len() as i32, 2]);
let a = random::normal::<f32>(&[m, k], None, None, None).unwrap();
let b = random::normal::<f32>(&[k, n], None, None, None).unwrap();
let c1 = segmented_mm_ref(&a, &b, &segments);
let c2 = segmented_mm(&a, &b, &segments).unwrap();
assert!(
c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
"segmented_mm failed for shape ({}, {}, {}) with segments {:?}",
m,
n,
k,
s
);
let a = random::normal::<f32>(&[k, m], None, None, None).unwrap();
let b = random::normal::<f32>(&[k, n], None, None, None).unwrap();
let a_t = a.t();
let c1 = segmented_mm_ref(&a_t, &b, &segments);
let c2 = segmented_mm(&a_t, &b, &segments).unwrap();
assert!(
c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
"segmented_mm with transposed a failed for shape ({}, {}, {})",
m,
n,
k
);
let a = random::normal::<f32>(&[m, k], None, None, None).unwrap();
let b = random::normal::<f32>(&[n, k], None, None, None).unwrap();
let b_t = b.t();
let c1 = segmented_mm_ref(&a, &b_t, &segments);
let c2 = segmented_mm(&a, &b_t, &segments).unwrap();
assert!(
c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
"segmented_mm with transposed b failed for shape ({}, {}, {})",
m,
n,
k
);
let a = random::normal::<f32>(&[k, m], None, None, None).unwrap();
let b = random::normal::<f32>(&[n, k], None, None, None).unwrap();
let a_t = a.t();
let b_t = b.t();
let c1 = segmented_mm_ref(&a_t, &b_t, &segments);
let c2 = segmented_mm(&a_t, &b_t, &segments).unwrap();
assert!(
c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
"segmented_mm with both transposed failed for shape ({}, {}, {})",
m,
n,
k
);
}
}
}
#[test]
fn test_segmented_mm_batched_error() {
let a = ones::<f32>(&[2, 10, 10]).unwrap();
let segments = Array::from_slice(&[0u32, 5, 5, 10], &[2, 2]);
let result = segmented_mm(&a, &a, &segments);
assert!(
result.is_err(),
"segmented_mm should fail for batched input"
);
}
#[test]
fn test_gather_mm() {
use crate::ops::indexing::take_axis;
use crate::random;
random::seed(0).unwrap();
fn gather_mm_ref(
a: &Array,
b: &Array,
lhs_indices: Option<&Array>,
rhs_indices: Option<&Array>,
) -> Array {
let a = a
.reshape(&[-1, a.shape()[a.ndim() - 2], a.shape()[a.ndim() - 1]])
.unwrap();
let b = b
.reshape(&[-1, b.shape()[b.ndim() - 2], b.shape()[b.ndim() - 1]])
.unwrap();
let a_gathered = match lhs_indices {
Some(idx) => take_axis(&a, idx, 0).unwrap(),
None => a,
};
let b_gathered = match rhs_indices {
Some(idx) => take_axis(&b, idx, 0).unwrap(),
None => b,
};
a_gathered.matmul(&b_gathered).unwrap()
}
let a = random::normal::<f32>(&[1, 32, 32], None, None, None).unwrap();
let b = random::normal::<f32>(&[3, 32, 32], None, None, None).unwrap();
let lhs_indices = Array::from_slice(&[0u32], &[1]);
let rhs_indices = Array::from_slice(&[2u32, 1], &[2]);
let out_ref = gather_mm_ref(&a, &b, Some(&lhs_indices), Some(&rhs_indices));
let out_test = gather_mm(&a, &b, &lhs_indices, &rhs_indices, None).unwrap();
assert!(
out_ref.all_close(&out_test, 1e-5, 1e-5, None).unwrap(),
"gather_mm test case 1 failed"
);
let out_ref = gather_mm_ref(&a, &b, None, Some(&rhs_indices));
let out_test = gather_mm(&a, &b, None::<&Array>, &rhs_indices, None).unwrap();
assert!(
out_ref.all_close(&out_test, 1e-5, 1e-5, None).unwrap(),
"gather_mm test case 2 failed"
);
let a = random::normal::<f32>(&[5, 32, 32], None, None, None).unwrap();
let lhs_indices = Array::from_slice(&[0u32, 2], &[2]);
let out_ref = gather_mm_ref(&a, &b, Some(&lhs_indices), Some(&rhs_indices));
let out_test = gather_mm(&a, &b, &lhs_indices, &rhs_indices, None).unwrap();
assert!(
out_ref.all_close(&out_test, 1e-5, 1e-5, None).unwrap(),
"gather_mm test case 3 failed"
);
}
#[test]
fn test_gather_mm_sorted() {
use crate::ops::indexing::take_axis;
use crate::ops::sort;
use crate::random;
random::seed(0).unwrap();
fn gather_mm_ref(a: &Array, b: &Array, rhs: &Array) -> Array {
let b_gathered = take_axis(b, rhs, 0).unwrap();
a.matmul(&b_gathered).unwrap()
}
let a = random::normal::<f32>(&[100, 1, 100], None, None, None).unwrap();
let b = random::normal::<f32>(&[8, 100, 100], None, None, None).unwrap();
let rhs = sort(random::randint::<_, i32>(0, 8, &[100], None).unwrap()).unwrap();
let c1 = gather_mm_ref(&a, &b, &rhs);
let c2 = gather_mm(&a, &b, None::<&Array>, &rhs, true).unwrap();
assert!(
c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
"gather_mm_sorted failed"
);
}
}