1use std::os::raw::c_int;
16
17use crate::MlxCError;
18use crate::compile::graph::{
19 MlxCompiledGraph, array_from_vector, graph_output_array, set_graph_output_vector,
20};
21use crate::{MlxArray, MlxBindingsContext, MlxStream, raw};
22
23const APPLY_GATED_DELTA_RATE_ACTIVATION_OPERATION: &str =
24 "apply the compiled MLX gated-delta rate activation";
25const COMPILE_GATED_DELTA_RATE_ACTIVATION_OPERATION: &str =
26 "compile the shapeless MLX gated-delta rate activation graph";
27
28#[derive(Debug)]
33pub struct MlxCompiledGatedDeltaRateActivation {
34 compiled_graph: MlxCompiledGraph,
35}
36
37impl MlxCompiledGatedDeltaRateActivation {
38 pub fn new() -> Result<Self, MlxCError> {
40 Ok(Self {
41 compiled_graph: MlxCompiledGraph::new(
42 build_gated_delta_rate_activation_graph,
43 COMPILE_GATED_DELTA_RATE_ACTIVATION_OPERATION,
44 true,
45 )?,
46 })
47 }
48}
49
50impl MlxBindingsContext {
51 pub fn apply_compiled_gated_delta_rate_activation(
54 &self,
55 rate_activation: &MlxCompiledGatedDeltaRateActivation,
56 update_logits: &MlxArray,
57 decay_interval_inputs: &MlxArray,
58 decay_rate_logarithm: &MlxArray,
59 decay_interval_bias: &MlxArray,
60 ) -> Result<(MlxArray, MlxArray), MlxCError> {
61 let mut graph_outputs = rate_activation.compiled_graph.apply_multi(
62 &[
63 update_logits,
64 decay_interval_inputs,
65 decay_rate_logarithm,
66 decay_interval_bias,
67 ],
68 APPLY_GATED_DELTA_RATE_ACTIVATION_OPERATION,
69 )?;
70 if graph_outputs.len() != 2 {
71 return Err(MlxCError {
72 operation: APPLY_GATED_DELTA_RATE_ACTIVATION_OPERATION,
73 description: format!(
74 "compiled MLX gated-delta rate activation returned {} outputs, expected 2",
75 graph_outputs.len()
76 ),
77 });
78 }
79 let decay_rates = graph_outputs
80 .pop()
81 .expect("the length check proved two outputs");
82 let update_rates = graph_outputs
83 .pop()
84 .expect("the length check proved two outputs");
85 Ok((update_rates, decay_rates))
86 }
87}
88
89unsafe extern "C" fn build_gated_delta_rate_activation_graph(
90 output_vector: *mut raw::mlx_vector_array,
91 input_vector: raw::mlx_vector_array,
92) -> c_int {
93 if output_vector.is_null() || unsafe { raw::mlx_vector_array_size(input_vector) } != 4 {
94 return 1;
95 }
96 let update_logits = match array_from_vector(input_vector, 0) {
97 Ok(update_logits) => update_logits,
98 Err(get_status) => return get_status,
99 };
100 let decay_interval_inputs = match array_from_vector(input_vector, 1) {
101 Ok(decay_interval_inputs) => decay_interval_inputs,
102 Err(get_status) => return get_status,
103 };
104 let decay_rate_logarithm = match array_from_vector(input_vector, 2) {
105 Ok(decay_rate_logarithm) => decay_rate_logarithm,
106 Err(get_status) => return get_status,
107 };
108 let decay_interval_bias = match array_from_vector(input_vector, 3) {
109 Ok(decay_interval_bias) => decay_interval_bias,
110 Err(get_status) => return get_status,
111 };
112 let gpu_stream = match MlxStream::default_gpu() {
113 Ok(gpu_stream) => gpu_stream,
114 Err(_) => return 1,
115 };
116 let update_rates = match graph_output_array(|output_array| {
117 unsafe { raw::mlx_sigmoid(output_array, update_logits.raw(), gpu_stream.raw()) }
119 }) {
120 Ok(update_rates) => update_rates,
121 Err(build_status) => return build_status,
122 };
123 let biased_decay_intervals = match graph_output_array(|output_array| {
128 unsafe {
130 raw::mlx_add(
131 output_array,
132 decay_interval_inputs.raw(),
133 decay_interval_bias.raw(),
134 gpu_stream.raw(),
135 )
136 }
137 }) {
138 Ok(biased_decay_intervals) => biased_decay_intervals,
139 Err(build_status) => return build_status,
140 };
141 let biased_dtype = biased_decay_intervals.dtype();
142 let scalar_shape_storage = [1_i32];
143 let zero_decay_interval_scalar = match graph_output_array(|output_array| {
144 unsafe {
149 raw::mlx_zeros(
150 output_array,
151 scalar_shape_storage.as_ptr(),
152 0,
153 biased_dtype.to_raw(),
154 gpu_stream.raw(),
155 )
156 }
157 }) {
158 Ok(zero_decay_interval_scalar) => zero_decay_interval_scalar,
159 Err(build_status) => return build_status,
160 };
161 let decay_intervals = match graph_output_array(|output_array| {
162 unsafe {
164 raw::mlx_logaddexp(
165 output_array,
166 biased_decay_intervals.raw(),
167 zero_decay_interval_scalar.raw(),
168 gpu_stream.raw(),
169 )
170 }
171 }) {
172 Ok(decay_intervals) => decay_intervals,
173 Err(build_status) => return build_status,
174 };
175 let float32_decay_logs = match graph_output_array(|output_array| {
176 unsafe {
178 raw::mlx_astype(
179 output_array,
180 decay_rate_logarithm.raw(),
181 raw::mlx_dtype__MLX_FLOAT32,
182 gpu_stream.raw(),
183 )
184 }
185 }) {
186 Ok(float32_decay_logs) => float32_decay_logs,
187 Err(build_status) => return build_status,
188 };
189 let decay_rates = match graph_output_array(|output_array| {
190 unsafe { raw::mlx_exp(output_array, float32_decay_logs.raw(), gpu_stream.raw()) }
192 }) {
193 Ok(decay_rates) => decay_rates,
194 Err(build_status) => return build_status,
195 };
196 let decay_products = match graph_output_array(|output_array| {
197 unsafe {
199 raw::mlx_multiply(
200 output_array,
201 decay_rates.raw(),
202 decay_intervals.raw(),
203 gpu_stream.raw(),
204 )
205 }
206 }) {
207 Ok(decay_products) => decay_products,
208 Err(build_status) => return build_status,
209 };
210 let negative_decay_products = match graph_output_array(|output_array| {
211 unsafe { raw::mlx_negative(output_array, decay_products.raw(), gpu_stream.raw()) }
213 }) {
214 Ok(negative_decay_products) => negative_decay_products,
215 Err(build_status) => return build_status,
216 };
217 let decays = match graph_output_array(|output_array| {
218 unsafe {
220 raw::mlx_exp(
221 output_array,
222 negative_decay_products.raw(),
223 gpu_stream.raw(),
224 )
225 }
226 }) {
227 Ok(decays) => decays,
228 Err(build_status) => return build_status,
229 };
230 unsafe { set_graph_output_vector(output_vector, &[&update_rates, &decays]) }
235}