use crate::MlxBindingsContext;
use crate::MlxCError;
use crate::{MlxArray, MlxDtype, raw};
impl MlxBindingsContext {
pub fn softplus(&self, input: &MlxArray) -> Result<MlxArray, MlxCError> {
let zero_scalar = self.zeros(&[], input.dtype())?;
self.logaddexp(input, &zero_scalar)
}
pub fn silu(&self, input: &MlxArray) -> Result<MlxArray, MlxCError> {
let sigmoid_weights = self.sigmoid(input)?;
self.multiply(input, &sigmoid_weights)
}
pub fn tanh(&self, input: &MlxArray) -> Result<MlxArray, MlxCError> {
self.output_array("apply MLX tanh", |output, stream| {
unsafe { raw::mlx_tanh(output, input.raw(), stream) }
})
}
pub fn erf(&self, input: &MlxArray) -> Result<MlxArray, MlxCError> {
self.output_array("apply MLX error function", |output, stream| {
unsafe { raw::mlx_erf(output, input.raw(), stream) }
})
}
pub fn cos(&self, input: &MlxArray) -> Result<MlxArray, MlxCError> {
self.output_array("apply MLX cosine", |output, stream| {
unsafe { raw::mlx_cos(output, input.raw(), stream) }
})
}
pub fn sin(&self, input: &MlxArray) -> Result<MlxArray, MlxCError> {
self.output_array("apply MLX sine", |output, stream| {
unsafe { raw::mlx_sin(output, input.raw(), stream) }
})
}
pub fn power(&self, bases: &MlxArray, exponents: &MlxArray) -> Result<MlxArray, MlxCError> {
self.output_array("apply MLX power", |output, stream| {
unsafe { raw::mlx_power(output, bases.raw(), exponents.raw(), stream) }
})
}
pub fn gelu(&self, input: &MlxArray) -> Result<MlxArray, MlxCError> {
let scaled_input = self.multiply_scalar(input, std::f32::consts::FRAC_1_SQRT_2)?;
let error_function_values = self.erf(&scaled_input)?;
let one_array_f32 = self.array_from_f32(&[1.0], &[1])?;
let one_array = self.astype(&one_array_f32, input.dtype())?;
let gaussian_cumulative_factor = self.add(&one_array, &error_function_values)?;
let unscaled_activation = self.multiply(input, &gaussian_cumulative_factor)?;
self.multiply_scalar(&unscaled_activation, 0.5)
}
pub fn gelu_tanh(&self, input: &MlxArray) -> Result<MlxArray, MlxCError> {
let sqrt_two_over_pi = 0.797_884_6_f32;
let cubic_coefficient = 0.044715_f32;
let cubic_exponent = self.array_from_i32(&[3], &[])?;
let cubed_input = self.power(input, &cubic_exponent)?;
let weighted_cubed_input = self.multiply_scalar(&cubed_input, cubic_coefficient)?;
let inner_polynomial = self.add(input, &weighted_cubed_input)?;
let scaled_inner = self.multiply_scalar(&inner_polynomial, sqrt_two_over_pi)?;
let tanh_result = self.tanh(&scaled_inner)?;
let one_array_f32 = self.array_from_f32(&[1.0], &[1])?;
let one_array = self.astype(&one_array_f32, input.dtype())?;
let one_plus_tanh = self.add(&one_array, &tanh_result)?;
let half_times_x = self.multiply_scalar(input, 0.5)?;
self.multiply(&half_times_x, &one_plus_tanh)
}
pub fn apply_softplus_attention_gate(
&self,
attention_output: &MlxArray,
gate_logits: &MlxArray,
) -> Result<MlxArray, MlxCError> {
validate_softplus_attention_gate_shapes(attention_output, gate_logits)?;
let gate_logits_f32 = self.astype(gate_logits, MlxDtype::Float32)?;
let softplus_gate = self.softplus(&gate_logits_f32)?;
let activation_dtype_gate = self.astype(&softplus_gate, attention_output.dtype())?;
self.multiply(attention_output, &activation_dtype_gate)
}
}
fn validate_softplus_attention_gate_shapes(
attention_output: &MlxArray,
gate_logits: &MlxArray,
) -> Result<(), MlxCError> {
const OPERATION: &str = "apply the softplus attention output gate";
let attention_output_shape = attention_output.shape();
let gate_logits_shape = gate_logits.shape();
if attention_output_shape.is_empty() || gate_logits_shape.is_empty() {
return Err(MlxCError {
operation: OPERATION,
description: "attention output and gate logits must have positive rank".to_owned(),
});
}
if gate_logits_shape.len() > attention_output_shape.len() {
return Err(MlxCError {
operation: OPERATION,
description: "gate logits rank must not exceed the attention output rank".to_owned(),
});
}
let rank_offset = attention_output_shape.len() - gate_logits_shape.len();
for (gate_axis_index, gate_axis_length) in gate_logits_shape.iter().enumerate() {
let attention_axis_length = attention_output_shape[rank_offset + gate_axis_index];
if *gate_axis_length != 1 && *gate_axis_length != attention_axis_length {
return Err(MlxCError {
operation: OPERATION,
description: "gate logits must broadcast to the attention output".to_owned(),
});
}
}
Ok(())
}