1use std::os::raw::c_void;
5
6use crate::error::check_status;
7use crate::raw;
8use crate::types::closures::MlxClosure;
9use crate::types::closures::MlxClosureKwargs;
10use crate::types::maps::MlxMapStringToArray;
11use crate::{MlxArray, MlxArrayVector, MlxCError};
12
13fn path_argument(path: &str, operation: &'static str) -> Result<std::ffi::CString, MlxCError> {
15 std::ffi::CString::new(path).map_err(|_| MlxCError {
16 operation,
17 description: "path contains an interior null byte".to_owned(),
18 })
19}
20
21pub fn export_function(
26 path: &str,
27 function: &MlxClosure,
28 example_arguments: &MlxArrayVector,
29 shapeless: bool,
30) -> Result<(), MlxCError> {
31 let operation: &'static str = "export an MLX function";
32 let path_value = path_argument(path, operation)?;
33 let status = unsafe {
35 raw::mlx_export_function(
36 path_value.as_ptr(),
37 function.raw(),
38 example_arguments.raw(),
39 shapeless,
40 )
41 };
42 check_status(status, operation)
43}
44
45pub fn export_function_with_keywords(
50 path: &str,
51 function: &MlxClosureKwargs,
52 example_arguments: &MlxArrayVector,
53 keyword_arguments: &MlxMapStringToArray,
54 shapeless: bool,
55) -> Result<(), MlxCError> {
56 let operation: &'static str = "export an MLX function with keywords";
57 let path_value = path_argument(path, operation)?;
58 let status = unsafe {
60 raw::mlx_export_function_kwargs(
61 path_value.as_ptr(),
62 function.raw(),
63 example_arguments.raw(),
64 keyword_arguments.raw(),
65 shapeless,
66 )
67 };
68 check_status(status, operation)
69}
70
71#[derive(Debug)]
73pub struct MlxFunctionExporter(raw::mlx_function_exporter);
74
75impl MlxFunctionExporter {
76 pub fn new(path: &str, function: &MlxClosure, shapeless: bool) -> Result<Self, MlxCError> {
81 let operation: &'static str = "create an MLX function exporter";
82 let path_value = path_argument(path, operation)?;
83 let raw_exporter = unsafe {
86 raw::mlx_function_exporter_new(path_value.as_ptr(), function.raw(), shapeless)
87 };
88 if raw_exporter.ctx.is_null() {
89 return Err(MlxCError {
90 operation,
91 description: "MLX returned an empty function exporter handle".to_owned(),
92 });
93 }
94 Ok(Self(raw_exporter))
95 }
96
97 pub fn apply(&self, arguments: &MlxArrayVector) -> Result<(), MlxCError> {
102 let operation: &'static str = "specialize an MLX function exporter";
103 let status = unsafe { raw::mlx_function_exporter_apply(self.0, arguments.raw()) };
105 check_status(status, operation)
106 }
107
108 pub fn apply_with_keywords(
113 &self,
114 arguments: &MlxArrayVector,
115 keyword_arguments: &MlxMapStringToArray,
116 ) -> Result<(), MlxCError> {
117 let operation: &'static str = "specialize an MLX function exporter with keywords";
118 let status = unsafe {
120 raw::mlx_function_exporter_apply_kwargs(
121 self.0,
122 arguments.raw(),
123 keyword_arguments.raw(),
124 )
125 };
126 check_status(status, operation)
127 }
128}
129
130impl Drop for MlxFunctionExporter {
131 fn drop(&mut self) {
132 unsafe {
135 raw::mlx_function_exporter_free(self.0);
136 }
137 }
138}
139
140#[derive(Debug)]
142pub struct MlxImportedFunction(raw::mlx_imported_function);
143
144impl MlxImportedFunction {
145 pub fn load(path: &str) -> Result<Self, MlxCError> {
150 let operation: &'static str = "import an MLX function";
151 let path_value = path_argument(path, operation)?;
152 let raw_function = unsafe { raw::mlx_imported_function_new(path_value.as_ptr()) };
155 if raw_function.ctx.is_null() {
156 return Err(MlxCError {
157 operation,
158 description: "MLX returned an empty imported function handle".to_owned(),
159 });
160 }
161 Ok(Self(raw_function))
162 }
163
164 pub fn apply(&self, arguments: &MlxArrayVector) -> Result<MlxArrayVector, MlxCError> {
169 let operation: &'static str = "apply an imported MLX function";
170 let mut output = MlxArrayVector::empty(operation)?;
171 let status =
174 unsafe { raw::mlx_imported_function_apply(output.raw_mut(), self.0, arguments.raw()) };
175 check_status(status, operation)?;
176 Ok(output)
177 }
178
179 pub fn apply_with_keywords(
184 &self,
185 arguments: &MlxArrayVector,
186 keyword_arguments: &MlxMapStringToArray,
187 ) -> Result<MlxArrayVector, MlxCError> {
188 let operation: &'static str = "apply an imported MLX function with keywords";
189 let mut output = MlxArrayVector::empty(operation)?;
190 let status = unsafe {
193 raw::mlx_imported_function_apply_kwargs(
194 output.raw_mut(),
195 self.0,
196 arguments.raw(),
197 keyword_arguments.raw(),
198 )
199 };
200 check_status(status, operation)?;
201 Ok(output)
202 }
203}
204
205impl Drop for MlxImportedFunction {
206 fn drop(&mut self) {
207 unsafe {
210 raw::mlx_imported_function_free(self.0);
211 }
212 }
213}
214
215#[derive(Debug)]
217pub struct MlxNodeNamer(raw::mlx_node_namer);
218
219impl MlxNodeNamer {
220 pub fn new() -> Result<Self, MlxCError> {
222 let operation: &'static str = "create an MLX node namer";
223 let raw_namer = unsafe { raw::mlx_node_namer_new() };
225 if raw_namer.ctx.is_null() {
226 return Err(MlxCError {
227 operation,
228 description: "MLX returned an empty node namer handle".to_owned(),
229 });
230 }
231 Ok(Self(raw_namer))
232 }
233
234 pub fn set_name(&mut self, array: &MlxArray, name: &str) -> Result<(), MlxCError> {
236 let operation: &'static str = "name an MLX graph node";
237 let name_argument = path_argument(name, operation)?;
238 let status =
241 unsafe { raw::mlx_node_namer_set_name(self.0, array.raw(), name_argument.as_ptr()) };
242 check_status(status, operation)
243 }
244
245 pub(crate) const fn raw(&self) -> raw::mlx_node_namer {
246 self.0
247 }
248
249 pub fn name_of(&self, array: &MlxArray) -> Result<String, MlxCError> {
251 let operation: &'static str = "read an MLX graph node name";
252 let mut name_pointer: *const std::os::raw::c_char = std::ptr::null();
253 let status =
256 unsafe { raw::mlx_node_namer_get_name(&mut name_pointer, self.0, array.raw()) };
257 check_status(status, operation)?;
258 if name_pointer.is_null() {
259 return Err(MlxCError {
260 operation,
261 description: "MLX returned a null node name".to_owned(),
262 });
263 }
264 let borrowed = unsafe { std::ffi::CStr::from_ptr(name_pointer) };
267 Ok(borrowed.to_string_lossy().into_owned())
268 }
269}
270
271impl Drop for MlxNodeNamer {
272 fn drop(&mut self) {
273 unsafe {
276 raw::mlx_node_namer_free(self.0);
277 }
278 }
279}
280
281pub struct GraphOutputFile(pub *mut c_void);
288
289pub unsafe fn export_graph_to_dot(
297 output_file: GraphOutputFile,
298 namer: &MlxNodeNamer,
299 outputs: &MlxArrayVector,
300) -> Result<(), MlxCError> {
301 let operation: &'static str = "export an MLX graph to DOT";
302 let status =
304 unsafe { raw::mlx_export_to_dot(output_file.0.cast(), namer.raw(), outputs.raw()) };
305 check_status(status, operation)
306}
307
308pub unsafe fn print_graph(
316 output_file: GraphOutputFile,
317 namer: &MlxNodeNamer,
318 outputs: &MlxArrayVector,
319) -> Result<(), MlxCError> {
320 let operation: &'static str = "print an MLX graph";
321 let status = unsafe { raw::mlx_print_graph(output_file.0.cast(), namer.raw(), outputs.raw()) };
323 check_status(status, operation)
324}