use std::os::raw::c_void;
use crate::error::check_status;
use crate::raw;
use crate::types::closures::MlxClosure;
use crate::types::closures::MlxClosureKwargs;
use crate::types::maps::MlxMapStringToArray;
use crate::{MlxArray, MlxArrayVector, MlxCError};
fn path_argument(path: &str, operation: &'static str) -> Result<std::ffi::CString, MlxCError> {
std::ffi::CString::new(path).map_err(|_| MlxCError {
operation,
description: "path contains an interior null byte".to_owned(),
})
}
pub fn export_function(
path: &str,
function: &MlxClosure,
example_arguments: &MlxArrayVector,
shapeless: bool,
) -> Result<(), MlxCError> {
let operation: &'static str = "export an MLX function";
let path_value = path_argument(path, operation)?;
let status = unsafe {
raw::mlx_export_function(
path_value.as_ptr(),
function.raw(),
example_arguments.raw(),
shapeless,
)
};
check_status(status, operation)
}
pub fn export_function_with_keywords(
path: &str,
function: &MlxClosureKwargs,
example_arguments: &MlxArrayVector,
keyword_arguments: &MlxMapStringToArray,
shapeless: bool,
) -> Result<(), MlxCError> {
let operation: &'static str = "export an MLX function with keywords";
let path_value = path_argument(path, operation)?;
let status = unsafe {
raw::mlx_export_function_kwargs(
path_value.as_ptr(),
function.raw(),
example_arguments.raw(),
keyword_arguments.raw(),
shapeless,
)
};
check_status(status, operation)
}
#[derive(Debug)]
pub struct MlxFunctionExporter(raw::mlx_function_exporter);
impl MlxFunctionExporter {
pub fn new(path: &str, function: &MlxClosure, shapeless: bool) -> Result<Self, MlxCError> {
let operation: &'static str = "create an MLX function exporter";
let path_value = path_argument(path, operation)?;
let raw_exporter = unsafe {
raw::mlx_function_exporter_new(path_value.as_ptr(), function.raw(), shapeless)
};
if raw_exporter.ctx.is_null() {
return Err(MlxCError {
operation,
description: "MLX returned an empty function exporter handle".to_owned(),
});
}
Ok(Self(raw_exporter))
}
pub fn apply(&self, arguments: &MlxArrayVector) -> Result<(), MlxCError> {
let operation: &'static str = "specialize an MLX function exporter";
let status = unsafe { raw::mlx_function_exporter_apply(self.0, arguments.raw()) };
check_status(status, operation)
}
pub fn apply_with_keywords(
&self,
arguments: &MlxArrayVector,
keyword_arguments: &MlxMapStringToArray,
) -> Result<(), MlxCError> {
let operation: &'static str = "specialize an MLX function exporter with keywords";
let status = unsafe {
raw::mlx_function_exporter_apply_kwargs(
self.0,
arguments.raw(),
keyword_arguments.raw(),
)
};
check_status(status, operation)
}
}
impl Drop for MlxFunctionExporter {
fn drop(&mut self) {
unsafe {
raw::mlx_function_exporter_free(self.0);
}
}
}
#[derive(Debug)]
pub struct MlxImportedFunction(raw::mlx_imported_function);
impl MlxImportedFunction {
pub fn load(path: &str) -> Result<Self, MlxCError> {
let operation: &'static str = "import an MLX function";
let path_value = path_argument(path, operation)?;
let raw_function = unsafe { raw::mlx_imported_function_new(path_value.as_ptr()) };
if raw_function.ctx.is_null() {
return Err(MlxCError {
operation,
description: "MLX returned an empty imported function handle".to_owned(),
});
}
Ok(Self(raw_function))
}
pub fn apply(&self, arguments: &MlxArrayVector) -> Result<MlxArrayVector, MlxCError> {
let operation: &'static str = "apply an imported MLX function";
let mut output = MlxArrayVector::empty(operation)?;
let status =
unsafe { raw::mlx_imported_function_apply(output.raw_mut(), self.0, arguments.raw()) };
check_status(status, operation)?;
Ok(output)
}
pub fn apply_with_keywords(
&self,
arguments: &MlxArrayVector,
keyword_arguments: &MlxMapStringToArray,
) -> Result<MlxArrayVector, MlxCError> {
let operation: &'static str = "apply an imported MLX function with keywords";
let mut output = MlxArrayVector::empty(operation)?;
let status = unsafe {
raw::mlx_imported_function_apply_kwargs(
output.raw_mut(),
self.0,
arguments.raw(),
keyword_arguments.raw(),
)
};
check_status(status, operation)?;
Ok(output)
}
}
impl Drop for MlxImportedFunction {
fn drop(&mut self) {
unsafe {
raw::mlx_imported_function_free(self.0);
}
}
}
#[derive(Debug)]
pub struct MlxNodeNamer(raw::mlx_node_namer);
impl MlxNodeNamer {
pub fn new() -> Result<Self, MlxCError> {
let operation: &'static str = "create an MLX node namer";
let raw_namer = unsafe { raw::mlx_node_namer_new() };
if raw_namer.ctx.is_null() {
return Err(MlxCError {
operation,
description: "MLX returned an empty node namer handle".to_owned(),
});
}
Ok(Self(raw_namer))
}
pub fn set_name(&mut self, array: &MlxArray, name: &str) -> Result<(), MlxCError> {
let operation: &'static str = "name an MLX graph node";
let name_argument = path_argument(name, operation)?;
let status =
unsafe { raw::mlx_node_namer_set_name(self.0, array.raw(), name_argument.as_ptr()) };
check_status(status, operation)
}
pub(crate) const fn raw(&self) -> raw::mlx_node_namer {
self.0
}
pub fn name_of(&self, array: &MlxArray) -> Result<String, MlxCError> {
let operation: &'static str = "read an MLX graph node name";
let mut name_pointer: *const std::os::raw::c_char = std::ptr::null();
let status =
unsafe { raw::mlx_node_namer_get_name(&mut name_pointer, self.0, array.raw()) };
check_status(status, operation)?;
if name_pointer.is_null() {
return Err(MlxCError {
operation,
description: "MLX returned a null node name".to_owned(),
});
}
let borrowed = unsafe { std::ffi::CStr::from_ptr(name_pointer) };
Ok(borrowed.to_string_lossy().into_owned())
}
}
impl Drop for MlxNodeNamer {
fn drop(&mut self) {
unsafe {
raw::mlx_node_namer_free(self.0);
}
}
}
pub struct GraphOutputFile(pub *mut c_void);
pub unsafe fn export_graph_to_dot(
output_file: GraphOutputFile,
namer: &MlxNodeNamer,
outputs: &MlxArrayVector,
) -> Result<(), MlxCError> {
let operation: &'static str = "export an MLX graph to DOT";
let status =
unsafe { raw::mlx_export_to_dot(output_file.0.cast(), namer.raw(), outputs.raw()) };
check_status(status, operation)
}
pub unsafe fn print_graph(
output_file: GraphOutputFile,
namer: &MlxNodeNamer,
outputs: &MlxArrayVector,
) -> Result<(), MlxCError> {
let operation: &'static str = "print an MLX graph";
let status = unsafe { raw::mlx_print_graph(output_file.0.cast(), namer.raw(), outputs.raw()) };
check_status(status, operation)
}