mlx_c_rust/compile/
graph.rs1use std::os::raw::c_int;
2
3use crate::{MlxArray, MlxArrayVector, MlxCError, error::check_status, raw};
4
5pub type MlxGraphBuilder =
9 unsafe extern "C" fn(*mut raw::mlx_vector_array, raw::mlx_vector_array) -> c_int;
10
11#[derive(Debug)]
17pub(crate) struct MlxCompiledGraph {
18 compiled_closure: MlxClosure,
19}
20
21impl MlxCompiledGraph {
22 pub(crate) fn new(
28 graph_builder: MlxGraphBuilder,
29 compile_operation: &'static str,
30 shapeless: bool,
31 ) -> Result<Self, MlxCError> {
32 let source_closure = MlxClosure::from_function(graph_builder, compile_operation)?;
33 let mut compiled_closure = MlxClosure::empty();
34 let compile_status = unsafe {
37 raw::mlx_compile(compiled_closure.raw_mut(), source_closure.raw(), shapeless)
38 };
39 check_status(compile_status, compile_operation)?;
40 compiled_closure.require_populated(compile_operation)?;
41 Ok(Self { compiled_closure })
42 }
43
44 pub(crate) fn apply(
45 &self,
46 graph_inputs: &[&MlxArray],
47 apply_operation: &'static str,
48 ) -> Result<MlxArray, MlxCError> {
49 let input_vector = MlxArrayVector::new(graph_inputs)?;
50 let mut output_vector = MlxArrayVector::empty(apply_operation)?;
51 let apply_status = unsafe {
54 raw::mlx_closure_apply(
55 output_vector.raw_mut(),
56 self.compiled_closure.raw(),
57 input_vector.raw(),
58 )
59 };
60 check_status(apply_status, apply_operation)?;
61 if output_vector.len() != 1 {
62 return Err(MlxCError {
63 operation: apply_operation,
64 description: format!(
65 "compiled MLX graph returned {} outputs instead of one",
66 output_vector.len()
67 ),
68 });
69 }
70 output_vector.array_at(0, apply_operation)
71 }
72
73 pub(crate) fn apply_multi(
78 &self,
79 graph_inputs: &[&MlxArray],
80 apply_operation: &'static str,
81 ) -> Result<Vec<MlxArray>, MlxCError> {
82 let input_vector = MlxArrayVector::new(graph_inputs)?;
83 let mut output_vector = MlxArrayVector::empty(apply_operation)?;
84 let apply_status = unsafe {
87 raw::mlx_closure_apply(
88 output_vector.raw_mut(),
89 self.compiled_closure.raw(),
90 input_vector.raw(),
91 )
92 };
93 check_status(apply_status, apply_operation)?;
94 let output_count = output_vector.len();
95 if output_count == 0 {
96 return Err(MlxCError {
97 operation: apply_operation,
98 description: "compiled MLX graph returned no outputs".to_owned(),
99 });
100 }
101 (0..output_count)
102 .map(|output_index| output_vector.array_at(output_index, apply_operation))
103 .collect::<Result<Vec<_>, MlxCError>>()
104 }
105}
106
107pub fn array_from_vector(
109 input_vector: raw::mlx_vector_array,
110 input_index: usize,
111) -> Result<MlxArray, c_int> {
112 let mut input_array = MlxArray::empty();
113 let get_status =
116 unsafe { raw::mlx_vector_array_get(input_array.raw_mut(), input_vector, input_index) };
117 if get_status != 0 || input_array.is_empty() {
118 return Err(if get_status == 0 { 1 } else { get_status });
119 }
120 Ok(input_array)
121}
122
123pub fn graph_output_array(
125 build_graph: impl FnOnce(*mut raw::mlx_array) -> c_int,
126) -> Result<MlxArray, c_int> {
127 let mut output_array = MlxArray::empty();
128 let build_status = build_graph(output_array.raw_mut());
129 if build_status != 0 || output_array.is_empty() {
130 return Err(if build_status == 0 { 1 } else { build_status });
131 }
132 Ok(output_array)
133}
134
135pub unsafe fn set_graph_output(
141 output_vector: *mut raw::mlx_vector_array,
142 graph_output: &MlxArray,
143) -> c_int {
144 unsafe { raw::mlx_vector_array_set_value(output_vector, graph_output.raw()) }
147}
148
149pub unsafe fn set_graph_output_vector(
165 output_vector: *mut raw::mlx_vector_array,
166 graph_outputs: &[&MlxArray],
167) -> c_int {
168 let output_handles: Vec<raw::mlx_array> = graph_outputs.iter().map(|o| o.raw()).collect();
169 unsafe {
172 raw::mlx_vector_array_set_data(output_vector, output_handles.as_ptr(), output_handles.len())
173 }
174}
175
176#[derive(Debug)]
177struct MlxClosure {
178 raw_closure: raw::mlx_closure,
179}
180
181impl MlxClosure {
182 fn empty() -> Self {
183 let raw_closure = unsafe { raw::mlx_closure_new() };
186 Self { raw_closure }
187 }
188
189 fn from_function(
190 graph_builder: MlxGraphBuilder,
191 compile_operation: &'static str,
192 ) -> Result<Self, MlxCError> {
193 let raw_closure = unsafe { raw::mlx_closure_new_func(Some(graph_builder)) };
195 if raw_closure.ctx.is_null() {
196 return Err(MlxCError {
197 operation: compile_operation,
198 description: "MLX returned an empty closure handle".to_owned(),
199 });
200 }
201 Ok(Self { raw_closure })
202 }
203
204 const fn raw(&self) -> raw::mlx_closure {
205 self.raw_closure
206 }
207
208 fn raw_mut(&mut self) -> *mut raw::mlx_closure {
209 &mut self.raw_closure
210 }
211
212 fn require_populated(&self, compile_operation: &'static str) -> Result<(), MlxCError> {
213 if self.raw_closure.ctx.is_null() {
214 return Err(MlxCError {
215 operation: compile_operation,
216 description: "MLX left the compiled closure handle empty".to_owned(),
217 });
218 }
219 Ok(())
220 }
221}
222
223impl Drop for MlxClosure {
224 fn drop(&mut self) {
225 unsafe {
227 raw::mlx_closure_free(self.raw_closure);
228 }
229 }
230}