use mirtal_sys::ffi;
use crate::{Array, Error, Graph, Result};
#[derive(Debug, Clone, Copy)]
pub struct RopeOptions {
pub dimensions: usize,
pub traditional: bool,
pub base: Option<f32>,
pub scale: f32,
pub offset: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct FrequencyRopeOptions {
pub dimensions: usize,
pub traditional: bool,
pub offset: usize,
}
impl Graph<'_> {
pub fn rope(self, input: &Array, options: RopeOptions) -> Result<Array> {
let dimensions = dimensions(options.dimensions)?;
positive(options.scale, "scale")?;
if let Some(base) = options.base {
positive(base, "base")?;
}
let native = ffi::NativeRopeOptions {
dimensions,
traditional: options.traditional,
has_base: options.base.is_some(),
base: options.base.unwrap_or_default(),
scale: options.scale,
offset: i32::try_from(options.offset)?,
};
Array::from_raw(ffi::rope(input.native()?, &native, self.native()?)?, "RoPE")
}
pub fn rope_with_frequencies(
self,
input: &Array,
frequencies: &Array,
options: FrequencyRopeOptions,
) -> Result<Array> {
Array::from_raw(
ffi::rope_with_frequencies(
input.native()?,
dimensions(options.dimensions)?,
options.traditional,
frequencies.native()?,
i32::try_from(options.offset)?,
self.native()?,
)?,
"frequency RoPE",
)
}
}
fn dimensions(value: usize) -> Result<i32> {
if value == 0 || !value.is_multiple_of(2) {
return Err(invalid("dimensions must be positive and even"));
}
Ok(i32::try_from(value)?)
}
fn positive(value: f32, name: &str) -> Result<()> {
if !value.is_finite() || value <= 0.0 {
return Err(invalid(format!("{name} must be finite and positive")));
}
Ok(())
}
fn invalid(message: impl Into<String>) -> Error {
Error::InvalidRope(message.into())
}