#[cfg(feature = "research-precision")]
pub mod field {
use num_bigint::BigInt;
use num_rational::Ratio;
pub type ResearchScalar = Ratio<BigInt>;
pub fn ln_arbitrary(x: &ResearchScalar) -> ResearchScalar {
let f64_val = x.to_f64().unwrap_or(1.0);
let result = f64_val.ln();
Ratio::from_float(result).unwrap_or_else(|| Ratio::new(BigInt::from(1), BigInt::from(1)))
}
}
pub mod portable {
pub type PortableScalar = f64;
#[inline]
pub fn ln(x: f64) -> f64 {
x.ln()
}
#[inline]
pub fn tanh(x: f64) -> f64 {
x.tanh()
}
}
pub mod fixed_point {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FixedPoint64 {
pub value: u64,
pub scale: u32,
}
impl FixedPoint64 {
pub const fn new(value: u64, scale: u32) -> Self {
FixedPoint64 { value, scale }
}
pub fn from_f64(f: f64, scale: u32) -> Self {
let scale_f = 10u64.pow(scale) as f64;
FixedPoint64 {
value: (f.max(0.0) * scale_f) as u64,
scale,
}
}
pub fn to_f64(&self) -> f64 {
let scale_f = 10u64.pow(self.scale) as f64;
self.value as f64 / scale_f
}
pub fn multiply(&self, other: &FixedPoint64) -> FixedPoint64 {
debug_assert_eq!(self.scale, other.scale);
let scale_factor = 10u64.pow(self.scale);
let result = ((self.value as u128 * other.value as u128) / scale_factor as u128) as u64;
FixedPoint64 {
value: result,
scale: self.scale,
}
}
pub fn ln(&self) -> FixedPoint64 {
let f = self.to_f64();
let ln_f = f.ln();
FixedPoint64::from_f64(ln_f, self.scale)
}
pub fn tanh(&self) -> FixedPoint64 {
let f = self.to_f64();
let tanh_f = f.tanh();
FixedPoint64::from_f64(tanh_f, self.scale)
}
}
}
pub mod hardware {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct HardwareFixed {
pub value: u32,
pub precision_bits: u8,
}
impl HardwareFixed {
pub const fn new(value: u32, precision_bits: u8) -> Self {
HardwareFixed {
value,
precision_bits,
}
}
pub fn multiply(&self, other: &HardwareFixed) -> HardwareFixed {
debug_assert_eq!(self.precision_bits, other.precision_bits);
let result =
((self.value as u64 * other.value as u64) >> (self.precision_bits as u64)) as u32;
HardwareFixed {
value: result,
precision_bits: self.precision_bits,
}
}
pub fn ln_approx(&self) -> HardwareFixed {
let scale = 1u32 << self.precision_bits;
let norm_val = (self.value as f32) / (scale as f32);
let ln_result = norm_val.ln();
let scaled = (ln_result * (scale as f32)) as u32;
HardwareFixed {
value: scaled,
precision_bits: self.precision_bits,
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fixed_point_64_from_f64() {
let fp = fixed_point::FixedPoint64::from_f64(1.5, 6);
assert_eq!(fp.value, 1_500_000);
}
#[test]
fn test_fixed_point_64_to_f64() {
let fp = fixed_point::FixedPoint64::new(1_500_000, 6);
let f = fp.to_f64();
assert!((f - 1.5).abs() < 0.000001);
}
#[test]
fn test_fixed_point_64_multiply() {
let a = fixed_point::FixedPoint64::from_f64(2.0, 6);
let b = fixed_point::FixedPoint64::from_f64(3.0, 6);
let result = a.multiply(&b);
let f = result.to_f64();
assert!((f - 6.0).abs() < 0.001);
}
#[test]
fn test_hardware_fixed_multiply() {
let a = hardware::HardwareFixed::new(256, 8); let b = hardware::HardwareFixed::new(512, 8); let result = a.multiply(&b);
assert!(result.value > 400 && result.value < 600);
}
}