L² algorithm
This commit is contained in:
		
							
								
								
									
										13
									
								
								src/agcd.rs
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/agcd.rs
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| use crate::bkz::bkz_reduce; | ||||
| use crate::deep_lll::deep_lll; | ||||
| // use crate::lll::lattice_reduce; | ||||
| use crate::lll::lattice_reduce; | ||||
| use crate::matrix::Matrix; | ||||
| use crate::utils::abs; | ||||
| use lll_rs::l2::bigl2; | ||||
| @@ -8,7 +8,8 @@ use rug::{Integer, Rational}; | ||||
|  | ||||
| pub fn agcd(ciphertexts: Vec<Integer>, noise_bits: usize, algorithm: u8) -> Integer { | ||||
|     // 1. Build lattice matrix basis | ||||
|     let basis_matrix = Matrix::new_lattice(noise_bits, ciphertexts.clone()).unwrap(); | ||||
|     let mut basis_matrix = Matrix::new_lattice(noise_bits, ciphertexts.clone()).unwrap(); | ||||
|     println!("basis: {:?}\n\n\n\n", basis_matrix); | ||||
|  | ||||
|     // 2. reduce with LLL, and extract first element of shortest vector | ||||
|     let mut lll_matrix = basis_matrix.to_lll_matrix(); | ||||
| @@ -27,10 +28,10 @@ pub fn agcd(ciphertexts: Vec<Integer>, noise_bits: usize, algorithm: u8) -> Inte | ||||
|             let reduced = deep_lll(basis_matrix.clone(), Rational::from((51, 100))).unwrap(); | ||||
|             reduced.columns[0][0].clone() | ||||
|         } | ||||
|         // 3u8 => { | ||||
|         //     lattice_reduce(&mut basis_matrix, 0.51, 0.75); | ||||
|         //     lll_matrix[0][0].clone() | ||||
|         // } | ||||
|         3u8 => { | ||||
|             lattice_reduce(&mut basis_matrix, 0.51, 0.75); | ||||
|             basis_matrix[0][0].clone() | ||||
|         } | ||||
|         _ => panic!("Unknown algorithm value: {}", algorithm), | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| use crate::matrix::Matrix; | ||||
| use rug::{Rational, Integer}; | ||||
| use rug::{Integer, Rational}; | ||||
|  | ||||
| /// Perform DeepLLL reduction on a given lattice basis represented by Matrix. | ||||
| /// 1/4 < delta < 1. | ||||
| @@ -78,7 +78,12 @@ fn gramm_schmidt(mat: &Matrix<Integer>) -> (Vec<Vec<Rational>>, Vec<Rational>) { | ||||
| } | ||||
|  | ||||
| /// Size-reduce column k in-place | ||||
| fn size_reduce(mat: &mut Matrix<Integer>, mu: &mut [Vec<Rational>], b_star_sq: &mut [Rational], k: usize) { | ||||
| fn size_reduce( | ||||
|     mat: &mut Matrix<Integer>, | ||||
|     mu: &mut [Vec<Rational>], | ||||
|     b_star_sq: &mut [Rational], | ||||
|     k: usize, | ||||
| ) { | ||||
|     let mut updated = true; | ||||
|     while updated { | ||||
|         updated = false; | ||||
|   | ||||
							
								
								
									
										112
									
								
								src/lll.rs
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								src/lll.rs
									
									
									
									
									
								
							| @@ -1,22 +1,122 @@ | ||||
| use lll_rs::vector::BigVector; | ||||
| use lll_rs::matrix::Matrix as LLLMatrix; | ||||
| use crate::matrix::Matrix; | ||||
| use lll_rs::{matrix::Matrix as LLLMatrix, vector::BigVector}; | ||||
| use rug::{Integer, Rational}; | ||||
| use std::cmp::max; | ||||
| use std::ops::Sub; | ||||
|  | ||||
| /// Lattice reduction (L² algorithm, improved LLL) | ||||
| pub fn lattice_reduce(basis: &mut Matrix<Integer>, eta: f64, delta: f64) { | ||||
|     assert!(0.25 < delta && delta < 1.); | ||||
|     assert!(0.5 < eta && eta * eta < delta); | ||||
|     let d = basis.n; | ||||
|     let mut gram: Matrix<Integer> = Matrix::init(d, d); // Gram matrix (upper triangular) | ||||
|     let mut r: Matrix<Rational> = Matrix::init(d, d);   // r_ij matrix | ||||
|     let mut mu: Matrix<Rational> = Matrix::init(d, d);  // Gram coefficient matrix | ||||
|     // Gram matrix | ||||
|     for i in 0..d { | ||||
|         for j in 0..=i { | ||||
|             gram[i][j] = basis[i].clone() * basis[j].clone(); | ||||
|         } | ||||
|     } | ||||
|     let eta_minus = Rational::from_f64((eta + 0.5) / 2.).unwrap(); | ||||
|     let delta_plus = Rational::from_f64((delta + 1.) / 2.).unwrap(); | ||||
|  | ||||
|     r[0][0] = Rational::from(&gram[0][0]); | ||||
|     let mut k = 1; | ||||
|     while k < d { | ||||
|         size_reduce( | ||||
|             k, | ||||
|             d, | ||||
|             basis, | ||||
|             &mut gram, | ||||
|             &mut mu, | ||||
|             &mut r, | ||||
|             Rational::from(&eta_minus), | ||||
|         ); | ||||
|         let delta_criterion = Rational::from(&delta_plus * &r[k - 1][k - 1]); | ||||
|         let scalar_criterion = &r[k][k] + Rational::from(&mu[k][k - 1]).square() * &r[k - 1][k - 1]; | ||||
|         // Lovazs condition | ||||
|         if delta_criterion < scalar_criterion { | ||||
|             k += 1; | ||||
|         } else { | ||||
|             basis.swap(k, k - 1); | ||||
|             // Updating Gram matrix | ||||
|             for j in 0..d { | ||||
|                 if j < k { | ||||
|                     gram[k][j] = basis[k].clone() * basis[j].clone(); | ||||
|                     gram[k - 1][j] = basis[k - 1].clone() * basis[j].clone(); | ||||
|                 } else { | ||||
|                     gram[j][k] = basis[k].clone() * basis[j].clone(); | ||||
|                     gram[j][k - 1] = basis[k - 1].clone() * basis[j].clone(); | ||||
|                 } | ||||
|             } | ||||
|             // Updating mu and r | ||||
|             for i in 0..=k { | ||||
|                 for j in 0..=i { | ||||
|                     r[i][j] = Rational::from(&gram[i][j]) | ||||
|                         - (0..j) | ||||
|                             .map(|index| Rational::from(&mu[j][index] * &r[i][index])) | ||||
|                             .sum::<Rational>(); | ||||
|                     mu[i][j] = Rational::from(&r[i][j] / &r[j][j]); | ||||
|                 } | ||||
|             } | ||||
|             k = max(1, k - 1); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| fn size_reduce( | ||||
|     k: usize, | ||||
|     d: usize, | ||||
|     basis: &mut Matrix<Integer>, | ||||
|     gram: &mut Matrix<Integer>, | ||||
|     mu: &mut Matrix<Rational>, | ||||
|     r: &mut Matrix<Rational>, | ||||
|     eta: Rational, | ||||
| ) { | ||||
|     // Update mu and r | ||||
|     for i in 0..=k { | ||||
|         r[k][i] = Rational::from(&gram[k][i]) | ||||
|             - (0..i) | ||||
|                 .map(|index| Rational::from(&mu[i][index] * &r[k][index])) | ||||
|                 .sum::<Rational>(); | ||||
|         mu[k][i] = Rational::from(&r[k][i] / &r[i][i]); | ||||
|     } | ||||
|  | ||||
|     if (0..k).any(|index| mu[k][index] > eta) { | ||||
|         for i in (0..k).rev() { | ||||
|             let (_, x) = Rational::from(&mu[k][i]).fract_round(Integer::new()); | ||||
|             basis[k] = basis[k].clone() - basis[i].mul_scalar(&x); | ||||
|             // Updating Gram matrix | ||||
|             for j in 0..d { | ||||
|                 if j < k { | ||||
|                     gram[k][j] = basis[k].clone() * basis[j].clone(); | ||||
|                 } else { | ||||
|                     gram[j][k] = basis[k].clone() * basis[j].clone(); | ||||
|                 } | ||||
|             } | ||||
|             for j in 0..i { | ||||
|                 let shift = Rational::from(&mu[i][j]); | ||||
|                 mu[k][j] -= Rational::from(&x) * shift; | ||||
|             } | ||||
|         } | ||||
|         size_reduce(k, d, basis, gram, mu, r, eta); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| /// conversion to LLLMatrix | ||||
| impl Matrix<Integer> { | ||||
|     pub fn to_lll_matrix(&self) -> LLLMatrix<BigVector> { | ||||
|         let n = self.n; | ||||
|         let mut lll_mat = LLLMatrix::init(n, n); | ||||
|  | ||||
|         for row_idx in 0..n { | ||||
|             let mut elements = Vec::with_capacity(n); | ||||
|  | ||||
|             for col_idx in 0..n { | ||||
|                 let val = self[(row_idx, col_idx)].clone(); | ||||
|                 let val = self[(col_idx, row_idx)].clone(); | ||||
|                 elements.push(val); | ||||
|             } | ||||
|  | ||||
|             lll_mat[row_idx] = BigVector::from_vector(elements); | ||||
|         } | ||||
|         lll_mat | ||||
|   | ||||
| @@ -1,9 +1,6 @@ | ||||
| use crate::vector::Vector; | ||||
| use rug::{ops::Pow, Integer}; | ||||
| use std::{ | ||||
|     fmt, | ||||
|     ops::{Index, IndexMut}, | ||||
| }; | ||||
| use std::ops::{Index, IndexMut}; | ||||
|  | ||||
| #[derive(Debug, PartialEq, Clone)] | ||||
| pub struct Matrix<T> { | ||||
| @@ -12,12 +9,19 @@ pub struct Matrix<T> { | ||||
|     pub columns: Vec<Vector<T>>, | ||||
| } | ||||
|  | ||||
| impl<T> Matrix<T> { | ||||
| impl<T: Default> Matrix<T> { | ||||
|     pub fn init(n: usize, m: usize) -> Self { | ||||
|         let mut columns = Vec::with_capacity(n); | ||||
|         for _ in 0..n { | ||||
|             columns.push(Vector::init(m)); | ||||
|         } | ||||
|         Self { n, m, columns } | ||||
|     } | ||||
|  | ||||
|     pub fn new(n: usize, m: usize, values: Vec<T>) -> Option<Self> { | ||||
|         if n * m != values.len() { | ||||
|             return None; | ||||
|         } | ||||
|         // avoid requiring Vec<T>: Clone by building manually | ||||
|         let mut columns = Vec::with_capacity(n); | ||||
|         for _ in 0..n { | ||||
|             columns.push(Vec::with_capacity(m)); | ||||
| @@ -32,6 +36,10 @@ impl<T> Matrix<T> { | ||||
|             columns: columns.into_iter().map(Vector::from_vec).collect(), | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     pub fn swap(&mut self, i: usize, j: usize) { | ||||
|         self.columns.swap(i, j); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Matrix<T> | ||||
| @@ -40,19 +48,22 @@ where | ||||
| { | ||||
|     pub fn new_lattice(noise_bits: usize, ciphertexts: Vec<T>) -> Option<Self> { | ||||
|         let n = ciphertexts.len(); | ||||
|         let mut columns = vec![Vec::with_capacity(n); n]; | ||||
|         if n < 1 { | ||||
|             return None; | ||||
|         } | ||||
|         let mut columns = vec![vec![]; n]; | ||||
|  | ||||
|         // First row: [2^(noise+1), ciphertexts[1], ciphertexts[2], ...] | ||||
|         // First column: [2^(noise_bits+1), ciphertexts[1], ciphertexts[2], ..., ciphertexts[n-1]] | ||||
|         let two_pow = Integer::from(2u64).pow((noise_bits + 1) as u32); | ||||
|         columns[0].push(T::from(two_pow)); | ||||
|         for i in 1..n { | ||||
|             columns[i].push(ciphertexts[i].clone()); | ||||
|             columns[0].push(ciphertexts[i].clone()); | ||||
|         } | ||||
|  | ||||
|         // Subsequent rows form identity matrix with ciphertexts[0] | ||||
|         for i in 1..n { | ||||
|             for (j, column) in columns.iter_mut().enumerate().take(n) { | ||||
|                 column.push(if i == j { | ||||
|         // Subsequent columns: identity matrix with ciphertexts[0] | ||||
|         for j in 1..n { | ||||
|             for i in 0..n { | ||||
|                 columns[j].push(if i == j { | ||||
|                     ciphertexts[0].clone() | ||||
|                 } else { | ||||
|                     T::default() | ||||
| @@ -89,6 +100,27 @@ impl<T> IndexMut<(usize, usize)> for Matrix<T> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Index<usize> for Matrix<T> { | ||||
|     type Output = Vector<T>; | ||||
|     /// `matrix[col]` yields a `&Vector<T>` representing that column. | ||||
|     fn index(&self, col: usize) -> &Self::Output { | ||||
|         if col >= self.columns.len() { | ||||
|             panic!("Matrix column index out of bounds"); | ||||
|         } | ||||
|         &self.columns[col] | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> IndexMut<usize> for Matrix<T> { | ||||
|     /// `matrix[col] = …` or `matrix[col][row] = …` to mutate a column (or element). | ||||
|     fn index_mut(&mut self, col: usize) -> &mut Self::Output { | ||||
|         if col >= self.columns.len() { | ||||
|             panic!("Matrix column index out of bounds"); | ||||
|         } | ||||
|         &mut self.columns[col] | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|   | ||||
| @@ -4,7 +4,7 @@ use std::{ | ||||
|     ops::{Add, Index, IndexMut, Mul, Sub}, | ||||
| }; | ||||
|  | ||||
| #[derive(Clone, PartialEq)] | ||||
| #[derive(PartialEq)] | ||||
| pub struct Vector<T> { | ||||
|     pub elements: Vec<T>, | ||||
| } | ||||
| @@ -44,6 +44,14 @@ where | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Clone> Clone for Vector<T> { | ||||
|     fn clone(&self) -> Self { | ||||
|         Vector { | ||||
|             elements: self.elements.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Add for Vector<T> | ||||
| where | ||||
|     T: Add<Output = T>, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user