use p3_field::{ExtensionField, Field};
use p3_multilinear_util::point::Point;
use super::tensor::TensorAlgebra;
pub fn equality_element<F: Field, EF: ExtensionField<F>>(
r_high: &Point<EF>,
r_prime: &Point<EF>,
) -> TensorAlgebra<F, EF> {
assert_eq!(
r_high.num_variables(),
r_prime.num_variables(),
"r_high and r_prime must name the same number of variables"
);
let mut e = TensorAlgebra::one();
for i in 0..r_high.num_variables() {
let (a, b) = (r_high[i], r_prime[i]);
let mut agree = e.clone();
agree.scale_columns(a);
agree.scale_rows(b);
e.scale_columns(EF::ONE - a);
e.scale_rows(EF::ONE - b);
e += agree;
}
e
}
#[cfg(test)]
mod tests {
use alloc::vec;
use p3_baby_bear::BabyBear;
use p3_field::PrimeCharacteristicRing;
use p3_field::extension::BinomialExtensionField;
use p3_multilinear_util::poly::Poly;
use proptest::prelude::*;
use rand::SeedableRng;
use rand::rngs::SmallRng;
use super::*;
use crate::ring_switch::test_util::{EF, F};
use crate::ring_switch::weights::batched_weights;
fn equality_element_reference<G: Field, EG: ExtensionField<G>>(
r_high: &Point<EG>,
r_prime: &Point<EG>,
) -> TensorAlgebra<G, EG> {
assert_eq!(
r_high.num_variables(),
r_prime.num_variables(),
"r_high and r_prime must name the same number of variables"
);
let eq_high = Poly::<EG>::new_from_point(r_high.as_slice(), EG::ONE);
let eq_prime = Poly::<EG>::new_from_point(r_prime.as_slice(), EG::ONE);
let mut e = TensorAlgebra::zero();
for (&a, &b) in eq_high.as_slice().iter().zip(eq_prime.as_slice()) {
e += TensorAlgebra::exterior_product(a, b);
}
e
}
#[test]
fn the_recurrence_matches_the_definition() {
let mut rng = SmallRng::seed_from_u64(11);
for ell_prime in 0..=4 {
let r_high = Point::<EF>::rand(&mut rng, ell_prime);
let r_prime = Point::<EF>::rand(&mut rng, ell_prime);
assert_eq!(
equality_element::<F, EF>(&r_high, &r_prime),
equality_element_reference::<F, EF>(&r_high, &r_prime),
"ell' = {ell_prime}"
);
}
}
#[test]
fn equality_rows_reproduce_the_batched_weights() {
let mut rng = SmallRng::seed_from_u64(12);
let ell_prime = 3;
let r_high = Point::<EF>::rand(&mut rng, ell_prime);
let r_prime = Point::<EF>::rand(&mut rng, ell_prime);
let r_batch = Point::<EF>::rand(&mut rng, 4);
let rows = equality_element::<F, EF>(&r_high, &r_prime).rows();
let eq_batch = Poly::<EF>::new_from_point(r_batch.as_slice(), EF::ONE);
let batched: EF = rows
.iter()
.zip(eq_batch.as_slice())
.map(|(e, b)| *e * *b)
.sum();
let weights = batched_weights::<F, EF>(&r_high, &r_batch);
assert_eq!(batched, weights.eval_ext::<F>(&r_prime));
}
#[test]
fn the_recurrence_matches_the_definition_in_odd_characteristic() {
type G = BabyBear;
type EG = BinomialExtensionField<BabyBear, 4>;
let mut rng = SmallRng::seed_from_u64(13);
for ell_prime in 0..=5 {
let r_high = Point::<EG>::rand(&mut rng, ell_prime);
let r_prime = Point::<EG>::rand(&mut rng, ell_prime);
assert_eq!(
equality_element::<G, EG>(&r_high, &r_prime),
equality_element_reference::<G, EG>(&r_high, &r_prime),
"ell' = {ell_prime}"
);
}
}
#[test]
fn empty_point_gives_the_identity() {
let e = equality_element::<F, EF>(&Point::new(vec![]), &Point::new(vec![]));
assert_eq!(e, TensorAlgebra::<F, EF>::one());
}
proptest! {
#[test]
fn the_recurrence_matches_the_definition_everywhere(ell_prime in 0usize..6, seed: u64) {
let mut rng = SmallRng::seed_from_u64(seed);
let r_high = Point::<EF>::rand(&mut rng, ell_prime);
let r_prime = Point::<EF>::rand(&mut rng, ell_prime);
prop_assert_eq!(
equality_element::<F, EF>(&r_high, &r_prime),
equality_element_reference::<F, EF>(&r_high, &r_prime),
);
}
#[test]
fn the_recurrence_matches_the_definition_everywhere_in_odd_characteristic(
ell_prime in 0usize..6,
seed: u64,
) {
type G = BabyBear;
type EG = BinomialExtensionField<BabyBear, 4>;
let mut rng = SmallRng::seed_from_u64(seed);
let r_high = Point::<EG>::rand(&mut rng, ell_prime);
let r_prime = Point::<EG>::rand(&mut rng, ell_prime);
prop_assert_eq!(
equality_element::<G, EG>(&r_high, &r_prime),
equality_element_reference::<G, EG>(&r_high, &r_prime),
);
}
}
}