Repository: NethermindEth/sonobe
Pull request #13: optimisations
Author: login: NiDimi
Created: 2025-03-21T16:52:53Z | Merged: 2025-04-21T13:39:29Z

-> used mul_assign
-> Fixed commit to sparse
-> Fixed setup to be more efficient


Diff:
diff --git a/benches/commitment_schemes.rs b/benches/commitment_schemes.rs
new file mode 100644
index 000000000..84a096c35
--- /dev/null
+++ b/benches/commitment_schemes.rs
@@ -0,0 +1,143 @@
+use ark_ec::VariableBaseMSM;
+use ark_pallas::{Fr, Projective};
+use ark_std::{log2, UniformRand, Zero};
+use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
+use folding_schemes::commitment::hyrax::{Hyrax, HyraxGenerators};
+use folding_schemes::commitment::pedersen::Pedersen;
+use folding_schemes::commitment::{CommitmentScheme, NethermindCommitmentScheme};
+use folding_schemes::Curve;
+use matrex::Matrix;
+use rand::{Rng, RngCore};
+use std::time::Duration;
+
+// Matrix sizes to test (dimensions will be 2^size x 2^size)
+const MATRIX_SIZES: &[usize] = &[4, 6, 8, 10]; // 16, 64, 128, 1024
+const SPARSITY_FACTOR: f64 = 0.05; // 5% non-zero elements for sparse matrices
+
+/// Generates a random dense matrix of specified dimensions
+fn random_dense_matrix<C: Curve>(n: usize, rng: &mut impl RngCore) -> Matrix<C::ScalarField> {
+    let elements: Vec<C::ScalarField> = (0..n * n).map(|_| C::ScalarField::rand(rng)).collect();
+    Matrix::dense_from_vec(elements, n, n).unwrap()
+}
+
+/// Generates a random sparse matrix with specified dimensions and sparsity
+fn random_sparse_matrix<C: Curve>(n: usize, rng: &mut impl RngCore) -> Matrix<C::ScalarField> {
+    let non_zero_count = (n * n * SPARSITY_FACTOR as usize).max(1);
+
+    let mut elements = Vec::with_capacity(non_zero_count);
+    for _ in 0..non_zero_count {
+        let row = rand::thread_rng().gen_range(0..n);
+        let col = rand::thread_rng().gen_range(0..n);
+        let index = row * n + col;
+        let value = C::ScalarField::rand(rng);
+        elements.push((index, value));
+    }
+
+    Matrix::sparse_from_vec(elements, n, n).unwrap()
+}
+
+/// Benchmark Pedersen vs Hyrax for dense matrices
+fn bench_dense_commits(c: &mut Criterion) {
+    let mut group = c.benchmark_group("Dense Matrix Commitments");
+    group.measurement_time(Duration::from_secs(10));
+
+    let mut rng = ark_std::test_rng();
+
+    for &size in MATRIX_SIZES {
+        let n = 1 << size; // 2^size
+        let matrix = random_dense_matrix::<Projective>(n, &mut rng);
+        let data = matrix.as_dense_slice().unwrap();
+
+        // Setup parameters
+        let pedersen_params = Pedersen::<Projective>::setup_prover(&mut rng, n * n).unwrap();
+        let hyrax_params = HyraxGenerators::<Projective>::setup(&mut rng, log2(n * n) as usize);
+
+        group.bench_with_input(BenchmarkId::new("Pedersen", n), &n, |b, _| {
+            b.iter(|| Pedersen::<Projective>::commit(&pedersen_params, data, &Fr::zero()).unwrap());
+        });
+
+        group.bench_with_input(BenchmarkId::new("Hyrax", n), &n, |b, _| {
+            b.iter(|| Hyrax::<Projective>::commit(data, &hyrax_params).unwrap());
+        });
+    }
+
+    group.finish();
+}
+
+/// Benchmark Pedersen vs Hyrax for sparse matrices
+fn bench_sparse_commits(c: &mut Criterion) {
+    let mut group = c.benchmark_group("Sparse Matrix Commitments");
+    group.measurement_time(Duration::from_secs(10));
+
+    let mut rng = ark_std::test_rng();
+
+    for &size in MATRIX_SIZES {
+        let n = 1 << size; // 2^size
+        let matrix = random_sparse_matrix::<Projective>(n, &mut rng);
+        let sparse_data = matrix.as_sparse_slice().unwrap();
+
+        // Setup parameters
+        let pedersen_params = Pedersen::<Projective>::setup_prover(&mut rng, n * n).unwrap();
+        let hyrax_params = HyraxGenerators::<Projective>::setup(&mut rng, log2(n * n) as usize);
+
+        group.bench_with_input(BenchmarkId::new("Pedersen Sparse", n), &n, |b, _| {
+            b.iter(|| {
+                Pedersen::<Projective>::commit_sparse(&pedersen_params, sparse_data, &Fr::zero())
+                    .unwrap()
+            });
+        });
+
+        group.bench_with_input(BenchmarkId::new("Hyrax Sparse", n), &n, |b, _| {
+            b.iter(|| {
+                Hyrax::<Projective>::commit_sparse_matrix(sparse_data, &hyrax_params).unwrap()
+            });
+        });
+    }
+
+    group.finish();
+}
+/// Benches the same MSM in 2 scenarios: a single MSM, or multiple smaller MSM until reaching the same size
+fn bench_msm(c: &mut Criterion) {
+    let mut group = c.benchmark_group("MultiScalar Exponentiations");
+    group.measurement_time(Duration::from_secs(10));
+    let mut rng = ark_std::test_rng();
+
+    for &size in MATRIX_SIZES {
+        let n = 1 << size; // 2^size
+        let matrix = random_dense_matrix::<Projective>(n, &mut rng);
+        let data = matrix.as_dense_slice().unwrap();
+        let partial_data: Vec<_> = (0..n).map(|_| Fr::rand(&mut rng)).collect();
+
+        // Use fully qualified type syntax for Affine
+        type ProjectiveAffine = <Projective as ark_ec::CurveGroup>::Affine;
+
+        // Setup parameters
+        let generators: Vec<ProjectiveAffine> =
+            (0..n).map(|_| ProjectiveAffine::rand(&mut rng)).collect();
+        let partial_gen = &generators[..n];
+
+        let pedersen_params = Pedersen::<Projective>::setup_prover(&mut rng, n * n).unwrap();
+
+        group.bench_with_input(BenchmarkId::new("Single Large MSM", n), &n, |b, _| {
+            b.iter(|| Projective::msm_unchecked(&pedersen_params.generators, data));
+        });
+
+        group.bench_with_input(BenchmarkId::new("Many Small MSM", n), &n, |b, _| {
+            b.iter(|| {
+                // Do n MSM os size n instead of a large MSM osf size n*n.
+                for _ in 0..n {
+                    let _ = Projective::msm_unchecked(&partial_gen, &partial_data);
+                }
+            });
+        });
+    }
+    group.finish();
+}
+
+criterion_group!(
+    benches,
+    bench_msm,
+    bench_dense_commits,
+    bench_sparse_commits
+);
+criterion_main!(benches);
diff --git a/benches/mova_matrix.rs b/benches/mova_matrix.rs
index 5696de5c8..f906f3011 100644
--- a/benches/mova_matrix.rs
+++ b/benches/mova_matrix.rs
@@ -3,8 +3,9 @@ use ark_crypto_primitives::sponge::CryptographicSponge;
 use ark_pallas::{Fr, Projective};
 use ark_std::{log2, UniformRand};
 use criterion::{criterion_group, criterion_main, Criterion};
+use folding_schemes::commitment::hyrax::HyraxGenerators;
 use folding_schemes::commitment::pedersen::Pedersen;
-use folding_schemes::commitment::CommitmentScheme;
+use folding_schemes::commitment::{CommitmentScheme, NethermindCommitmentScheme};
 use folding_schemes::folding::nova::nifs::mova_matrix::{RelaxedCommittedRelation, Witness, NIFS};
 use folding_schemes::transcript::poseidon::poseidon_canonical_config;
 use folding_schemes::Curve;
@@ -27,11 +28,11 @@ fn random_sparse_matrix<C: Curve>(n: usize, rng: &mut impl RngCore) -> Matrix<C:
 }
 
 // Helper functions
