arnaucube
arnaucube/fhe-studyPublic Self-hosted repository
fhe-study/arith/src/complex.rs
arith/src/complex.rsRaw
  1//! Complex
  2
  3use rand::Rng;
  4use std::ops::{Add, Div, Mul, Neg, Sub};
  5
  6#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
  7pub struct C<T> {
  8    pub re: T,
  9    pub im: T,
 10}
 11
 12impl From<f64> for C<f64> {
 13    fn from(v: f64) -> Self {
 14        Self { re: v, im: 0_f64 }
 15    }
 16}
 17
 18impl<T> C<T>
 19where
 20    T: Default + From<i32>,
 21{
 22    pub fn new(re: T, im: T) -> Self {
 23        Self { re, im }
 24    }
 25
 26    pub fn rand(mut rng: impl Rng, max: u64) -> Self {
 27        Self::new(
 28            T::from(rng.gen_range(0..max) as i32),
 29            T::from(rng.gen_range(0..max) as i32),
 30        )
 31    }
 32
 33    pub fn zero() -> C<T> {
 34        Self {
 35            re: T::from(0),
 36            im: T::from(0),
 37        }
 38    }
 39    pub fn one() -> C<T> {
 40        Self {
 41            re: T::from(1),
 42            im: T::from(0),
 43        }
 44    }
 45    pub fn i() -> C<T> {
 46        Self {
 47            re: T::from(0),
 48            im: T::from(1),
 49        }
 50    }
 51}
 52
 53impl C<f64> {
 54    // cos & sin from Taylor series approximation, details at
 55    // https://en.wikipedia.org/wiki/Sine_and_cosine#Series_and_polynomials
 56    fn cos(x: f64) -> f64 {
 57        let mut r = 1.0;
 58        let mut term = 1.0;
 59        let mut n = 1;
 60        for _ in 0..10 {
 61            term *= -(x * x) / ((2 * n - 1) * (2 * n)) as f64;
 62            r += term;
 63            n += 1;
 64        }
 65
 66        r
 67    }
 68    fn sin(x: f64) -> f64 {
 69        let mut r = x;
 70        let mut term = x;
 71        let mut n = 1;
 72
 73        for _ in 0..10 {
 74            term *= -(x * x) / ((2 * n) * (2 * n + 1)) as f64;
 75            r += term;
 76            n += 1;
 77        }
 78
 79        r
 80    }
 81
 82    // e^(self))
 83    pub fn exp(self) -> Self {
 84        Self {
 85            re: Self::cos(self.im), // TODO WIP review
 86            im: Self::sin(self.im),
 87        }
 88    }
 89    pub fn pow(self, k: u32) -> Self {
 90        let mut k = k;
 91        if k == 0 {
 92            return Self::one();
 93        }
 94        let mut base = self.clone();
 95        while k & 1 == 0 {
 96            base = base.clone() * base;
 97            k >>= 1;
 98        }
 99
100        if k == 1 {
101            return base;
102        }
103
104        let mut acc = base.clone();
105        while k > 1 {
106            k >>= 1;
107            base = base.clone() * base;
108            if k & 1 == 1 {
109                acc = acc * base.clone();
110            }
111        }
112        acc
113    }
114    pub fn modulus<const Q: u64>(self) -> Self {
115        let q: f64 = Q as f64;
116        let re = (self.re % q + q) % q;
117        let im = (self.im % q + q) % q;
118        Self { re, im }
119    }
120    pub fn modulus_centered<const Q: u64>(self) -> Self {
121        let re = modulus_centered_f64::<Q>(self.re);
122        let im = modulus_centered_f64::<Q>(self.im);
123        Self { re, im }
124    }
125}
126
127fn modulus_centered_f64<const Q: u64>(v: f64) -> f64 {
128    let q = Q as f64;
129    let mut res = v % q;
130    if res > q / 2.0 {
131        res = res - q;
132    }
133    res
134}
135
136impl<T: Default> Default for C<T> {
137    fn default() -> Self {
138        C {
139            re: T::default(),
140            im: T::default(),
141        }
142    }
143}
144
145impl<T> Add for C<T>
146where
147    T: Add<Output = T> + Copy,
148{
149    type Output = Self;
150    fn add(self, rhs: Self) -> Self::Output {
151        C {
152            re: self.re + rhs.re,
153            im: self.im + rhs.im,
154        }
155    }
156}
157
158impl<T> Sub for C<T>
159where
160    T: Sub<Output = T> + Copy,
161{
162    type Output = Self;
163    fn sub(self, rhs: Self) -> Self::Output {
164        C {
165            re: self.re - rhs.re,
166            im: self.im - rhs.im,
167        }
168    }
169}
170
171impl<T> Mul for C<T>
172where
173    T: Mul<Output = T> + Sub<Output = T> + Add<Output = T> + Copy,
174{
175    type Output = Self;
176    fn mul(self, rhs: Self) -> Self::Output {
177        C {
178            re: self.re * rhs.re - self.im * rhs.im,
179            im: self.re * rhs.im + self.im * rhs.re,
180        }
181    }
182}
183
184impl<T> Neg for C<T>
185where
186    T: Neg<Output = T> + Copy,
187{
188    type Output = Self;
189    fn neg(self) -> Self::Output {
190        C {
191            re: -self.re,
192            im: -self.im,
193        }
194    }
195}
196
197impl<T> Div for C<T>
198where
199    T: Copy
200        + Add<Output = T>
201        + Sub<Output = T>
202        + Mul<Output = T>
203        + Div<Output = T>
204        + Neg<Output = T>,
205{
206    type Output = Self;
207
208    fn div(self, rhs: Self) -> Self {
209        // (a+ib)/(c+id) = (ac + bd)/(c^2 + d^2) + i* (bc -ad)/(c^2 + d^2)
210        let den = rhs.re * rhs.re + rhs.im * rhs.im;
211        C {
212            re: (self.re * rhs.re + self.im * rhs.im) / den,
213            im: (self.im * rhs.re - self.re * rhs.im) / den,
214        }
215    }
216}
217impl<T> C<T>
218where
219    T: Neg<Output = T> + Copy,
220{
221    pub fn conj(&self) -> Self {
222        C {
223            re: self.re,
224            im: -self.im,
225        }
226    }
227}
228
229impl<T: Add<Output = T> + Default + Copy> std::iter::Sum for C<T> {
230    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
231        iter.fold(C::default(), |acc, x| acc + x)
232    }
233}
234
235impl C<f64> {
236    pub fn abs(&self) -> f64 {
237        (self.re * self.re + self.im * self.im).sqrt()
238    }
239}
240
241// poly mul with complex coefficients
242pub fn naive_poly_mul<const N: usize>(poly1: &Vec<C<f64>>, poly2: &Vec<C<f64>>) -> Vec<C<f64>> {
243    let mut result: Vec<C<f64>> = vec![C::<f64>::zero(); (N * 2) - 1];
244    for i in 0..N {
245        for j in 0..N {
246            result[i + j] = result[i + j] + poly1[i] * poly2[j];
247        }
248    }
249
250    // apply mod (X^N + 1))
251    // R::<N>::from_vec(result.iter().map(|c| *c as i64).collect())
252    // modulus_i128::<N>(&mut result);
253    // dbg!(&result);
254    // dbg!(R::<N>(array::from_fn(|i| result[i] as i64)).coeffs());
255
256    // R(array::from_fn(|i| result[i] as i64))
257    result
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263
264    #[test]
265    fn test_i() {
266        assert_eq!(C::i(), C::new(0.0, 1.0));
267    }
268
269    #[test]
270    fn test_add() {
271        let a = C::new(2.0, 3.0);
272        let b = C::new(1.0, 4.0);
273        assert_eq!(a + b, C::new(3.0, 7.0));
274    }
275
276    #[test]
277    fn test_sub() {
278        let a = C::new(5.0, 7.0);
279        let b = C::new(2.0, 3.0);
280        assert_eq!(a - b, C::new(3.0, 4.0));
281    }
282
283    #[test]
284    fn test_mult() {
285        let a = C::new(1.0, 2.0);
286        let b = C::new(3.0, 4.0);
287        assert_eq!(a * b, C::new(-5.0, 10.0));
288    }
289
290    #[test]
291    fn test_div() {
292        let a: C<f64> = C::new(1.0, 2.0);
293        let b: C<f64> = C::new(3.0, 4.0);
294        let r = a / b;
295        let expected = C::new(0.44, 0.08);
296        let epsilon = 1e-2;
297        assert!((r.re - expected.re).abs() < epsilon);
298        assert!((r.im - expected.im).abs() < epsilon);
299    }
300
301    #[test]
302    fn test_conj() {
303        let a = C::new(3.0, -4.0);
304        assert_eq!(a.conj(), C::new(3.0, 4.0));
305        assert_eq!(a.conj().conj(), a);
306    }
307
308    #[test]
309    fn test_neg() {
310        let a = C::new(1.0, -2.0);
311        assert_eq!(-a, C::new(-1.0, 2.0));
312    }
313
314    #[test]
315    fn test_abs() {
316        let a = C::new(3.0, 4.0);
317        assert!((a.abs() - 5.0).abs() < 1e-10);
318    }
319
320    #[test]
321    fn test_exp() {
322        // let a = C::new(3.0, 4.0);
323        let pi = C::<f64>::from(std::f64::consts::PI);
324        let n = 4;
325        let a = ((C::<f64>::from(2f64) * pi * C::<f64>::i()) / C::<f64>::new(n as f64, 0f64)).exp();
326        dbg!(&a);
327        assert_eq!(a.exp(), a.exp());
328    }
329}