1use std::os::raw::c_int;
2
3use crate::MlxBindingsContext;
4use crate::compile::attention_output_gate::build_attention_output_gate_graph;
5use crate::compile::signed_float32_cast::build_signed_float32_cast_graph;
6use crate::compile::sparse_shared_expert_combination::build_sparse_shared_expert_combination_graph;
7use crate::compile::vision_rope::build_vision_rope_graph;
8use crate::{MlxArray, MlxStream, raw};
9use crate::{
10 MlxCError,
11 compile::graph::{MlxCompiledGraph, array_from_vector, graph_output_array, set_graph_output},
12};
13
14const APPLY_ATTENTION_OUTPUT_GATE_OPERATION: &str = "apply the compiled MLX attention output gate";
15const COMPILE_ATTENTION_OUTPUT_GATE_OPERATION: &str =
16 "compile the shapeless MLX attention output gate";
17const APPLY_SPARSE_SHARED_EXPERT_COMBINATION_OPERATION: &str =
18 "apply the compiled MLX sparse and shared expert combination";
19const COMPILE_SPARSE_SHARED_EXPERT_COMBINATION_OPERATION: &str =
20 "compile the shapeless MLX sparse and shared expert combination";
21const APPLY_PRECISE_SWIGLU_OPERATION: &str = "apply the compiled precise MLX SwiGLU graph";
22const COMPILE_PRECISE_SWIGLU_OPERATION: &str = "compile the shapeless precise MLX SwiGLU graph";
23const APPLY_GATED_DELTA_DECAY_OPERATION: &str =
24 "apply the compiled MLX gated-delta decay arithmetic";
25const COMPILE_GATED_DELTA_DECAY_OPERATION: &str =
26 "compile the shapeless MLX gated-delta decay arithmetic";
27const APPLY_FUSED_SILU_OPERATION: &str = "apply the compiled MLX fused SiLU graph";
28const COMPILE_FUSED_SILU_OPERATION: &str = "compile the shapeless MLX fused SiLU graph";
29const APPLY_FUSED_SOFTPLUS_OPERATION: &str = "apply the compiled MLX fused K2 softplus graph";
30const COMPILE_FUSED_SOFTPLUS_OPERATION: &str = "compile the shapeless MLX fused K2 softplus graph";
31const APPLY_SIGNED_FLOAT32_CAST_OPERATION: &str = "apply the compiled MLX signed float32 cast";
32const COMPILE_SIGNED_FLOAT32_CAST_OPERATION: &str = "compile the shapeless MLX signed float32 cast";
33const APPLY_VISION_ROPE_OPERATION: &str =
34 "apply the compiled MLX vision rotate-half rotary embedding";
35const COMPILE_VISION_ROPE_OPERATION: &str =
36 "compile the shapeless MLX vision rotate-half rotary embedding";
37
38#[derive(Debug)]
40pub struct MlxCompiledElementwiseGraphs {
41 attention_output_gate: MlxCompiledGraph,
42 gated_delta_decay: MlxCompiledGraph,
43 precise_swiglu: MlxCompiledGraph,
44 sparse_shared_expert_combination: MlxCompiledGraph,
45 fused_silu: MlxCompiledGraph,
46 fused_softplus: MlxCompiledGraph,
47 vision_rope: MlxCompiledGraph,
48 signed_float32_cast: MlxCompiledGraph,
49}
50
51impl MlxCompiledElementwiseGraphs {
52 pub fn new() -> Result<Self, MlxCError> {
54 Ok(Self {
55 attention_output_gate: MlxCompiledGraph::new(
56 build_attention_output_gate_graph,
57 COMPILE_ATTENTION_OUTPUT_GATE_OPERATION,
58 true,
59 )?,
60 gated_delta_decay: MlxCompiledGraph::new(
61 build_gated_delta_decay_graph,
62 COMPILE_GATED_DELTA_DECAY_OPERATION,
63 true,
64 )?,
65 precise_swiglu: MlxCompiledGraph::new(
66 build_precise_swiglu_graph,
67 COMPILE_PRECISE_SWIGLU_OPERATION,
68 true,
69 )?,
70 sparse_shared_expert_combination: MlxCompiledGraph::new(
71 build_sparse_shared_expert_combination_graph,
72 COMPILE_SPARSE_SHARED_EXPERT_COMBINATION_OPERATION,
73 true,
74 )?,
75 fused_silu: MlxCompiledGraph::new(
76 build_fused_silu_graph,
77 COMPILE_FUSED_SILU_OPERATION,
78 true,
79 )?,
80 fused_softplus: MlxCompiledGraph::new(
81 build_fused_softplus_graph,
82 COMPILE_FUSED_SOFTPLUS_OPERATION,
83 true,
84 )?,
85 vision_rope: MlxCompiledGraph::new(
86 build_vision_rope_graph,
87 COMPILE_VISION_ROPE_OPERATION,
88 true,
89 )?,
90 signed_float32_cast: MlxCompiledGraph::new(
91 build_signed_float32_cast_graph,
92 COMPILE_SIGNED_FLOAT32_CAST_OPERATION,
93 true,
94 )?,
95 })
96 }
97}
98
99impl MlxBindingsContext {
100 pub fn apply_compiled_attention_output_gate(
102 &self,
103 compiled_elementwise_graphs: &MlxCompiledElementwiseGraphs,
104 attention_output: &MlxArray,
105 output_gate_logits: &MlxArray,
106 ) -> Result<MlxArray, MlxCError> {
107 compiled_elementwise_graphs.attention_output_gate.apply(
108 &[attention_output, output_gate_logits],
109 APPLY_ATTENTION_OUTPUT_GATE_OPERATION,
110 )
111 }
112
113 pub fn apply_compiled_sparse_shared_expert_combination(
115 &self,
116 compiled_elementwise_graphs: &MlxCompiledElementwiseGraphs,
117 sparse_expert_output: &MlxArray,
118 shared_expert_output: &MlxArray,
119 shared_expert_gate_logits: &MlxArray,
120 ) -> Result<MlxArray, MlxCError> {
121 compiled_elementwise_graphs
122 .sparse_shared_expert_combination
123 .apply(
124 &[
125 sparse_expert_output,
126 shared_expert_output,
127 shared_expert_gate_logits,
128 ],
129 APPLY_SPARSE_SHARED_EXPERT_COMBINATION_OPERATION,
130 )
131 }
132
133 pub fn apply_compiled_precise_swiglu(
135 &self,
136 compiled_elementwise_graphs: &MlxCompiledElementwiseGraphs,
137 up_states: &MlxArray,
138 gate_states: &MlxArray,
139 ) -> Result<MlxArray, MlxCError> {
140 compiled_elementwise_graphs
141 .precise_swiglu
142 .apply(&[up_states, gate_states], APPLY_PRECISE_SWIGLU_OPERATION)
143 }
144
145 pub fn apply_compiled_gated_delta_decay(
147 &self,
148 compiled_elementwise_graphs: &MlxCompiledElementwiseGraphs,
149 decay_rate_logarithm: &MlxArray,
150 decay_interval_inputs: &MlxArray,
151 decay_interval_bias: &MlxArray,
152 ) -> Result<MlxArray, MlxCError> {
153 compiled_elementwise_graphs.gated_delta_decay.apply(
154 &[
155 decay_rate_logarithm,
156 decay_interval_inputs,
157 decay_interval_bias,
158 ],
159 APPLY_GATED_DELTA_DECAY_OPERATION,
160 )
161 }
162
163 pub fn apply_compiled_silu(
171 &self,
172 compiled_elementwise_graphs: &MlxCompiledElementwiseGraphs,
173 input: &MlxArray,
174 ) -> Result<MlxArray, MlxCError> {
175 compiled_elementwise_graphs
176 .fused_silu
177 .apply(&[input], APPLY_FUSED_SILU_OPERATION)
178 }
179
180 pub fn apply_compiled_signed_float32_cast(
188 &self,
189 compiled_elementwise_graphs: &MlxCompiledElementwiseGraphs,
190 activations: &MlxArray,
191 signs: &MlxArray,
192 ) -> Result<MlxArray, MlxCError> {
193 compiled_elementwise_graphs
194 .signed_float32_cast
195 .apply(&[activations, signs], APPLY_SIGNED_FLOAT32_CAST_OPERATION)
196 }
197
198 pub fn apply_compiled_softplus(
206 &self,
207 compiled_elementwise_graphs: &MlxCompiledElementwiseGraphs,
208 input: &MlxArray,
209 ) -> Result<MlxArray, MlxCError> {
210 compiled_elementwise_graphs
211 .fused_softplus
212 .apply(&[input], APPLY_FUSED_SOFTPLUS_OPERATION)
213 }
214
215 pub fn apply_compiled_vision_rope(
224 &self,
225 compiled_elementwise_graphs: &MlxCompiledElementwiseGraphs,
226 attention_states: &MlxArray,
227 rotary_cosines: &MlxArray,
228 rotary_sines: &MlxArray,
229 first_half: &MlxArray,
230 second_half: &MlxArray,
231 ) -> Result<MlxArray, MlxCError> {
232 compiled_elementwise_graphs.vision_rope.apply(
233 &[
234 attention_states,
235 rotary_cosines,
236 rotary_sines,
237 first_half,
238 second_half,
239 ],
240 APPLY_VISION_ROPE_OPERATION,
241 )
242 }
243}
244
245unsafe extern "C" fn build_precise_swiglu_graph(
246 output_vector: *mut raw::mlx_vector_array,
247 input_vector: raw::mlx_vector_array,
248) -> c_int {
249 if output_vector.is_null() || unsafe { raw::mlx_vector_array_size(input_vector) } != 2 {
250 return 1;
251 }
252 let up_states = match array_from_vector(input_vector, 0) {
253 Ok(up_states) => up_states,
254 Err(get_status) => return get_status,
255 };
256 let gate_states = match array_from_vector(input_vector, 1) {
257 Ok(gate_states) => gate_states,
258 Err(get_status) => return get_status,
259 };
260 let gpu_stream = match MlxStream::default_gpu() {
261 Ok(gpu_stream) => gpu_stream,
262 Err(_) => return 1,
263 };
264 let output_dtype = unsafe { raw::mlx_array_dtype(up_states.raw()) };
265 let float32_gate = match graph_output_array(|output_array| {
266 unsafe {
268 raw::mlx_astype(
269 output_array,
270 gate_states.raw(),
271 raw::mlx_dtype__MLX_FLOAT32,
272 gpu_stream.raw(),
273 )
274 }
275 }) {
276 Ok(float32_gate) => float32_gate,
277 Err(build_status) => return build_status,
278 };
279 let gate_weights = match graph_output_array(|output_array| {
280 unsafe { raw::mlx_sigmoid(output_array, float32_gate.raw(), gpu_stream.raw()) }
282 }) {
283 Ok(gate_weights) => gate_weights,
284 Err(build_status) => return build_status,
285 };
286 let activated_gate = match graph_output_array(|output_array| {
287 unsafe {
289 raw::mlx_multiply(
290 output_array,
291 float32_gate.raw(),
292 gate_weights.raw(),
293 gpu_stream.raw(),
294 )
295 }
296 }) {
297 Ok(activated_gate) => activated_gate,
298 Err(build_status) => return build_status,
299 };
300 let float32_up = match graph_output_array(|output_array| {
301 unsafe {
303 raw::mlx_astype(
304 output_array,
305 up_states.raw(),
306 raw::mlx_dtype__MLX_FLOAT32,
307 gpu_stream.raw(),
308 )
309 }
310 }) {
311 Ok(float32_up) => float32_up,
312 Err(build_status) => return build_status,
313 };
314 let float32_activated_states = match graph_output_array(|output_array| {
315 unsafe {
317 raw::mlx_multiply(
318 output_array,
319 activated_gate.raw(),
320 float32_up.raw(),
321 gpu_stream.raw(),
322 )
323 }
324 }) {
325 Ok(float32_activated_states) => float32_activated_states,
326 Err(build_status) => return build_status,
327 };
328 let activated_states = match graph_output_array(|output_array| {
329 unsafe {
331 raw::mlx_astype(
332 output_array,
333 float32_activated_states.raw(),
334 output_dtype,
335 gpu_stream.raw(),
336 )
337 }
338 }) {
339 Ok(activated_states) => activated_states,
340 Err(build_status) => return build_status,
341 };
342 unsafe { set_graph_output(output_vector, &activated_states) }
344}
345
346unsafe extern "C" fn build_gated_delta_decay_graph(
347 output_vector: *mut raw::mlx_vector_array,
348 input_vector: raw::mlx_vector_array,
349) -> c_int {
350 if output_vector.is_null() || unsafe { raw::mlx_vector_array_size(input_vector) } != 3 {
351 return 1;
352 }
353 let decay_rate_logarithm = match array_from_vector(input_vector, 0) {
354 Ok(decay_rate_logarithm) => decay_rate_logarithm,
355 Err(get_status) => return get_status,
356 };
357 let decay_interval_inputs = match array_from_vector(input_vector, 1) {
358 Ok(decay_interval_inputs) => decay_interval_inputs,
359 Err(get_status) => return get_status,
360 };
361 let decay_interval_bias = match array_from_vector(input_vector, 2) {
362 Ok(decay_interval_bias) => decay_interval_bias,
363 Err(get_status) => return get_status,
364 };
365 let gpu_stream = match MlxStream::default_gpu() {
366 Ok(gpu_stream) => gpu_stream,
367 Err(_) => return 1,
368 };
369 let biased_decay_intervals = match graph_output_array(|output_array| {
370 unsafe {
372 raw::mlx_add(
373 output_array,
374 decay_interval_inputs.raw(),
375 decay_interval_bias.raw(),
376 gpu_stream.raw(),
377 )
378 }
379 }) {
380 Ok(biased_decay_intervals) => biased_decay_intervals,
381 Err(build_status) => return build_status,
382 };
383 let biased_dtype = biased_decay_intervals.dtype();
388 let scalar_shape_storage = [1_i32];
389 let zero_decay_interval_scalar = match graph_output_array(|output_array| {
390 unsafe {
395 raw::mlx_zeros(
396 output_array,
397 scalar_shape_storage.as_ptr(),
398 0,
399 biased_dtype.to_raw(),
400 gpu_stream.raw(),
401 )
402 }
403 }) {
404 Ok(zero_decay_interval_scalar) => zero_decay_interval_scalar,
405 Err(build_status) => return build_status,
406 };
407 let decay_intervals = match graph_output_array(|output_array| {
408 unsafe {
410 raw::mlx_logaddexp(
411 output_array,
412 biased_decay_intervals.raw(),
413 zero_decay_interval_scalar.raw(),
414 gpu_stream.raw(),
415 )
416 }
417 }) {
418 Ok(decay_intervals) => decay_intervals,
419 Err(build_status) => return build_status,
420 };
421 let float32_decay_logs = match graph_output_array(|output_array| {
422 unsafe {
424 raw::mlx_astype(
425 output_array,
426 decay_rate_logarithm.raw(),
427 raw::mlx_dtype__MLX_FLOAT32,
428 gpu_stream.raw(),
429 )
430 }
431 }) {
432 Ok(float32_decay_logs) => float32_decay_logs,
433 Err(build_status) => return build_status,
434 };
435 let decay_rates = match graph_output_array(|output_array| {
436 unsafe { raw::mlx_exp(output_array, float32_decay_logs.raw(), gpu_stream.raw()) }
438 }) {
439 Ok(decay_rates) => decay_rates,
440 Err(build_status) => return build_status,
441 };
442 let decay_products = match graph_output_array(|output_array| {
443 unsafe {
445 raw::mlx_multiply(
446 output_array,
447 decay_rates.raw(),
448 decay_intervals.raw(),
449 gpu_stream.raw(),
450 )
451 }
452 }) {
453 Ok(decay_products) => decay_products,
454 Err(build_status) => return build_status,
455 };
456 let negative_decay_products = match graph_output_array(|output_array| {
457 unsafe { raw::mlx_negative(output_array, decay_products.raw(), gpu_stream.raw()) }
459 }) {
460 Ok(negative_decay_products) => negative_decay_products,
461 Err(build_status) => return build_status,
462 };
463 let decays = match graph_output_array(|output_array| {
464 unsafe {
466 raw::mlx_exp(
467 output_array,
468 negative_decay_products.raw(),
469 gpu_stream.raw(),
470 )
471 }
472 }) {
473 Ok(decays) => decays,
474 Err(build_status) => return build_status,
475 };
476 unsafe { set_graph_output(output_vector, &decays) }
478}
479
480unsafe extern "C" fn build_fused_silu_graph(
481 output_vector: *mut raw::mlx_vector_array,
482 input_vector: raw::mlx_vector_array,
483) -> c_int {
484 if output_vector.is_null() || unsafe { raw::mlx_vector_array_size(input_vector) } != 1 {
485 return 1;
486 }
487 let input = match array_from_vector(input_vector, 0) {
488 Ok(input) => input,
489 Err(get_status) => return get_status,
490 };
491 let gpu_stream = match MlxStream::default_gpu() {
492 Ok(gpu_stream) => gpu_stream,
493 Err(_) => return 1,
494 };
495 let sigmoid_input = match graph_output_array(|output_array| {
496 unsafe { raw::mlx_sigmoid(output_array, input.raw(), gpu_stream.raw()) }
498 }) {
499 Ok(sigmoid_input) => sigmoid_input,
500 Err(build_status) => return build_status,
501 };
502 let silu_output = match graph_output_array(|output_array| {
503 unsafe {
505 raw::mlx_multiply(
506 output_array,
507 input.raw(),
508 sigmoid_input.raw(),
509 gpu_stream.raw(),
510 )
511 }
512 }) {
513 Ok(silu_output) => silu_output,
514 Err(build_status) => return build_status,
515 };
516 unsafe { set_graph_output(output_vector, &silu_output) }
518}
519
520unsafe extern "C" fn build_fused_softplus_graph(
521 output_vector: *mut raw::mlx_vector_array,
522 input_vector: raw::mlx_vector_array,
523) -> c_int {
524 if output_vector.is_null() || unsafe { raw::mlx_vector_array_size(input_vector) } != 1 {
525 return 1;
526 }
527 let input = match array_from_vector(input_vector, 0) {
528 Ok(input) => input,
529 Err(get_status) => return get_status,
530 };
531 let gpu_stream = match MlxStream::default_gpu() {
532 Ok(gpu_stream) => gpu_stream,
533 Err(_) => return 1,
534 };
535 let ln2 = std::f32::consts::LN_2;
536 let ln2_scalar = match MlxArray::from_f32(&[ln2], &[]) {
537 Ok(ln2_scalar) => ln2_scalar,
538 Err(_) => return 1,
539 };
540 let typed_ln2 = match graph_output_array(|output_array| {
541 unsafe {
543 raw::mlx_astype(
544 output_array,
545 ln2_scalar.raw(),
546 raw::mlx_array_dtype(input.raw()),
547 gpu_stream.raw(),
548 )
549 }
550 }) {
551 Ok(typed_ln2) => typed_ln2,
552 Err(build_status) => return build_status,
553 };
554 let scaled_input = match graph_output_array(|output_array| {
555 unsafe { raw::mlx_multiply(output_array, input.raw(), typed_ln2.raw(), gpu_stream.raw()) }
557 }) {
558 Ok(scaled_input) => scaled_input,
559 Err(build_status) => return build_status,
560 };
561 let exponent = match graph_output_array(|output_array| {
562 unsafe { raw::mlx_exp(output_array, scaled_input.raw(), gpu_stream.raw()) }
564 }) {
565 Ok(exponent) => exponent,
566 Err(build_status) => return build_status,
567 };
568 let logarithm = match graph_output_array(|output_array| {
569 unsafe { raw::mlx_log1p(output_array, exponent.raw(), gpu_stream.raw()) }
571 }) {
572 Ok(logarithm) => logarithm,
573 Err(build_status) => return build_status,
574 };
575 let inverse_ln2_scalar = match MlxArray::from_f32(&[1.0 / ln2], &[]) {
576 Ok(inverse_ln2_scalar) => inverse_ln2_scalar,
577 Err(_) => return 1,
578 };
579 let typed_inverse_ln2 = match graph_output_array(|output_array| {
580 unsafe {
582 raw::mlx_astype(
583 output_array,
584 inverse_ln2_scalar.raw(),
585 raw::mlx_array_dtype(input.raw()),
586 gpu_stream.raw(),
587 )
588 }
589 }) {
590 Ok(typed_inverse_ln2) => typed_inverse_ln2,
591 Err(build_status) => return build_status,
592 };
593 let softplus_output = match graph_output_array(|output_array| {
594 unsafe {
596 raw::mlx_multiply(
597 output_array,
598 logarithm.raw(),
599 typed_inverse_ln2.raw(),
600 gpu_stream.raw(),
601 )
602 }
603 }) {
604 Ok(softplus_output) => softplus_output,
605 Err(build_status) => return build_status,
606 };
607 unsafe { set_graph_output(output_vector, &softplus_output) }
609}