-fn get_instances<C: Curve, CS: CommitmentScheme<C>>(
+fn get_instances<C: Curve>(
     num: usize,
     n: usize,
     rng: &mut impl RngCore,
-    params: &CS::ProverParams,
+    params: &HyraxGenerators<C>,
 ) -> Vec<(Witness<C>, RelaxedCommittedRelation<C>)> {
     (0..num)
         .map(|_| -> (Witness<C>, RelaxedCommittedRelation<C>) {
@@ -40,16 +41,17 @@ fn get_instances<C: Curve, CS: CommitmentScheme<C>>(
             // B matrix
             let b = random_sparse_matrix::<C>(n, rng);
             // C = A * B matrix
-            let c = (a.clone() * &b).unwrap();
+            let c = (&a * &b).unwrap();
             // Error matrix initialized to 0s
             let e = Matrix::zero(n, n);
+
             // Random challenge
             let rE = (0..2 * log2(n))
                 .map(|_| C::ScalarField::rand(rng))
                 .collect();
             // Witness
             let witness = Witness::new::<false>(a, b, c, e);
-            let instance = witness.commit::<CS, false>(params, rE).unwrap();
+            let instance = witness.commit(params, rE).unwrap();
             (witness, instance)
         })
         .collect()
@@ -65,8 +67,10 @@ fn bench_mova_matrix(c: &mut Criterion) {
             .measurement_time(Duration::from_secs(20 * (*count as u64)))
             .bench_function(&format!("{count}"), |b| {
                 // Set up transcript and commitment scheme
-                let (pedersen_params, _) =
-                    Pedersen::<Projective>::setup(&mut rng, mat_dim * mat_dim).unwrap();
+                let hyrax_params = HyraxGenerators::<Projective>::setup(
+                    &mut rng,
+                    log2(mat_dim * mat_dim) as usize,
+                );
                 let poseidon_config = poseidon_canonical_config::<Fr>();
                 let pp_hash = Fr::rand(&mut rng);
 
@@ -76,32 +80,30 @@ fn bench_mova_matrix(c: &mut Criterion) {
                         let mut instances: Vec<(
                             Witness<Projective>,
                             RelaxedCommittedRelation<Projective>,
-                        )> = get_instances::<Projective, Pedersen<Projective>>(
+                        )> = get_instances::<Projective>(
                             count + 1, // we want the number of folds plus one for the acc_instance
                             mat_dim,
                             &mut rng,
-                            &pedersen_params,
+                            &hyrax_params,
                         );
                         let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
                         let mut acc = instances.pop().unwrap();
 
                         for _ in 0..*count {
-                            let next = instances.pop().unwrap();
+                            let mut next = instances.pop().unwrap();
                             total_duration += {
                                 let timer = Instant::now();
-                                let (wit_acc, inst_acc, _) = NIFS::<
-                                    Projective,
-                                    Pedersen<Projective>,
-                                    PoseidonSponge<Fr>,
-                                >::prove(
-                                    &mut transcript_p,
-                                    pp_hash,
-                                    &next.0,
-                                    &next.1,
-                                    &acc.0,
-                                    &acc.1,
-                                )
-                                .unwrap();
+
+                                let (wit_acc, inst_acc, _) =
+                                    NIFS::<Projective, PoseidonSponge<Fr>>::prove(
+                                        &mut transcript_p,
+                                        pp_hash,
+                                        &mut next.0,
+                                        &next.1,
+                                        &acc.0,
+                                        &acc.1,
+                                    )
+                                    .unwrap();
                                 let time = timer.elapsed();
                                 acc = (wit_acc, inst_acc);
                                 time
diff --git a/examples/mova_matrix_memory.rs b/examples/mova_matrix_memory.rs
new file mode 100644
index 000000000..47f6204cc
--- /dev/null
+++ b/examples/mova_matrix_memory.rs
@@ -0,0 +1,130 @@
+use ark_crypto_primitives::sponge::poseidon::PoseidonSponge;
+use ark_crypto_primitives::sponge::CryptographicSponge;
+use ark_pallas::{Fr, Projective};
+use ark_std::{log2, UniformRand};
+use folding_schemes::commitment::hyrax::HyraxGenerators;
+use folding_schemes::folding::nova::nifs::mova_matrix::{RelaxedCommittedRelation, Witness, NIFS};
+use folding_schemes::transcript::poseidon::poseidon_canonical_config;
+use folding_schemes::Curve;
+use matrex::Matrix;
+use rand::{Rng, RngCore};
+use std::time::{Duration, Instant};
+
+const NUM_OF_PRECONDITION_FOLDS: &[usize] = &[1];
+
+fn random_sparse_matrix<C: Curve>(n: usize, rng: &mut impl RngCore) -> Matrix<C::ScalarField> {
+    let elements = (0..n)
+        .map(|row| {
+            (
+                row * n + rand::thread_rng().gen_range(0..n),
+                C::ScalarField::rand(rng),
+            )
+        })
+        .collect();
+    Matrix::sparse_from_vec(elements, n, n).unwrap()
+}
+
+// Helper functions
+fn get_instances<C: Curve>(
+    num: usize,
+    n: usize,
+    rng: &mut impl RngCore,
+    params: &HyraxGenerators<C>,
+) -> Vec<(Witness<C>, RelaxedCommittedRelation<C>)> {
+    let start = Instant::now();
+    (0..num)
+        .map(|_| -> (Witness<C>, RelaxedCommittedRelation<C>) {
+            let before_gen_matrices = start.elapsed();
+            println!("before_gen_matrices 2 {:?}", before_gen_matrices);
+            // A matrix
+            let a = random_sparse_matrix::<C>(n, rng);
+            // B matrix
+            let b = random_sparse_matrix::<C>(n, rng);
+            // C = A * B matrix
+            let c = (&a * &b).unwrap();
+            // Error matrix initialized to 0s
+            let e = Matrix::zero(n, n);
+            let after_gen_matrices = start.elapsed();
+            println!("after_gen_matrices 2 {:?}", after_gen_matrices);
+
+            // Random challenge
+            let rE = (0..2 * log2(n))
+                .map(|_| C::ScalarField::rand(rng))
+                .collect();
+            // Witness
+            let witness = Witness::new::<false>(a, b, c, e);
+            let before_commit_wit = start.elapsed();
+            println!("before_commit_wit 2 {:?}", before_commit_wit);
+            let instance = witness.commit(params, rE).unwrap();
+            let after_commit_wit = start.elapsed();
+            println!("after_commit_wit 2 {:?}", after_commit_wit);
+            (witness, instance)
+        })
+        .collect()
+}
+
+fn bench_mova_matrix() {
+    let mut rng = ark_std::test_rng();
+    let mat_dim = 1 << 2; // 4x4 matrices
+    println!("mat_dim {}", mat_dim);
+    for count in NUM_OF_PRECONDITION_FOLDS {
+        println!("Starting with pedersen setup");
+
+        let start = Instant::now();
+        let hyrax_params =
+            HyraxGenerators::<Projective>::setup(&mut rng, log2(mat_dim * mat_dim) as usize);
+        let hyrax_elapsed = start.elapsed();
+        println!("hyrax_elapsed 1 {:?}", hyrax_elapsed);
+
+        let poseidon_config = poseidon_canonical_config::<Fr>();
+        let pp_hash = Fr::rand(&mut rng);
+
+        let mut total_duration = Duration::ZERO;
+        println!("Starting with gen instances");
+
+        let before_instances = start.elapsed();
+        println!("before_instances 1 {:?}", before_instances);
+        let mut instances: Vec<(Witness<Projective>, RelaxedCommittedRelation<Projective>)> =
+            get_instances::<Projective>(
+                count + 1, // we want the number of folds plus one for the acc_instance
+                mat_dim,
+                &mut rng,
+                &hyrax_params,
+            );
+        let after_instances = start.elapsed();
+        println!("after_instances 1 {:?}", after_instances);
+
+        let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
+        let mut acc = instances.pop().unwrap();
+
+        for _ in 0..*count {
+            let mut next = instances.pop().unwrap();
+            total_duration += {
+                let timer = Instant::now();
+                println!("Starting with prove");
+
+                let before_proof = start.elapsed();
+                println!("before_proof 1 {:?}", before_proof);
+                let (wit_acc, inst_acc, _) = NIFS::<Projective, PoseidonSponge<Fr>>::prove(
+                    &mut transcript_p,
+                    pp_hash,
+                    &mut next.0,
+                    &next.1,
+                    &acc.0,
+                    &acc.1,
+                )
+                .unwrap();
+                let after_proof = start.elapsed();
+                println!("after_proof 1 {:?}", after_proof);
+                let time = timer.elapsed();
+                acc = (wit_acc, inst_acc);
+                time
+            };
+        }
+        println!("Ending");
+    }
+}
+
+fn main() {
+    bench_mova_matrix();
+}
diff --git a/folding-schemes/Cargo.toml b/folding-schemes/Cargo.toml
index d5827c11f..248174918 100644
--- a/folding-schemes/Cargo.toml
+++ b/folding-schemes/Cargo.toml
@@ -23,7 +23,7 @@ num-bigint = "0.4"
 num-integer = "0.1"
 sha3 = "0.10"
 log = "0.4"
-matrex = {git = "ssh://git@github.com/NethermindEth/matrex.git", tag = "v0.1.3", features = ["parallel"]}
+matrex = {git = "ssh://git@github.com/NethermindEth/matrex.git", features = ["parallel"]}
 
 
 [dev-dependencies]
@@ -75,6 +75,11 @@ name = "mova_matrix"
 path = "../benches/mova_matrix.rs"
 harness = false
 
+[[bench]]
+name = "commitment_schemes"
+path = "../benches/commitment_schemes.rs"
+harness = false
+
 [[example]]
 name = "sha256"
 path = "../examples/sha256.rs"
@@ -86,3 +91,9 @@ path = "../examples/multi_inputs.rs"
 [[example]]
 name = "external_inputs"
 path = "../examples/external_inputs.rs"
+
+
+[[example]]
+name = "mova_matrix"
+path = "../examples/mova_matrix_memory.rs"
+
diff --git a/folding-schemes/src/commitment/hyrax.rs b/folding-schemes/src/commitment/hyrax.rs
new file mode 100644
index 000000000..12314dee9
--- /dev/null
+++ b/folding-schemes/src/commitment/hyrax.rs
@@ -0,0 +1,244 @@
+use ark_ff::Zero;
+use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
+use ark_std::iterable::Iterable;
+use ark_std::rand::RngCore;
+use ark_std::vec::Vec;
+use rayon::prelude::*;
+use std::fmt::Debug;
+use std::marker::PhantomData;
+
+use crate::commitment::pedersen::{Params, Pedersen};
+use crate::commitment::{CommitmentScheme, NethermindCommitmentScheme};
+use crate::{Curve, Error};
+
+/// Taken from jolt but we assume ratio is 1 since we are dealing with square matrices
+fn matrix_dimensions(num_elems: usize) -> (usize, usize) {
+    let left_num_vars = num_elems / 2;
+    let right_num_vars = num_elems - left_num_vars;
+
+    let col_size = 2_usize.pow(left_num_vars as u32);
+    let row_size = 2_usize.pow(right_num_vars as u32);
+
+    (col_size, row_size)
+}
+
+#[derive(Clone, CanonicalSerialize, CanonicalDeserialize, Debug)]
+pub struct HyraxGenerators<C: Curve> {
+    pub pedersen_generators: Params<C>,
+}
+
+#[derive(Clone, CanonicalSerialize, CanonicalDeserialize, Debug, PartialEq, Eq)]
+pub struct Hyrax<C: Curve> {
+    _c: PhantomData<C>,
+}
+
+impl<C: Curve> HyraxGenerators<C> {
+    pub fn setup(rng: impl RngCore, len: usize) -> Self {
+        let (_col_len, row_len) = matrix_dimensions(len);
+        let gens = Pedersen::<C, false>::setup_prover(rng, row_len).unwrap();
+        HyraxGenerators {
+            pedersen_generators: gens,
+        }
+    }
+}
+
+impl<C: Curve> Hyrax<C> {
+    pub fn commit(elems: &[C::ScalarField], gens: &HyraxGenerators<C>) -> Result<Vec<C>, Error> {
+        let n = elems.len();
+        let ell = {
+            if n.is_power_of_two() {
+                (1usize.leading_zeros() - n.leading_zeros()) as usize
+            } else {
+                (0usize.leading_zeros() - n.leading_zeros()) as usize
+            }
+        };
+
+        let (L_size, R_size) = matrix_dimensions(ell);
+        assert_eq!(L_size * R_size, n);
+
+        let row_commitments: Vec<C> = elems
+            .par_chunks(R_size)
+            .map(|row| {
+                Pedersen::<C, false>::commit(
+                    &gens.pedersen_generators,
+                    row,
+                    &C::ScalarField::zero(),
+                )
+                .unwrap()
+            })
+            .collect();
+        Ok(row_commitments)
+    }
+
+    pub fn batch_commit(
+        batch: &[&[C::ScalarField]],
+        gens: &HyraxGenerators<C>,
+    ) -> Result<Vec<Vec<C>>, Error> {
+        let n = batch[0].len();
+        batch.iter().for_each(|poly| assert_eq!(poly.len(), n));
+        let ell = {
+            if n.is_power_of_two() {
+                (1usize.leading_zeros() - n.leading_zeros()) as usize
+            } else {
+                (0usize.leading_zeros() - n.leading_zeros()) as usize
+            }
+        };
+        let (L_size, R_size) = matrix_dimensions(ell);
+        assert_eq!(L_size * R_size, n);
+
+        let rows = batch.par_iter().flat_map(|poly| poly.par_chunks(R_size));
+        let row_commitments: Vec<C> = rows
+            .map(|row| {
+                Pedersen::<C, false>::commit(
+                    &gens.pedersen_generators,
+                    row,
+                    &C::ScalarField::zero(),
+                )
+                .unwrap()
+            })
+            .collect();
+
+        Ok(row_commitments
+            .par_chunks(L_size)
+            .map(|chunk| chunk.to_vec())
+            .collect())
+    }
+
+    pub fn commit_sparse_matrix(
+        indices_values: &[(usize, C::ScalarField)],
+        gens: &HyraxGenerators<C>,
+    ) -> Result<Vec<C>, Error> {
+        // deduce the highest index to figure out n
+        let max_index = indices_values.iter().map(|(pos, _)| *pos).max().unwrap();
+        let n = max_index + 1; // since indices are 0-based
+
+        let ell = if n.is_power_of_two() {
+            (1usize.leading_zeros() - n.leading_zeros()) as usize
+        } else {
+            (0usize.leading_zeros() - n.leading_zeros()) as usize
+        };
+        let (L_size, R_size) = matrix_dimensions(ell);
+
+        // For each row i in [0..L_size], gather all (pos, val) where row_start <= pos < row_end
+        // and do a "sparse" Pedersen commit using the row‐local indices (pos - row_start).
+        let row_commitments: Vec<C> = (0..L_size)
+            .into_par_iter()
+            .map(|row_idx| {
+                let row_start = row_idx * R_size;
+                let row_end = row_start + R_size;
+
+                // gather the pairs that fall in row i
+                let row_sparse: Vec<(usize, C::ScalarField)> = indices_values
+                    .iter()
+                    .filter_map(|(pos, val)| {
+                        if *pos >= row_start && *pos < row_end {
+                            // shift the index for the row commit
+                            Some((pos - row_start, *val))
+                        } else {
+                            None
+                        }
+                    })
+                    .collect();
+
+                Pedersen::<C, false>::commit_sparse(
+                    &gens.pedersen_generators,
+                    &row_sparse,
+                    &C::ScalarField::zero(),
+                )
+            })
+            .collect::<Result<Vec<C>, _>>()?;
+
+        Ok(row_commitments)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use ark_pallas::{Fr, Projective};
+    use ark_std::{test_rng, UniformRand};
+
+    #[test]
+    fn test_matrix_dimensions() {
+        assert_eq!(matrix_dimensions(0), (1, 1));
+        assert_eq!(matrix_dimensions(1), (1, 2));
+        assert_eq!(matrix_dimensions(2), (2, 2));
+        assert_eq!(matrix_dimensions(4), (4, 4));
+        assert_eq!(matrix_dimensions(6), (8, 8));
+
+        let num_vars = 30; // Very large number of variables
+        let (cols, rows) = matrix_dimensions(num_vars);
+
+        let expected_left = num_vars / 2;
+        let expected_right = num_vars - expected_left;
+
+        assert_eq!(cols, 1 << expected_left);
+        assert_eq!(rows, 1 << expected_right);
+    }
+
+    #[test]
+    fn test_setup() {
+        let mut rng = test_rng();
+        let gens = HyraxGenerators::<Projective>::setup(&mut rng, 4);
+        assert!(!gens.pedersen_generators.generators.is_empty());
+    }
+
+    #[test]
+    fn test_commit_success() {
+        let mut rng = test_rng();
+        let len = 4; // Must be a power of two number of bits
+        let elems: Vec<Fr> = (0..(1 << len)).map(|_| Fr::rand(&mut rng)).collect();
+
+        let gens = HyraxGenerators::<Projective>::setup(&mut rng, len);
+        let commitment = Hyrax::<Projective>::commit(&elems, &gens);
+        assert!(commitment.is_ok());
+
+        let hyrax = commitment.unwrap();
+        let (l_size, _) = matrix_dimensions(len);
+        assert_eq!(hyrax.len(), l_size);
+    }
+
+    #[test]
+    fn test_same_commit() {
+        let mut rng = test_rng();
+        let len = 4; // Must be a power of two number of bits
+        let elems: Vec<Fr> = (0..(1 << len)).map(|_| Fr::rand(&mut rng)).collect();
+
+        let gens = HyraxGenerators::<Projective>::setup(&mut rng, len);
+        let commitment = Hyrax::<Projective>::commit(&elems, &gens);
+        assert!(commitment.is_ok());
+
+        let hyrax = commitment.unwrap();
+
+        let commitment2 = Hyrax::<Projective>::commit(&elems, &gens);
+        assert!(commitment2.is_ok());
+
+        let hyrax2 = commitment2.unwrap();
+        assert_eq!(hyrax, hyrax2);
+    }
+
+    #[test]
+    fn test_batch_commit() {
+        let mut rng = test_rng();
+        let len = 3;
+        let poly_len = 1 << len;
+        let batch_size = 5;
+
+        let batch: Vec<Vec<Fr>> = (0..batch_size)
+            .map(|_| (0..poly_len).map(|_| Fr::rand(&mut rng)).collect())
+            .collect();
+
+        let batch_refs: Vec<&[Fr]> = batch.iter().map(|v| v.as_slice()).collect();
+
+        let gens = HyraxGenerators::<Projective>::setup(&mut rng, len);
+        let result = Hyrax::<Projective>::batch_commit(&batch_refs, &gens);
+        assert!(result.is_ok());
+
+        let commitments = result.unwrap();
+        assert_eq!(commitments.len(), batch_size);
+        let (l_size, _) = matrix_dimensions(len);
+        for c in commitments {
+            assert_eq!(c.len(), l_size);
+        }
+    }
+}
diff --git a/folding-schemes/src/commitment/mod.rs b/folding-schemes/src/commitment/mod.rs
index 0c9301d69..5b31ee003 100644
--- a/folding-schemes/src/commitment/mod.rs
+++ b/folding-schemes/src/commitment/mod.rs
@@ -5,6 +5,7 @@ use ark_std::rand::RngCore;
 use crate::transcript::Transcript;
 use crate::{Curve, Error};
 
+pub mod hyrax;
 pub mod ipa;
 pub mod kzg;
 pub mod pedersen;
@@ -67,6 +68,16 @@ pub trait CommitmentScheme<C: Curve, const H: bool = false>: Clone + Debug {
     ) -> Result<(), Error>;
 }
 
+pub trait NethermindCommitmentScheme<C: Curve, const H: bool = false>:
+    CommitmentScheme<C, H>
+{
+    fn commit_sparse(
+        params: &Self::ProverParams,
+        v: &[(usize, C::ScalarField)],
+        r: &C::ScalarField,
+    ) -> Result<C, Error>;
+    fn setup_prover(rng: impl RngCore, len: usize) -> Result<Self::ProverParams, Error>;
+}
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/folding-schemes/src/commitment/pedersen.rs b/folding-schemes/src/commitment/pedersen.rs
index a9e8d850a..44103e107 100644
--- a/folding-schemes/src/commitment/pedersen.rs
+++ b/folding-schemes/src/commitment/pedersen.rs
@@ -3,7 +3,7 @@ use ark_relations::r1cs::SynthesisError;
 use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
 use ark_std::{marker::PhantomData, rand::RngCore, UniformRand, Zero};
 
-use super::CommitmentScheme;
+use super::{CommitmentScheme, NethermindCommitmentScheme};
 use crate::folding::circuits::CF2;
 use crate::transcript::Transcript;
 use crate::utils::vec::{vec_add, vec_scalar_mul};
@@ -61,6 +61,9 @@ impl<C: Curve, const H: bool> CommitmentScheme<C, H> for Pedersen<C, H> {
         v: &[C::ScalarField],
         r: &C::ScalarField, // blinding factor
     ) -> Result<C, Error> {
+        if v.is_empty() {
+            return Ok(C::zero());
+        }
         if params.generators.len() < v.len() {
             return Err(Error::PedersenParamsLen(params.generators.len(), v.len()));
         }
@@ -178,6 +181,50 @@ pub struct PedersenGadget<C: Curve, const H: bool = false> {
     _c: PhantomData<C>,
 }
 
+impl<C: Curve, const H: bool> NethermindCommitmentScheme<C, H> for Pedersen<C, H> {
+    fn commit_sparse(
+        params: &Self::ProverParams,
+        v: &[(usize, C::ScalarField)],
+        r: &C::ScalarField,
+    ) -> Result<C, Error> {
+        if v.is_empty() {
+            return Ok(C::zero());
+        };
+        let (selected_generators, values): (Vec<_>, Vec<_>) = v
+            .iter()
+            .map(|(i, val)| (params.generators[*i], *val))
+            .unzip();
+
+        let msm_result = C::msm_unchecked(&selected_generators, &values);
+
+        if !H {
+            if !r.is_zero() {
+                return Err(Error::BlindingNotZero);
+            }
+            Ok(msm_result)
+        } else {
+            Ok(params.h.mul(r) + msm_result)
+        }
+    }
+
+    fn setup_prover(mut rng: impl RngCore, len: usize) -> Result<Self::ProverParams, Error> {
+        // Pre-calculate the size and pre-allocate the vector capacity
+        let size = len.next_power_of_two();
+
+        let mut generators = Vec::with_capacity(size);
+
+        for _ in 0..size {
+            generators.push(C::Affine::rand(&mut rng));
+        }
+
+        let p = Params::<C> {
+            h: C::rand(&mut rng),
+            generators,
+        };
+
+        Ok(p)
+    }
+}
 impl<C: Curve, const H: bool> PedersenGadget<C, H> {
     pub fn commit(
         h: &C::Var,
diff --git a/folding-schemes/src/folding/nova/nifs/mova_matrix.rs b/folding-schemes/src/folding/nova/nifs/mova_matrix.rs
index 5b26ad315..71c23a38d 100644
--- a/folding-schemes/src/folding/nova/nifs/mova_matrix.rs
+++ b/folding-schemes/src/folding/nova/nifs/mova_matrix.rs
@@ -1,8 +1,8 @@
 #![allow(dead_code)]
+use crate::commitment::hyrax::{Hyrax, HyraxGenerators};
 /// Mova-like folding for matrix multiplications as described in "Folding and Lookup Arguments for Proving Inference of Deep Learning Models" by Nethermind Research
 /// Currently, we are not interested in the hiding properties, so we ignore the hiding factors and focus on the succinctness property.
 /// Please note the code could be easily extended so support hiding.
-use crate::commitment::CommitmentScheme;
 use crate::folding::nova::nifs::pointvsline::{
     PointVsLine, PointVsLineEvaluationClaimMatrix, PointVsLineMatrix, PointVsLineProofMatrix,
 };
@@ -23,9 +23,9 @@ use num_integer::Roots;
 /// When u=1 and E is the zero matrix, we have the simple committed relation in which A * B = C.
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct RelaxedCommittedRelation<C: Curve> {
-    pub cmA: C,                  // Commitment to matrix A. cmA = commitment(A).
-    pub cmB: C,                  // Commitment to matrix B. cmB = commitment(B).
-    pub cmC: C,                  // Commitment to matrix C. cmC = commitment(C).
+    pub cmA: Vec<C>,             // Commitment to matrix A. cmA = commitment(A).
+    pub cmB: Vec<C>,             // Commitment to matrix B. cmB = commitment(B).
+    pub cmC: Vec<C>,             // Commitment to matrix C. cmC = commitment(C).
     pub u: C::ScalarField,       // Scalar used in the folding.
     pub mleE: C::ScalarField, // v = mle[E](rE) in MOVA notation. Multilinear extension of matrix E evaluated at random point rE.
     pub rE: Vec<C::ScalarField>, // Random point where MLE is evaluated. Size of 2 * log2(n).
@@ -37,9 +37,11 @@ impl<C: Curve> Absorb for RelaxedCommittedRelation<C> {
     }
 
     fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
-        self.cmA.to_native_sponge_field_elements(dest);
-        self.cmB.to_native_sponge_field_elements(dest);
-        self.cmC.to_native_sponge_field_elements(dest);
+        for i in 0..self.cmA.len() {
+            self.cmA[i].to_native_sponge_field_elements(dest);
+            self.cmB[i].to_native_sponge_field_elements(dest);
+            self.cmC[i].to_native_sponge_field_elements(dest);
+        }
         self.u.to_sponge_field_elements(dest);
         self.mleE.to_sponge_field_elements(dest);
         self.rE.to_sponge_field_elements(dest);
@@ -87,9 +89,9 @@ impl<C: Curve> Witness<C> {
     /// * `self` - Witness instance to be committed.
     /// * `params` - Commitment scheme parameters.
     /// * `rE` - Random evaluation point for the committed instance.
-    pub fn commit<CS: CommitmentScheme<C, H>, const H: bool>(
+    pub fn commit(
         &self,
-        params: &CS::ProverParams,
+        params: &HyraxGenerators<C>,
         rE: Vec<C::ScalarField>,
     ) -> Result<RelaxedCommittedRelation<C>, Error> {
         let mut mleE = C::ScalarField::zero();
@@ -97,29 +99,35 @@ impl<C: Curve> Witness<C> {
             let mle = MultilinearExtension::from_evaluations(&self.E, log2(self.E.len()) as usize);
             mleE = mle.evaluate(&rE);
         }
+        // There are three versions of committing for hyrax.
+        // 1. Simple Dense Commit 2. Sparse Commit 3. Batch dense commit
+        // Simple Dense Commit
+        // let com_a = Hyrax::commit(self.A.as_dense_slice().unwrap(), params)?;
+        // let com_b = Hyrax::commit(self.B.as_dense_slice().unwrap(), params)?;
+        // let com_c = Hyrax::commit(self.C.as_dense_slice().unwrap(), params)?;
+
+        // Sparse commit
+        let com_a = Hyrax::commit_sparse_matrix(self.A.as_sparse_slice().unwrap(), params)?;
+        let com_b = Hyrax::commit_sparse_matrix(self.B.as_sparse_slice().unwrap(), params)?;
+        let com_c = if self.C.is_dense() {
+            Hyrax::commit(self.C.as_dense_slice().unwrap(), params)?
+        } else {
+            Hyrax::commit_sparse_matrix(self.C.as_sparse_slice().unwrap(), params)?
+        };
 
-        let mut dense_a = self.A.clone();
-        let mut dense_b = self.B.clone();
-        let mut dense_c = self.C.clone();
-        dense_a.to_dense();
-        dense_b.to_dense();
-        dense_c.to_dense();
-        // Right now we are ignoring the hiding property and directly commit to the matrices
-        let com_a = CS::commit(
-            params,
-            dense_a.as_dense_slice().unwrap(),
-            &C::ScalarField::zero(),
-        )?;
-        let com_b = CS::commit(
-            params,
-            dense_b.as_dense_slice().unwrap(),
-            &C::ScalarField::zero(),
-        )?;
-        let com_c = CS::commit(
-            params,
-            dense_c.as_dense_slice().unwrap(),
-            &C::ScalarField::zero(),
-        )?;
+        // Batch dense commit
+        // let a_slice = self.A.as_dense_slice().unwrap();
+        // let b_slice = self.B.as_dense_slice().unwrap();
+        // let c_slice = self.C.as_dense_slice().unwrap();
+        //
+        // let slices = [a_slice, b_slice, c_slice];
+        //
+        // let mut all_commitments = Hyrax::batch_commit(&slices, params)?;
+        //
+        // let mut commitments_iter = all_commitments.into_iter();
+        // let com_a = commitments_iter.next().expect("Expected commitments for A");
+        // let com_b = commitments_iter.next().expect("Expected commitments for B");
+        // let com_c = commitments_iter.next().expect("Expected commitments for C");
 
         Ok(RelaxedCommittedRelation {
             cmA: com_a,
@@ -148,20 +156,12 @@ pub struct Proof<C: Curve> {
 /// Implements the Non-Interactive Folding Scheme described in section 3.2 of the previous referenced article.
 /// `H` specifies whether the NIFS will use a blinding factor
 #[allow(clippy::upper_case_acronyms)]
-pub struct NIFS<
-    C: Curve,
-    CS: CommitmentScheme<C, H>,
-    T: Transcript<C::ScalarField>,
-    const H: bool = false,
-> {
+pub struct NIFS<C: Curve, T: Transcript<C::ScalarField>, const H: bool = false> {
     _c: PhantomData<C>,
-    _cp: PhantomData<CS>,
     _ct: PhantomData<T>,
 }
 
-impl<C: Curve, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const H: bool>
-    NIFS<C, CS, T, H>
-{
+impl<C: Curve, T: Transcript<C::ScalarField>, const H: bool> NIFS<C, T, H> {
     fn new_witness(
         a: Matrix<C::ScalarField>,
         b: Matrix<C::ScalarField>,
@@ -173,7 +173,7 @@ impl<C: Curve, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const
 
     fn new_instance(
         mut rng: impl RngCore,
-        params: &CS::ProverParams,
+        params: &HyraxGenerators<C>,
         witness: Witness<C>,
         aux: Vec<C::ScalarField>, // rE in MOVA notation.
     ) -> Result<RelaxedCommittedRelation<C>, Error> {
@@ -184,20 +184,25 @@ impl<C: Curve, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const
                 .map(|_| C::ScalarField::rand(&mut rng))
                 .collect();
         }
-        witness.commit::<CS, H>(params, rE)
+        witness.commit(params, rE)
     }
 
     // Protocol 5 - point 8 (Page 25)
     fn fold_witness(
-        alpha: C::ScalarField,       // Random challenge
-        simple_wit: &Witness<C>,     // Simple witness
-        acc_wit: &Witness<C>,        // Accumulated witness
-        aux: Matrix<C::ScalarField>, // T in Mova's notation
+        alpha: C::ScalarField,           // Random challenge
+        simple_wit: &mut Witness<C>,     // Simple witness
+        acc_wit: &Witness<C>,            // Accumulated witness
+        mut aux: Matrix<C::ScalarField>, // T in Mova's notation
     ) -> Result<Witness<C>, Error> {
-        let a_acc = ((simple_wit.A.clone() * alpha) + &acc_wit.A).unwrap();
-        let b_acc = ((simple_wit.B.clone() * alpha) + &acc_wit.B).unwrap();
-        let c_acc = ((simple_wit.C.clone() * alpha) + &acc_wit.C).unwrap();
-        let e_acc = ((aux * alpha) + acc_wit.E.clone()).unwrap();
+        simple_wit.A *= alpha;
+        simple_wit.B *= alpha;
+        simple_wit.C *= alpha;
+        aux *= alpha;
+
+        let a_acc = (&simple_wit.A + &acc_wit.A).unwrap();
+        let b_acc = (&simple_wit.B + &acc_wit.B).unwrap();
+        let c_acc = (&simple_wit.C + &acc_wit.C).unwrap();
+        let e_acc = (aux + &acc_wit.E).unwrap();
 
         Ok(Witness::<C> {
             A: a_acc,
@@ -214,7 +219,7 @@ impl<C: Curve, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const
     pub fn prove(
         transcript: &mut T,
         pp_hash: C::ScalarField,
-        simple_witness: &Witness<C>,
+        simple_witness: &mut Witness<C>,
         simple_instance: &RelaxedCommittedRelation<C>,
         acc_witness: &Witness<C>,
         acc_instance: &RelaxedCommittedRelation<C>,
@@ -255,12 +260,12 @@ impl<C: Curve, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const
         transcript.absorb(&mleE2_prime);
 
         // Compute cross term T
-        let A1B2 = (simple_witness.A.clone() * &acc_witness.B).unwrap();
+        let mut A1B2 = (&simple_witness.A * &acc_witness.B).unwrap(); // Representing A1B2A2B1 later
 
         let B1A2 = (&acc_witness.A * &simple_witness.B).unwrap();
-        let A1B2B1A2 = (A1B2 + B1A2).unwrap();
+        A1B2 += B1A2;
         let u2c1 = simple_witness.C.clone() * acc_instance.u;
-        let T: Matrix<C::ScalarField> = ((A1B2B1A2 - &acc_witness.C).unwrap() - u2c1).unwrap();
+        let T: Matrix<C::ScalarField> = ((A1B2 - &acc_witness.C).unwrap() - u2c1).unwrap();
 
         // Compute MLE_T
         let n_vars: usize = log2(simple_witness.E.len()) as usize;
@@ -338,9 +343,7 @@ impl<C: Curve, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const
     }
 }
 
-impl<C: Curve, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const H: bool>
-    NIFS<C, CS, T, H>
-{
+impl<C: Curve, T: Transcript<C::ScalarField>, const H: bool> NIFS<C, T, H> {
     /// Folds two committed instances into a single one using the provided parameters
     /// Protocol 5 - Step 7 (page 25) for folding instances
     /// # Parameters
@@ -360,9 +363,30 @@ impl<C: Curve, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const
     ) -> Result<RelaxedCommittedRelation<C>, Error> {
         // Step 7
         // Accumulate commitments
-        let com_a_acc = simple_instance.cmA.mul(alpha) + acc_instance.cmA;
-        let com_b_acc = simple_instance.cmB.mul(alpha) + acc_instance.cmB;
-        let com_c_acc = simple_instance.cmC.mul(alpha) + acc_instance.cmC;
+        // let com_a_acc = simple_instance.cmA.mul(alpha) + acc_instance.cmA;
+        // let com_b_acc = simple_instance.cmB.mul(alpha) + acc_instance.cmB;
+        // let com_c_acc = simple_instance.cmC.mul(alpha) + acc_instance.cmC;
+
+        let com_a_acc: Vec<_> = simple_instance
+            .cmA
+            .iter()
+            .zip(&acc_instance.cmA)
+            .map(|(a, b)| a.mul(alpha).add(b))
+            .collect();
+
+        let com_b_acc: Vec<_> = simple_instance
+            .cmB
+            .iter()
+            .zip(&acc_instance.cmB)
+            .map(|(a, b)| a.mul(alpha).add(b))
+            .collect();
+
+        let com_c_acc: Vec<_> = simple_instance
+            .cmC
+            .iter()
+            .zip(&acc_instance.cmC)
+            .map(|(a, b)| a.mul(alpha).add(b))
+            .collect();
 
         // Update scalars
         let u_acc = alpha + acc_instance.u;
@@ -386,7 +410,7 @@ impl<C: Curve, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const
     fn check_relation(
         witness: &Witness<C>,
         instance: &RelaxedCommittedRelation<C>,
-        params: &CS::ProverParams,
+        params: &HyraxGenerators<C>,
     ) -> Result<(), Error> {
         let mut dense_a = witness.A.clone();
         let mut dense_b = witness.B.clone();
@@ -394,21 +418,9 @@ impl<C: Curve, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const
         dense_a.to_dense();
         dense_b.to_dense();
         dense_c.to_dense();
-        let com_a = CS::commit(
-            params,
-            dense_a.as_dense_slice().unwrap(),
-            &C::ScalarField::zero(),
-        )?;
-        let com_b = CS::commit(
-            params,
-            dense_b.as_dense_slice().unwrap(),
-            &C::ScalarField::zero(),
-        )?;
-        let com_c = CS::commit(
-            params,
-            dense_c.as_dense_slice().unwrap(),
-            &C::ScalarField::zero(),
-        )?;
+        let com_a = Hyrax::commit(dense_a.as_dense_slice().unwrap(), params)?;
+        let com_b = Hyrax::commit(dense_b.as_dense_slice().unwrap(), params)?;
+        let com_c = Hyrax::commit(dense_c.as_dense_slice().unwrap(), params)?;
 
         let AB = (&witness.A * &witness.B).unwrap();
 
@@ -442,7 +454,6 @@ impl<C: Curve, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const
 #[cfg(test)]
 pub mod tests {
     use super::*;
-    use crate::commitment::pedersen::Pedersen;
     use crate::transcript::poseidon::poseidon_canonical_config;
     use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge};
     use ark_pallas::{Fr, Projective};
@@ -462,18 +473,20 @@ pub mod tests {
     }
 
     // Helper functions
-    fn get_instances<C: Curve, CS: CommitmentScheme<C>>(
+    fn get_instances<C: Curve>(
         num: usize,
         n: usize,
         rng: &mut impl RngCore,
-        params: &CS::ProverParams,
+        params: &HyraxGenerators<C>,
     ) -> Vec<(Witness<C>, RelaxedCommittedRelation<C>)> {
         (0..num)
             .map(|_| -> (Witness<C>, RelaxedCommittedRelation<C>) {
                 // A matrix
                 let a = random_sparse_matrix::<C>(n, rng);
+                // a.to_dense();
                 // B matrix
                 let b = random_sparse_matrix::<C>(n, rng);
+                // b.to_dense();
                 // C = A * B matrix
                 let c = (&a * &b).unwrap();
                 // Error matrix initialized to 0s
@@ -485,7 +498,7 @@ pub mod tests {
                     .collect();
                 // Witness
                 let witness = Witness::new::<false>(a, b, c, e);
-                let instance = witness.commit::<CS, false>(params, rE).unwrap();
+                let instance = witness.commit(params, rE).unwrap();
                 (witness, instance)
             })
             .collect()
@@ -498,54 +511,45 @@ pub mod tests {
         let mat_dim = 4; // 4x4 matrices
 
         // Set up transcript and commitment scheme
-        let (pedersen_params, _) =
-            Pedersen::<Projective>::setup(&mut rng, mat_dim * mat_dim).unwrap();
+        let hyrax_params =
+            HyraxGenerators::<Projective>::setup(&mut rng, log2(mat_dim * mat_dim) as usize);
         let poseidon_config = poseidon_canonical_config::<Fr>();
         let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
         let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
         let pp_hash = Fr::rand(&mut rng);
 
-        let instances: Vec<(Witness<Projective>, RelaxedCommittedRelation<Projective>)> =
-            get_instances::<Projective, Pedersen<Projective>>(
-                n_instances,
-                mat_dim,
-                &mut rng,
-                &pedersen_params,
-            );
-
-        for i in 0..instances.len() - 1 {
-            // Fold
-            let (wit_acc, instance_acc, proof) =
-                NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::prove(
-                    &mut transcript_p,
-                    pp_hash,
-                    &instances[i].0,
-                    &instances[i].1,
-                    &instances[i + 1].0,
-                    &instances[i + 1].1,
-                )
-                .unwrap();
-
-            // Verify
-            let (ci_verify, _) =
-                NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::verify(
-                    &mut transcript_v,
-                    pp_hash,
-                    &instances[i].1,
-                    &instances[i + 1].1,
-                    &proof,
-                )
-                .unwrap();
-
-            // Ensure they match
-            assert_eq!(instance_acc, ci_verify);
-            NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::check_relation(
-                &wit_acc,
-                &instance_acc,
-                &pedersen_params,
-            )
+        let mut instances: Vec<(Witness<Projective>, RelaxedCommittedRelation<Projective>)> =
+            get_instances::<Projective>(n_instances, mat_dim, &mut rng, &hyrax_params);
+
+        let (left, right) = instances.split_at_mut(1);
+        let (ref mut simple_w, ref simple_i) = &mut left[0];
+        let (ref acc_w, ref acc_i) = &mut right[0];
+
+        let (wit_acc, inst_acc, proof) = NIFS::<Projective, PoseidonSponge<Fr>>::prove(
+            &mut transcript_p,
+            pp_hash,
+            simple_w,
+            &simple_i,
+            &acc_w,
+            &acc_i,
+        )
+        .unwrap();
+
+        // Verify
+        let (ci_verify, _) = NIFS::<Projective, PoseidonSponge<Fr>>::verify(
+            &mut transcript_v,
+            pp_hash,
+            &simple_i,
+            &acc_i,
+            &proof,
+        )
+        .unwrap();
+
+        // Ensure they match
+        assert_eq!(inst_acc, ci_verify);
+
+        NIFS::<Projective, PoseidonSponge<Fr>>::check_relation(&wit_acc, &inst_acc, &hyrax_params)
             .expect("Relationship check failed");
-        }
     }
 
     #[test]
@@ -557,20 +561,15 @@ pub mod tests {
         let mat_dim = 16; // 16x16 matrices
 
         // Set up transcript and commitment scheme
-        let (pedersen_params, _) =
-            Pedersen::<Projective>::setup(&mut rng, mat_dim * mat_dim).unwrap();
+        let hyrax_params =
+            HyraxGenerators::<Projective>::setup(&mut rng, log2(mat_dim * mat_dim) as usize);
         let poseidon_config = poseidon_canonical_config::<Fr>();
         let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
         let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
         let pp_hash = Fr::rand(&mut rng);
 
         let mut instances: Vec<(Witness<Projective>, RelaxedCommittedRelation<Projective>)> =
-            get_instances::<Projective, Pedersen<Projective>>(
-                n_instances,
-                mat_dim,
-                &mut rng,
-                &pedersen_params,
-            );
+            get_instances::<Projective>(n_instances, mat_dim, &mut rng, &hyrax_params);
 
         // Keep track of the accumulated state
         let first_instance = instances.remove(0);
@@ -578,38 +577,36 @@ pub mod tests {
         let mut current_acc_inst = first_instance.1;
 
         // Fold through all remaining instances
-        for (next_w, next_i) in instances {
+        for (mut next_w, next_i) in instances {
             // Fold
-            let (wit_acc, inst_acc, proof) =
-                NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::prove(
-                    &mut transcript_p,
-                    pp_hash,
-                    &next_w,
-                    &next_i,
-                    &current_acc_wit,
-                    &current_acc_inst,
-                )
-                .unwrap();
+            let (wit_acc, inst_acc, proof) = NIFS::<Projective, PoseidonSponge<Fr>>::prove(
+                &mut transcript_p,
+                pp_hash,
+                &mut next_w,
+                &next_i,
+                &current_acc_wit,
+                &current_acc_inst,
+            )
+            .unwrap();
 
             // Verify
-            let (ci_verify, _) =
-                NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::verify(
-                    &mut transcript_v,
-                    pp_hash,
-                    &next_i,
-                    &current_acc_inst,
-                    &proof,
-                )
-                .unwrap();
+            let (ci_verify, _) = NIFS::<Projective, PoseidonSponge<Fr>>::verify(
+                &mut transcript_v,
+                pp_hash,
+                &next_i,
+                &current_acc_inst,
+                &proof,
+            )
+            .unwrap();
 
             // Ensure they match
             assert_eq!(inst_acc, ci_verify);
 
             // Update state for next iteration
-            NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::check_relation(
+            NIFS::<Projective, PoseidonSponge<Fr>>::check_relation(
                 &wit_acc,
                 &inst_acc,
-                &pedersen_params,
+                &hyrax_params,
             )
             .expect("Relationship check failed");
 
diff --git a/folding-schemes/src/folding/nova/nifs/pointvsline.rs b/folding-schemes/src/folding/nova/nifs/pointvsline.rs
index 391e7bfe8..e79a62c42 100644
--- a/folding-schemes/src/folding/nova/nifs/pointvsline.rs
+++ b/folding-schemes/src/folding/nova/nifs/pointvsline.rs
@@ -416,8 +416,11 @@ mod tests {
     use ark_std::{log2, UniformRand};
 
     use crate::folding::nova::nifs::mova::Witness;
-    use crate::folding::nova::nifs::mova_matrix::Witness as MatrixWitness;
+    use crate::folding::nova::nifs::mova_matrix::{
+        RelaxedCommittedRelation, Witness as MatrixWitness,
+    };
 
+    use crate::commitment::hyrax::{Hyrax, HyraxGenerators};
     use crate::utils::mle::MultilinearExtension;
     use ark_crypto_primitives::sponge::CryptographicSponge;
     use ark_ff::{One, Zero};
@@ -606,7 +609,7 @@ mod tests {
         // This test mainly focuses on if the evaluation of h1 are correct.
         let mut rng = ark_std::test_rng();
 
-        let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, 4)?;
+        let hyrax_params = HyraxGenerators::<Projective>::setup(&mut rng, 4);
         let poseidon_config = poseidon_canonical_config::<Fr>();
         let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
         let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
@@ -621,8 +624,25 @@ mod tests {
             C: Matrix::dense_from_vec(vec![three, four, five, six], 2, 2).unwrap(),
             E: Matrix::dense_from_vec(vec![three, four, five, six], 2, 2).unwrap(),
         };
-        let rE = (0..log2(W_i.E.len())).map(|_| Fr::rand(&mut rng)).collect();
-        let U_i = MatrixWitness::commit::<Pedersen<Projective>, false>(&W_i, &pedersen_params, rE)?;
+        let rE: Vec<Fr> = (0..log2(W_i.E.len())).map(|_| Fr::rand(&mut rng)).collect();
+        // below is the commit code modified to work with dense matrices.
+        let U_i = {
+            let mle = MultilinearExtension::from_evaluations(&W_i.E, log2(W_i.E.len()) as usize);
+            let mleE = mle.evaluate(&rE);
+            // Right now we are ignoring the hiding property and directly commit to the matrices
+            let com_a = Hyrax::commit(W_i.A.as_dense_slice().unwrap(), &hyrax_params)?;
+            let com_b = Hyrax::commit(W_i.B.as_dense_slice().unwrap(), &hyrax_params)?;
+            let com_c = Hyrax::commit(W_i.C.as_dense_slice().unwrap(), &hyrax_params)?;
+
+            RelaxedCommittedRelation {
+                cmA: com_a,
+                cmB: com_b,
+                cmC: com_c,
+                u: Fr::one(),
+                mleE,
+                rE,
+            }
+        };
 
         let w_i = MatrixWitness {
             A: Matrix::dense_from_vec(vec![three, four, five, six], 2, 2).unwrap(),
@@ -630,8 +650,25 @@ mod tests {
             C: Matrix::dense_from_vec(vec![three, four, five, six], 2, 2).unwrap(),
             E: Matrix::dense_from_vec(vec![three, four, five, six], 2, 2).unwrap(),
         };
-        let rE = (0..log2(W_i.E.len())).map(|_| Fr::rand(&mut rng)).collect();
-        let u_i = MatrixWitness::commit::<Pedersen<Projective>, false>(&w_i, &pedersen_params, rE)?;
+        let rE: Vec<Fr> = (0..log2(W_i.E.len())).map(|_| Fr::rand(&mut rng)).collect();
+        // below is the commit code modified to work with dense matrices.
+        let u_i = {
+            let mle = MultilinearExtension::from_evaluations(&W_i.E, log2(W_i.E.len()) as usize);
+            let mleE = mle.evaluate(&rE);
+            // Right now we are ignoring the hiding property and directly commit to the matrices
+            let com_a = Hyrax::commit(W_i.A.as_dense_slice().unwrap(), &hyrax_params)?;
+            let com_b = Hyrax::commit(W_i.B.as_dense_slice().unwrap(), &hyrax_params)?;
+            let com_c = Hyrax::commit(W_i.C.as_dense_slice().unwrap(), &hyrax_params)?;
+
+            RelaxedCommittedRelation {
+                cmA: com_a,
+                cmB: com_b,
+                cmC: com_c,
+                u: Fr::one(),
+                mleE,
+                rE,
+            }
+        };
 
         let (proof, claim) = PointVsLineMatrix::prove(&mut transcript_p, None, &u_i, &W_i, &w_i)?;
 
@@ -673,7 +710,7 @@ mod tests {
         // This test mainly focuses on if the evaluation of h1 are correct.
         let mut rng = ark_std::test_rng();
 
-        let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, 4)?;
+        let hyrax_params = HyraxGenerators::<Projective>::setup(&mut rng, 4);
         let poseidon_config = poseidon_canonical_config::<Fr>();
         let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
         let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
@@ -689,7 +726,7 @@ mod tests {
             E: Matrix::sparse_from_vec(vec![(1, four), (2, five)], 2, 2).unwrap(),
         };
         let rE = (0..log2(W_i.E.len())).map(|_| Fr::rand(&mut rng)).collect();
-        let U_i = MatrixWitness::commit::<Pedersen<Projective>, false>(&W_i, &pedersen_params, rE)?;
+        let U_i = MatrixWitness::commit(&W_i, &hyrax_params, rE)?;
 
         let w_i = MatrixWitness {
             A: Matrix::sparse_from_vec(vec![(0, three), (3, six)], 2, 2).unwrap(),
@@ -698,7 +735,7 @@ mod tests {
             E: Matrix::sparse_from_vec(vec![(1, four), (2, five)], 2, 2).unwrap(),
         };
         let rE = (0..log2(W_i.E.len())).map(|_| Fr::rand(&mut rng)).collect();
-        let u_i = MatrixWitness::commit::<Pedersen<Projective>, false>(&w_i, &pedersen_params, rE)?;
+        let u_i = MatrixWitness::commit(&w_i, &hyrax_params, rE)?;
 
         let (proof, claim) = PointVsLineMatrix::prove(&mut transcript_p, None, &u_i, &W_i, &w_i)?;
 


Reviews:
user: login: Fantoni0
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T17:09:00Z
user: login: Fantoni0
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T17:10:25Z
user: login: NiDimi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T17:14:44Z
user: login: NiDimi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T17:15:29Z

Review comments:
diff_hunk: @@ -67,6 +67,16 @@ pub trait CommitmentScheme<C: Curve, const H: bool = false>: Clone + Debug {
     ) -> Result<(), Error>;
 }
 
+pub trait NethermindCommitmentScheme<C: Curve, const H: bool = false>:
path: folding-schemes/src/commitment/mod.rs
user: login: Fantoni0
body: Could we change the name to SparseCommitmentScheme or something more descriptive? The same for setup2, maybe setup_sparse? 
created_at: 2025-04-01T17:09:00Z
author_association: COLLABORATOR
diff_hunk: @@ -178,6 +178,47 @@ pub struct PedersenGadget<C: Curve, const H: bool = false> {
     _c: PhantomData<C>,
 }
 
+impl<C: Curve, const H: bool> NethermindCommitmentScheme<C, H> for Pedersen<C, H> {
+    fn commit_sparse(
+        params: &Self::ProverParams,
+        v: &[(usize, C::ScalarField)],
+        r: &C::ScalarField,
+    ) -> Result<C, Error> {
+        let (selected_generators, values): (Vec<_>, Vec<_>) = v
+            .iter()
+            .map(|(i, val)| (params.generators[*i], *val))
+            .unzip();
+
+        let msm_result = C::msm_unchecked(&selected_generators, &values);
+
+        if !H {
+            if !r.is_zero() {
+                return Err(Error::BlindingNotZero);
+            }
+            Ok(msm_result)
+        } else {
+            Ok(params.h.mul(r) + msm_result)
+        }
+    }
+
+    fn setup2(mut rng: impl RngCore, len: usize) -> Result<Self::ProverParams, Error> {
+        // Pre-calculate the size and pre-allocate the vector capacity
+        let size = len.next_power_of_two();
path: folding-schemes/src/commitment/pedersen.rs
user: login: Fantoni0
body: I don't see how this is different from the original Pedersen setup. Maybe I am missing something, but please add some comments mentioning why we need a second setup.
created_at: 2025-04-01T17:10:25Z
author_association: COLLABORATOR
diff_hunk: @@ -67,6 +67,16 @@ pub trait CommitmentScheme<C: Curve, const H: bool = false>: Clone + Debug {
     ) -> Result<(), Error>;
 }
 
+pub trait NethermindCommitmentScheme<C: Curve, const H: bool = false>:
path: folding-schemes/src/commitment/mod.rs
user: login: NiDimi
body: So `setup2` is going to change for sure. The problem is that I am not sure how to name NethermindCommitmentScheme given I also changed the setup to be more efficient in our use case. Also since we are going to switch to hyrax we might not even need this code.

created_at: 2025-04-01T17:14:43Z
author_association: COLLABORATOR
diff_hunk: @@ -178,6 +178,47 @@ pub struct PedersenGadget<C: Curve, const H: bool = false> {
     _c: PhantomData<C>,
 }
 
+impl<C: Curve, const H: bool> NethermindCommitmentScheme<C, H> for Pedersen<C, H> {
+    fn commit_sparse(
+        params: &Self::ProverParams,
+        v: &[(usize, C::ScalarField)],
+        r: &C::ScalarField,
+    ) -> Result<C, Error> {
+        let (selected_generators, values): (Vec<_>, Vec<_>) = v
+            .iter()
+            .map(|(i, val)| (params.generators[*i], *val))
+            .unzip();
+
+        let msm_result = C::msm_unchecked(&selected_generators, &values);
+
+        if !H {
+            if !r.is_zero() {
+                return Err(Error::BlindingNotZero);
+            }
+            Ok(msm_result)
+        } else {
+            Ok(params.h.mul(r) + msm_result)
+        }
+    }
+
+    fn setup2(mut rng: impl RngCore, len: usize) -> Result<Self::ProverParams, Error> {
+        // Pre-calculate the size and pre-allocate the vector capacity
+        let size = len.next_power_of_two();
path: folding-schemes/src/commitment/pedersen.rs
user: login: NiDimi
body: The original is returning the params twice, twice the memory. This is because their trait has implemented the prover and verifier params. But the params are the same in Pedersen. 
created_at: 2025-04-01T17:15:29Z
author_association: COLLABORATOR

PR comments:
user: login: Fantoni0
created_at: 2025-03-27T21:44:35Z
body: I believe you need to update the matrex dependency to use the in-place operators. I tried to do that manually and benches are failing. Is it only on my end? @NiDimi 
author_association: COLLABORATOR
user: login: Fantoni0
created_at: 2025-04-01T17:07:40Z
body: In general, it looks fine, but I was not able to perceive a significant memory improvement. I was testing this PR against main, and couldn't see significant difference. I tried up to matrix_dim=10000. Were you able to profile it using valgrind @NiDimi ?
author_association: COLLABORATOR
user: login: cupicmarko
created_at: 2025-04-01T22:52:42Z
body: I slightly modified the code for the benchmark and created a test out of it, because it is easier to profile with valgrind, I'll paste it here because GitHub doesn't allow attaching rs files 😢 It can be placed in folding-schemes/tests and run as a test. I didn't notice improvement with different mat_dim (compared with main), otherwise the code looks good.
We can also use `matrex = {git = "ssh://git@github.com/NethermindEth/matrex.git", tag = "v0.1.7", features = ["parallel"]}` in Cargo.toml.
```rust
use ark_crypto_primitives::sponge::CryptographicSponge;
use ark_crypto_primitives::sponge::poseidon::PoseidonSponge;
use ark_pallas::{Fr, Projective};
use ark_std::{log2, UniformRand};
use matrex::Matrix;
use rand::{Rng, RngCore};
use folding_schemes::commitment::{CommitmentScheme, NethermindCommitmentScheme};
use folding_schemes::commitment::pedersen::Pedersen;
use folding_schemes::Curve;
use folding_schemes::transcript::poseidon::poseidon_canonical_config;
use folding_schemes::folding::nova::nifs::mova_matrix::{RelaxedCommittedRelation, Witness, NIFS};

fn random_sparse_matrix<C: Curve>(n: usize, rng: &mut impl RngCore) -> Matrix<C::ScalarField> {
    let elements = (0..n)
        .map(|row| {
            (
                row * n + rand::thread_rng().gen_range(0..n),
                C::ScalarField::rand(rng),
            )
        })
        .collect();
    Matrix::sparse_from_vec(elements, n, n).unwrap()
}


fn get_instances<C: Curve, CS: CommitmentScheme<C> + NethermindCommitmentScheme<C>>(
    num: usize,
    n: usize,
    rng: &mut impl RngCore,
    params: &CS::ProverParams,
) -> Vec<(Witness<C>, RelaxedCommittedRelation<C>)> {
    (0..num)
        .map(|_| -> (Witness<C>, RelaxedCommittedRelation<C>) {
            // A matrix
            let a = random_sparse_matrix::<C>(n, rng);
            // println!("Size of `a` by value: {} bytes", size_of_val(&a));
            // println!("Size of `scalar` by value: {} bytes",size_of::<C::ScalarField>());

            // B matrix
            let b = random_sparse_matrix::<C>(n, rng);
            // C = A * B matrix
            let mut c = (&a * &b).unwrap();
            // c.to_dense();
            // Error matrix initialized to 0s
            let e = Matrix::zero(n, n);

            // Random challenge
            let rE = (0..2 * log2(n))
                .map(|_| C::ScalarField::rand(rng))
                .collect();
            // Witness
            let witness = Witness::new::<false>(a, b, c, e);
            let instance = witness.commit::<CS, false>(params, rE).unwrap();
            (witness, instance)
        })
        .collect()
}

#[test]
fn test_mova_matrix_single_run() {
    let mut rng = ark_std::test_rng();
    let mat_dim = 1 << 4;
    let num_folds = 10;
    
    let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, mat_dim * mat_dim).unwrap();
    let poseidon_config = poseidon_canonical_config::<Fr>();
    let pp_hash = Fr::rand(&mut rng);
    
    let mut instances: Vec<(
        Witness<Projective>,
        RelaxedCommittedRelation<Projective>,
    )> = get_instances::<Projective, Pedersen<Projective>>(
        num_folds + 1, // +1 for the accumulator instance
        mat_dim,
        &mut rng,
        &pedersen_params,
    );

    let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
    let mut acc = instances.pop().unwrap();

    for _ in 0..num_folds {
        let mut next = instances.pop().unwrap();

        let (wit_acc, inst_acc, _) = NIFS::<
            Projective,
            Pedersen<Projective>,
            PoseidonSponge<Fr>,
        >::prove(
            &mut transcript_p,
            pp_hash,
            &mut next.0,
            &next.1,
            &acc.0,
            &acc.1,
        )
            .unwrap();

        acc = (wit_acc, inst_acc);
    }
    
    println!("Completed {} folds successfully.", num_folds);
}

```

author_association: NONE
user: login: NiDimi
created_at: 2025-04-02T07:20:05Z
body: @cupicmarko when you say you didn't notice any improvement,t what do you mean? In memory?
author_association: COLLABORATOR
user: login: cupicmarko
created_at: 2025-04-02T09:14:24Z
body: @NiDimi yes, memory use, sorry for missing that
author_association: NONE