fix: bug regarding MixedRadix coset (I)NTT for NM/MN ordering (#497)

The bug is in how twiddles array is indexed when multiplied by a mixed
(M) vector to implement (I)NTT on cosets.
The fix is to use the DIF-digit-reverse to compute the index of the element in the
natural (N) vector that moved to index 'i' in the M vector. This is
emulating a DIT-digit-reverse (which is mixing like a DIF-compute)
reorder of the twiddles array and element-wise multiplication without
reordering the twiddles memory.
This commit is contained in:
yshekel
2024-04-25 18:09:27 +03:00
committed by GitHub
parent f8d15e2613
commit 36e288c1fa
5 changed files with 90 additions and 11 deletions

View File

@@ -378,6 +378,13 @@ macro_rules! impl_ntt_tests {
check_ntt_coset_from_subgroup::<$field>()
}
#[test]
#[parallel]
fn test_ntt_coset_interpolation_nm() {
INIT.get_or_init(move || init_domain::<$field>(MAX_SIZE, DEFAULT_DEVICE_ID, FAST_TWIDDLES_MODE));
check_ntt_coset_interpolation_nm::<$field>();
}
#[test]
#[parallel]
fn test_ntt_arbitrary_coset() {

View File

@@ -190,6 +190,62 @@ where
}
}
pub fn check_ntt_coset_interpolation_nm<F: FieldImpl + ArkConvertible>()
where
F::ArkEquivalent: FftField,
<F as FieldImpl>::Config: NTT<F, F> + GenerateRandom<F>,
{
let test_sizes = [1 << 9, 1 << 10, 1 << 11, 1 << 13, 1 << 14, 1 << 16];
for test_size in test_sizes {
let test_size_rou = F::ArkEquivalent::get_root_of_unity((test_size << 1) as u64).unwrap();
let coset_generators = [F::from_ark(test_size_rou), F::Config::generate_random(1)[0]];
let scalars: Vec<F> = F::Config::generate_random(test_size);
let ark_domain = GeneralEvaluationDomain::<F::ArkEquivalent>::new(test_size).unwrap();
for coset_gen in coset_generators {
// (1) intt from evals to coeffs
let mut config = NTTConfig::default();
config.ordering = Ordering::kNM;
config.ntt_algorithm = NttAlgorithm::MixedRadix;
let mut intt_result = vec![F::zero(); test_size];
let intt_result = HostSlice::from_mut_slice(&mut intt_result);
ntt(HostSlice::from_slice(&scalars), NTTDir::kInverse, &config, intt_result).unwrap();
let mut ark_scalars = scalars
.iter()
.map(|v| v.to_ark())
.collect::<Vec<F::ArkEquivalent>>();
ark_domain.ifft_in_place(&mut ark_scalars);
// (2) coset-ntt (compute coset evals)
config.coset_gen = coset_gen;
config.ordering = Ordering::kMN;
let mut coset_evals = vec![F::zero(); test_size];
ntt(
intt_result,
NTTDir::kForward,
&config,
HostSlice::from_mut_slice(&mut coset_evals),
)
.unwrap();
let ark_coset_domain = ark_domain
.get_coset(coset_gen.to_ark())
.unwrap();
ark_coset_domain.fft_in_place(&mut ark_scalars); // to reuse in next iteration
let coest_evals_as_ark = coset_evals
.iter()
.map(|v| v.to_ark())
.collect::<Vec<F::ArkEquivalent>>();
assert_eq!(coest_evals_as_ark, ark_scalars);
}
}
}
pub fn check_ntt_arbitrary_coset<F: FieldImpl + ArkConvertible>()
where
F::ArkEquivalent: FftField + ArkField,