use super::CpuBackend;
use crate::core::circle::Coset;
use crate::core::fft::ibutterfly;
use crate::core::fields::m31::BaseField;
use crate::core::fields::qm31::SecureField;
use crate::core::poly::line::LineDomain;
use crate::core::utils::bit_reverse_index;
use crate::prover::fri::FriOps;
use crate::prover::line::LineEvaluation;
use crate::prover::poly::circle::SecureEvaluation;
use crate::prover::poly::twiddles::TwiddleTree;
use crate::prover::poly::BitReversedOrder;
use crate::prover::secure_column::SecureColumnByCoords;
impl FriOps for CpuBackend {
fn fold_line(
eval: &LineEvaluation<Self>,
alpha: SecureField,
_twiddles: &TwiddleTree<Self>,
fold_step: u32,
) -> LineEvaluation<Self> {
assert!(fold_step >= 1);
let mut folding_alpha = alpha;
let mut res = fold_line_cpu(eval, folding_alpha);
for _ in 0..fold_step - 1 {
folding_alpha = folding_alpha * folding_alpha;
res = fold_line_cpu(&res, folding_alpha)
}
res
}
fn fold_circle_into_line(
src: &SecureEvaluation<Self, BitReversedOrder>,
alpha: SecureField,
_twiddles: &TwiddleTree<Self>,
) -> LineEvaluation<Self> {
fold_circle_into_line_cpu(src, alpha)
}
fn decompose(
eval: &SecureEvaluation<Self, BitReversedOrder>,
) -> (SecureEvaluation<Self, BitReversedOrder>, SecureField) {
let lambda = Self::decomposition_coefficient(eval);
let mut g_values = unsafe { SecureColumnByCoords::<Self>::uninitialized(eval.len()) };
let domain_size = eval.len();
let half_domain_size = domain_size / 2;
for i in 0..half_domain_size {
let x = eval.values.at(i);
let val = x - lambda;
g_values.set(i, val);
}
for i in half_domain_size..domain_size {
let x = eval.values.at(i);
let val = x + lambda;
g_values.set(i, val);
}
let g = SecureEvaluation::new(eval.domain, g_values);
(g, lambda)
}
}
pub fn fold_line_cpu(
eval: &LineEvaluation<CpuBackend>,
alpha: SecureField,
) -> LineEvaluation<CpuBackend> {
let n = eval.len();
assert!(n >= 2, "Evaluation too small");
let domain = eval.domain();
let folded_values = eval
.values
.into_iter()
.array_chunks()
.enumerate()
.map(|(i, [f_x, f_neg_x])| {
let x = domain.at(bit_reverse_index(i << 1, domain.log_size()));
let (mut f0, mut f1) = (f_x, f_neg_x);
ibutterfly(&mut f0, &mut f1, x.inverse());
f0 + alpha * f1
})
.collect();
LineEvaluation::new(domain.double(), folded_values)
}
pub fn fold_circle_into_line_cpu(
src: &SecureEvaluation<CpuBackend, BitReversedOrder>,
alpha: SecureField,
) -> LineEvaluation<CpuBackend> {
let domain = src.domain;
let line_log_size = src.domain.log_size() - 1;
let dst_domain = LineDomain::new(Coset::half_odds(line_log_size));
let values = unsafe { SecureColumnByCoords::uninitialized(1 << line_log_size) };
let mut dst = LineEvaluation::new(dst_domain, values);
src.values
.into_iter()
.array_chunks()
.enumerate()
.for_each(|(i, [f_p, f_neg_p])| {
let p = domain.at(bit_reverse_index(i << 1, domain.log_size()));
let (mut f0_px, mut f1_px) = (f_p, f_neg_p);
ibutterfly(&mut f0_px, &mut f1_px, p.y.inverse());
let f_prime = alpha * f1_px + f0_px;
dst.values.set(i, f_prime)
});
dst
}
impl CpuBackend {
fn decomposition_coefficient(eval: &SecureEvaluation<Self, BitReversedOrder>) -> SecureField {
let domain_size = 1 << eval.domain.log_size();
let half_domain_size = domain_size / 2;
let a_sum = (0..half_domain_size)
.map(|i| eval.values.at(i))
.sum::<SecureField>();
let b_sum = (half_domain_size..domain_size)
.map(|i| eval.values.at(i))
.sum::<SecureField>();
(a_sum - b_sum) / BaseField::from_u32_unchecked(domain_size as u32)
}
}
#[cfg(test)]
mod tests {
use num_traits::Zero;
use crate::core::fields::m31::BaseField;
use crate::core::fields::qm31::SecureField;
use crate::core::poly::circle::CanonicCoset;
use crate::m31;
use crate::prover::backend::cpu::{CpuCircleEvaluation, CpuCirclePoly};
use crate::prover::backend::CpuBackend;
use crate::prover::fri::FriOps;
use crate::prover::poly::circle::SecureEvaluation;
use crate::prover::poly::BitReversedOrder;
use crate::prover::secure_column::SecureColumnByCoords;
#[test]
fn decompose_coeff_out_fft_space_test() {
for domain_log_size in 5..12 {
let domain_log_half_size = domain_log_size - 1;
let s = CanonicCoset::new(domain_log_size);
let domain = s.circle_domain();
let mut coeffs = vec![BaseField::zero(); 1 << domain_log_size];
coeffs[1 << domain_log_half_size] = m31!(1);
assert!(!CpuCirclePoly::new(coeffs.clone()).is_in_fft_space(domain_log_half_size));
let poly = CpuCirclePoly::new(coeffs);
let values = poly.evaluate(domain);
let secure_column = SecureColumnByCoords {
columns: [
values.values.clone(),
values.values.clone(),
values.values.clone(),
values.values.clone(),
],
};
let secure_eval = SecureEvaluation::<CpuBackend, BitReversedOrder>::new(
domain,
secure_column.clone(),
);
let (g, lambda) = CpuBackend::decompose(&secure_eval);
assert_ne!(lambda, SecureField::zero());
for i in 0..4 {
let basefield_column = g.columns[i].clone();
let eval = CpuCircleEvaluation::new(domain, basefield_column);
let coeffs = eval.interpolate().coeffs;
assert!(CpuCirclePoly::new(coeffs).is_in_fft_space(domain_log_half_size));
}
}
}
}