use std::ffi::c_void;
use std::fmt;
use crate::error::{check, install_error_handler, Result};
use crate::stream::stream;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Dtype {
Bool,
UInt8,
UInt16,
UInt32,
UInt64,
Int8,
Int16,
Int32,
Int64,
Float16,
Float32,
Float64,
BFloat16,
Complex64,
}
impl Dtype {
pub(crate) fn to_raw(self) -> crate::sys::mlx_dtype {
use crate::sys::*;
match self {
Dtype::Bool => mlx_dtype__MLX_BOOL,
Dtype::UInt8 => mlx_dtype__MLX_UINT8,
Dtype::UInt16 => mlx_dtype__MLX_UINT16,
Dtype::UInt32 => mlx_dtype__MLX_UINT32,
Dtype::UInt64 => mlx_dtype__MLX_UINT64,
Dtype::Int8 => mlx_dtype__MLX_INT8,
Dtype::Int16 => mlx_dtype__MLX_INT16,
Dtype::Int32 => mlx_dtype__MLX_INT32,
Dtype::Int64 => mlx_dtype__MLX_INT64,
Dtype::Float16 => mlx_dtype__MLX_FLOAT16,
Dtype::Float32 => mlx_dtype__MLX_FLOAT32,
Dtype::Float64 => mlx_dtype__MLX_FLOAT64,
Dtype::BFloat16 => mlx_dtype__MLX_BFLOAT16,
Dtype::Complex64 => mlx_dtype__MLX_COMPLEX64,
}
}
pub(crate) fn from_raw(raw: crate::sys::mlx_dtype) -> Self {
match raw {
crate::sys::mlx_dtype__MLX_BOOL => Dtype::Bool,
crate::sys::mlx_dtype__MLX_UINT8 => Dtype::UInt8,
crate::sys::mlx_dtype__MLX_UINT16 => Dtype::UInt16,
crate::sys::mlx_dtype__MLX_UINT32 => Dtype::UInt32,
crate::sys::mlx_dtype__MLX_UINT64 => Dtype::UInt64,
crate::sys::mlx_dtype__MLX_INT8 => Dtype::Int8,
crate::sys::mlx_dtype__MLX_INT16 => Dtype::Int16,
crate::sys::mlx_dtype__MLX_INT32 => Dtype::Int32,
crate::sys::mlx_dtype__MLX_INT64 => Dtype::Int64,
crate::sys::mlx_dtype__MLX_FLOAT16 => Dtype::Float16,
crate::sys::mlx_dtype__MLX_FLOAT32 => Dtype::Float32,
crate::sys::mlx_dtype__MLX_FLOAT64 => Dtype::Float64,
crate::sys::mlx_dtype__MLX_BFLOAT16 => Dtype::BFloat16,
crate::sys::mlx_dtype__MLX_COMPLEX64 => Dtype::Complex64,
other => panic!("unknown mlx dtype {other}"),
}
}
}
pub struct Array {
pub(crate) raw: crate::sys::mlx_array,
}
unsafe impl Send for Array {}
unsafe impl Sync for Array {}
impl Drop for Array {
fn drop(&mut self) {
unsafe {
crate::sys::mlx_array_free(self.raw);
}
}
}
impl Clone for Array {
fn clone(&self) -> Self {
install_error_handler();
unsafe {
let mut out = crate::sys::mlx_array_new();
crate::sys::mlx_array_set(&mut out, self.raw);
Array { raw: out }
}
}
}
impl fmt::Debug for Array {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Array")
.field("shape", &self.shape())
.field("dtype", &self.dtype())
.finish()
}
}
impl Array {
pub(crate) fn from_raw(raw: crate::sys::mlx_array) -> Self {
Array { raw }
}
pub(crate) fn new_handle() -> crate::sys::mlx_array {
install_error_handler();
unsafe { crate::sys::mlx_array_new() }
}
pub fn from_slice<T: ArrayElement>(data: &[T], shape: &[i32]) -> Self {
install_error_handler();
let expected: i64 = shape.iter().map(|&d| d as i64).product();
assert_eq!(
data.len() as i64,
expected,
"data length {} does not match shape {:?}",
data.len(),
shape
);
unsafe {
let raw = crate::sys::mlx_array_new_data(
data.as_ptr() as *const c_void,
shape.as_ptr(),
shape.len() as i32,
T::DTYPE.to_raw(),
);
Array { raw }
}
}
pub fn scalar_f32(value: f32) -> Self {
Self::from_slice(&[value], &[])
}
pub fn scalar_i32(value: i32) -> Self {
Self::from_slice(&[value], &[])
}
pub fn ndim(&self) -> usize {
unsafe { crate::sys::mlx_array_ndim(self.raw) }
}
pub fn shape(&self) -> Vec<i32> {
unsafe {
let ndim = crate::sys::mlx_array_ndim(self.raw);
let ptr = crate::sys::mlx_array_shape(self.raw);
std::slice::from_raw_parts(ptr, ndim).to_vec()
}
}
pub fn dim(&self, axis: i32) -> i32 {
let shape = self.shape();
let ndim = shape.len() as i32;
let axis = if axis < 0 { axis + ndim } else { axis };
shape[axis as usize]
}
pub fn size(&self) -> usize {
self.shape().iter().map(|&d| d as usize).product()
}
pub fn dtype(&self) -> Dtype {
unsafe { Dtype::from_raw(crate::sys::mlx_array_dtype(self.raw)) }
}
pub fn eval(&self) -> Result<()> {
unsafe { check(crate::sys::mlx_array_eval(self.raw)) }
}
pub fn item_f32(&self) -> Result<f32> {
let mut out: f32 = 0.0;
unsafe {
let arr = crate::ops::astype(self, Dtype::Float32)?;
check(crate::sys::mlx_array_item_float32(&mut out, arr.raw))?;
}
Ok(out)
}
pub fn item_u32(&self) -> Result<u32> {
let mut out: u32 = 0;
unsafe {
let arr = crate::ops::astype(self, Dtype::UInt32)?;
check(crate::sys::mlx_array_item_uint32(&mut out, arr.raw))?;
}
Ok(out)
}
pub fn to_vec_f32(&self) -> Result<Vec<f32>> {
let as_f32 = crate::ops::astype(self, Dtype::Float32)?;
as_f32.eval()?;
unsafe {
let ptr = crate::sys::mlx_array_data_float32(as_f32.raw);
if ptr.is_null() {
return Err(crate::error::Error::Mlx(
"null data pointer reading array".into(),
));
}
Ok(std::slice::from_raw_parts(ptr, as_f32.size()).to_vec())
}
}
pub fn to_vec_u32(&self) -> Result<Vec<u32>> {
let arr = crate::ops::astype(self, Dtype::UInt32)?;
arr.eval()?;
unsafe {
let ptr = crate::sys::mlx_array_data_uint32(arr.raw);
if ptr.is_null() {
return Err(crate::error::Error::Mlx(
"null data pointer reading array".into(),
));
}
Ok(std::slice::from_raw_parts(ptr, arr.size()).to_vec())
}
}
}
pub trait ArrayElement {
const DTYPE: Dtype;
}
impl ArrayElement for f32 {
const DTYPE: Dtype = Dtype::Float32;
}
impl ArrayElement for u32 {
const DTYPE: Dtype = Dtype::UInt32;
}
impl ArrayElement for i32 {
const DTYPE: Dtype = Dtype::Int32;
}
impl ArrayElement for u8 {
const DTYPE: Dtype = Dtype::UInt8;
}
impl ArrayElement for bool {
const DTYPE: Dtype = Dtype::Bool;
}
pub fn synchronize() -> Result<()> {
unsafe { check(crate::sys::mlx_synchronize(stream())) }
}