1use crate::error::check_status;
5use crate::raw;
6use crate::types::closure_custom::{MlxClosureCustom, MlxClosureCustomJvp, MlxClosureCustomVmap};
7use crate::types::closures::{MlxClosure, MlxClosureValueAndGrad};
8use crate::types::vectors::MlxVectorInt;
9use crate::{MlxArray, MlxArrayVector, MlxCError};
10
11impl MlxClosure {
12 pub fn compiled(&self, shapeless: bool) -> Result<Self, MlxCError> {
14 let operation: &'static str = "compile an MLX closure";
15 let mut output = Self::empty();
16 let status = unsafe { raw::mlx_compile(output.raw_mut(), self.0, shapeless) };
19 check_status(status, operation)?;
20 output.require_populated(operation)?;
21 Ok(output)
22 }
23
24 pub fn checkpointed(&self) -> Result<Self, MlxCError> {
27 let operation: &'static str = "checkpoint an MLX closure";
28 let mut output = Self::empty();
29 let status = unsafe { raw::mlx_checkpoint(output.raw_mut(), self.0) };
32 check_status(status, operation)?;
33 output.require_populated(operation)?;
34 Ok(output)
35 }
36
37 pub fn with_custom_vjp(&self, backward: &MlxClosureCustom) -> Result<Self, MlxCError> {
39 let operation: &'static str = "attach a custom VJP to an MLX closure";
40 let mut output = Self::empty();
41 let status = unsafe { raw::mlx_custom_vjp(output.raw_mut(), self.0, backward.raw()) };
44 check_status(status, operation)?;
45 output.require_populated(operation)?;
46 Ok(output)
47 }
48
49 #[allow(clippy::too_many_arguments)]
51 pub fn with_custom_rules(
52 &self,
53 backward: &MlxClosureCustom,
54 forward: &MlxClosureCustomJvp,
55 linearize: &MlxClosureCustomVmap,
56 ) -> Result<Self, MlxCError> {
57 let operation: &'static str = "attach custom rules to an MLX closure";
58 let mut output = Self::empty();
59 let status = unsafe {
62 raw::mlx_custom_function(
63 output.raw_mut(),
64 self.0,
65 backward.raw(),
66 forward.raw(),
67 linearize.raw(),
68 )
69 };
70 check_status(status, operation)?;
71 output.require_populated(operation)?;
72 Ok(output)
73 }
74}
75
76pub fn jvp_of_closure(
81 function: &MlxClosure,
82 primals: &MlxArrayVector,
83 tangents: &MlxArrayVector,
84) -> Result<(MlxArrayVector, MlxArrayVector), MlxCError> {
85 let operation: &'static str = "apply the MLX JVP transform";
86 let mut outputs = MlxArrayVector::empty(operation)?;
87 let mut tangent_outputs = MlxArrayVector::empty(operation)?;
88 let status = unsafe {
91 raw::mlx_jvp(
92 outputs.raw_mut(),
93 tangent_outputs.raw_mut(),
94 function.raw(),
95 primals.raw(),
96 tangents.raw(),
97 )
98 };
99 check_status(status, operation)?;
100 Ok((outputs, tangent_outputs))
101}
102
103pub fn vjp_of_closure(
108 function: &MlxClosure,
109 primals: &MlxArrayVector,
110 cotangents: &MlxArrayVector,
111) -> Result<(MlxArrayVector, MlxArrayVector), MlxCError> {
112 let operation: &'static str = "apply the MLX VJP transform";
113 let mut outputs = MlxArrayVector::empty(operation)?;
114 let mut cotangent_outputs = MlxArrayVector::empty(operation)?;
115 let status = unsafe {
118 raw::mlx_vjp(
119 outputs.raw_mut(),
120 cotangent_outputs.raw_mut(),
121 function.raw(),
122 primals.raw(),
123 cotangents.raw(),
124 )
125 };
126 check_status(status, operation)?;
127 Ok((outputs, cotangent_outputs))
128}
129
130pub fn value_and_grad_of_closure(
135 function: &MlxClosure,
136 argument_indices: &[i32],
137) -> Result<MlxClosureValueAndGrad, MlxCError> {
138 let operation: &'static str = "build the MLX value-and-gradient transform";
139 let mut output = MlxClosureValueAndGrad::empty();
140 let status = unsafe {
143 raw::mlx_value_and_grad(
144 output.raw_mut(),
145 function.raw(),
146 argument_indices.as_ptr(),
147 argument_indices.len(),
148 )
149 };
150 check_status(status, operation)?;
151 Ok(output)
152}
153
154#[derive(Debug)]
156pub struct MlxCompileCache(raw::mlx_compile_cache);
157
158impl MlxCompileCache {
159 pub fn empty() -> Self {
161 let mut raw_cache = raw::mlx_compile_cache {
162 ctx: std::ptr::null_mut(),
163 };
164 let status = unsafe { raw::mlx_detail_compile_cache(&mut raw_cache) };
166 debug_assert_eq!(status, 0);
167 Self(raw_cache)
168 }
169
170 pub fn clear(&mut self) -> Result<(), MlxCError> {
172 let status = unsafe { raw::mlx_detail_compile_clear_cache(self.0) };
174 check_status(status, "clear an MLX compile cache")
175 }
176
177 pub fn erase(&mut self, function_identity: usize) -> Result<(), MlxCError> {
179 let status = unsafe { raw::mlx_detail_compile_erase(self.0, function_identity) };
181 check_status(status, "erase an MLX compile cache entry")
182 }
183}
184
185impl Drop for MlxCompileCache {
186 fn drop(&mut self) {
187 unsafe {
190 raw::mlx_compile_cache_free(self.0);
191 }
192 }
193}
194
195impl MlxClosure {
196 pub fn compile_detailed(
205 &self,
206 function_identity: usize,
207 shapeless: bool,
208 constants: &[u64],
209 ) -> Result<Self, MlxCError> {
210 let operation: &'static str = "compile an MLX closure with tracing details";
211 let mut output = Self::empty();
212 let status = unsafe {
215 raw::mlx_detail_compile(
216 output.raw_mut(),
217 self.0,
218 function_identity,
219 shapeless,
220 constants.as_ptr(),
221 constants.len(),
222 )
223 };
224 check_status(status, operation)?;
225 output.require_populated(operation)?;
226 Ok(output)
227 }
228}
229
230pub fn disable_compile() -> Result<(), MlxCError> {
232 let status = unsafe { raw::mlx_disable_compile() };
235 check_status(status, "disable MLX compilation")
236}
237
238pub fn enable_compile() -> Result<(), MlxCError> {
240 let status = unsafe { raw::mlx_enable_compile() };
243 check_status(status, "enable MLX compilation")
244}
245
246pub fn set_compile_mode(mode: raw::mlx_compile_mode) -> Result<(), MlxCError> {
248 let status = unsafe { raw::mlx_set_compile_mode(mode) };
251 check_status(status, "set the MLX compile mode")
252}
253
254pub fn vmap_replace(
260 inputs: &MlxArrayVector,
261 traced_inputs: &MlxArrayVector,
262 traced_outputs: &MlxArrayVector,
263 input_axes: &[i32],
264 output_axes: &[i32],
265) -> Result<MlxArrayVector, MlxCError> {
266 let operation: &'static str = "apply the MLX VMAP replacement";
267 let mut output = MlxArrayVector::empty(operation)?;
268 let status = unsafe {
271 raw::mlx_detail_vmap_replace(
272 output.raw_mut(),
273 inputs.raw(),
274 traced_inputs.raw(),
275 traced_outputs.raw(),
276 input_axes.as_ptr(),
277 input_axes.len(),
278 output_axes.as_ptr(),
279 output_axes.len(),
280 )
281 };
282 check_status(status, operation)?;
283 Ok(output)
284}
285
286pub fn vmap_trace(
292 function: &MlxClosure,
293 inputs: &MlxArrayVector,
294 input_axes: &[i32],
295) -> Result<(MlxArrayVector, MlxArrayVector, MlxVectorInt), MlxCError> {
296 let operation: &'static str = "apply the MLX VMAP trace";
297 let mut traced_inputs = MlxArrayVector::empty(operation)?;
298 let mut traced_outputs = MlxArrayVector::empty(operation)?;
299 let output_axes = MlxVectorInt::empty();
300 let status = unsafe {
303 raw::mlx_detail_vmap_trace(
304 traced_inputs.raw_mut(),
305 traced_outputs.raw_mut(),
306 function.raw(),
307 inputs.raw(),
308 input_axes.as_ptr(),
309 input_axes.len(),
310 )
311 };
312 check_status(status, operation)?;
313 Ok((traced_inputs, traced_outputs, output_axes))
314}
315
316impl MlxArray {
317 pub fn evaluate_after(
323 &self,
324 dependencies: &MlxArrayVector,
325 ) -> Result<MlxArrayVector, MlxCError> {
326 let operation: &'static str = "evaluate an MLX array after its dependencies";
327 let outputs = MlxArrayVector::from_array(self)?;
328 let mut result = MlxArrayVector::empty(operation)?;
329 let status =
332 unsafe { raw::mlx_depends(result.raw_mut(), outputs.raw(), dependencies.raw()) };
333 check_status(status, operation)?;
334 Ok(result)
335 }
336}