mirror of
https://github.com/arnaucube/mat-vec-prod-exp.git
synced 2026-01-13 01:11:28 +01:00
init
This commit is contained in:
113
src/lib.rs
Normal file
113
src/lib.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused_doc_comments)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use ark_ff::PrimeField;
|
||||
use ark_r1cs_std::fields::nonnative::NonNativeFieldVar;
|
||||
use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, fields::FieldVar, R1CSVar};
|
||||
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
|
||||
use core::marker::PhantomData;
|
||||
use std::ops::Mul;
|
||||
|
||||
mod utils;
|
||||
use utils::*;
|
||||
|
||||
/// - F stands for the field that we represent
|
||||
/// - CF stands for the ConstraintField over which we do the operations
|
||||
|
||||
/// Implements the A * z matrix-vector-product by fixing the combinations of 'z'.
|
||||
fn handcrafted_A_by_z<F: PrimeField, CF: PrimeField>(
|
||||
cs: ConstraintSystemRef<CF>,
|
||||
z: Vec<NonNativeFieldVar<F, CF>>,
|
||||
) -> Result<Vec<NonNativeFieldVar<F, CF>>, SynthesisError> {
|
||||
let five = NonNativeFieldVar::<F, CF>::new_constant(cs.clone(), F::from(5u32))?;
|
||||
// directly hand-craft the output vector containing the operations in-place:
|
||||
Ok(vec![
|
||||
z[1].clone() + five.clone() * z[4].clone(),
|
||||
z[1].clone() + z[3].clone(),
|
||||
z[1].clone() + z[4].clone(),
|
||||
five * z[0].clone() + z[4].clone() + z[5].clone(),
|
||||
]
|
||||
.clone())
|
||||
}
|
||||
|
||||
/// Implements the A * z matrix-vector-product by doing the sparse matrix by vector algorithm, and
|
||||
/// assuming that the elements of the matrix A are constants of the system.
|
||||
pub fn mat_vec_mul_sparse_gadget<F: PrimeField, CF: PrimeField>(
|
||||
m: SparseMatrixVar<F, CF>,
|
||||
v: Vec<NonNativeFieldVar<F, CF>>,
|
||||
) -> Vec<NonNativeFieldVar<F, CF>> {
|
||||
let mut res = vec![NonNativeFieldVar::<F, CF>::zero(); m.n_rows];
|
||||
for (row_i, row) in m.coeffs.iter().enumerate() {
|
||||
for (value, col_i) in row.iter() {
|
||||
if value.value().unwrap() == F::one() {
|
||||
res[row_i] += v[*col_i].clone(); // when value==1, no need to multiply by it
|
||||
continue;
|
||||
}
|
||||
res[row_i] += value.clone().mul(&v[*col_i].clone());
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// Circuit that takes as constants the sparse matrix A, and as inputs the vectors z and y. It
|
||||
/// computes the matrix by vector product between A and z, and checks that is equal to y
|
||||
/// (ie. y == A*z)
|
||||
struct MatrixVectorCircuit<F: PrimeField, CF: PrimeField> {
|
||||
_cf: PhantomData<CF>,
|
||||
pub A: SparseMatrix<F>,
|
||||
pub z: Vec<F>,
|
||||
pub y: Vec<F>,
|
||||
}
|
||||
impl<F: PrimeField, CF: PrimeField> ConstraintSynthesizer<CF> for MatrixVectorCircuit<F, CF> {
|
||||
fn generate_constraints(self, cs: ConstraintSystemRef<CF>) -> Result<(), SynthesisError> {
|
||||
// set A as circuit constants
|
||||
let A = SparseMatrixVar::<F, CF>::new_constant(cs.clone(), self.A)?;
|
||||
// set z and y as witness (private inputs)
|
||||
let z: Vec<NonNativeFieldVar<F, CF>> = Vec::new_witness(cs.clone(), || Ok(self.z.clone()))?;
|
||||
let y: Vec<NonNativeFieldVar<F, CF>> = Vec::new_witness(cs.clone(), || Ok(self.y.clone()))?;
|
||||
|
||||
/// The next two lines are the ones that can be swapped to see the number of constraints
|
||||
/// taken by the two approaches:
|
||||
let Az = mat_vec_mul_sparse_gadget(A, z);
|
||||
// let Az = handcrafted_A_by_z(cs, z)?;
|
||||
|
||||
Az.enforce_equal(&y)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ark_pallas::{Fq, Fr};
|
||||
use ark_relations::r1cs::ConstraintSystem;
|
||||
|
||||
#[test]
|
||||
fn test_relaxed_r1cs_nonnative_matrix_vector_product() {
|
||||
let A = to_F_matrix::<Fq>(vec![
|
||||
vec![0, 1, 0, 0, 5, 0],
|
||||
vec![0, 1, 0, 1, 0, 0],
|
||||
vec![0, 1, 0, 0, 1, 0],
|
||||
vec![5, 0, 0, 0, 1, 1],
|
||||
]);
|
||||
let z = to_F_vec(vec![1, 123, 35, 53, 80, 30]);
|
||||
let y = mat_vec_mul_sparse(&A, &z); // y = A*z
|
||||
println!("Matrix of size {} x {}", A.n_rows, A.n_cols);
|
||||
println!("Vector of size {}", z.len());
|
||||
|
||||
println!(
|
||||
"Build the circuit that computes the matrix-vector-product over a non-native field"
|
||||
);
|
||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
let circuit = MatrixVectorCircuit::<Fq, Fr> {
|
||||
_cf: PhantomData,
|
||||
A,
|
||||
z,
|
||||
y,
|
||||
};
|
||||
circuit.generate_constraints(cs.clone()).unwrap();
|
||||
println!("Number of constraints: {}", cs.num_constraints());
|
||||
assert!(cs.is_satisfied().unwrap());
|
||||
}
|
||||
}
|
||||
101
src/utils.rs
Normal file
101
src/utils.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use ark_ff::PrimeField;
|
||||
use ark_r1cs_std::{
|
||||
alloc::{AllocVar, AllocationMode},
|
||||
fields::nonnative::NonNativeFieldVar,
|
||||
};
|
||||
use ark_relations::r1cs::{Matrix as R1CSMatrix, Namespace, SynthesisError};
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
pub struct SparseMatrix<F: PrimeField> {
|
||||
pub n_rows: usize,
|
||||
pub n_cols: usize,
|
||||
/// coeffs = R1CSMatrix = Vec<Vec<(F, usize)>>, which contains each row and the F is the value
|
||||
/// of the coefficient and the usize indicates the column position
|
||||
pub coeffs: R1CSMatrix<F>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SparseMatrixVar<F: PrimeField, CF: PrimeField> {
|
||||
_f: PhantomData<F>,
|
||||
_cf: PhantomData<CF>,
|
||||
pub n_rows: usize,
|
||||
pub n_cols: usize,
|
||||
// same format as the native SparseMatrix (which follows ark_relations::r1cs::Matrix format
|
||||
pub coeffs: Vec<Vec<(NonNativeFieldVar<F, CF>, usize)>>,
|
||||
}
|
||||
|
||||
impl<F, CF> AllocVar<SparseMatrix<F>, CF> for SparseMatrixVar<F, CF>
|
||||
where
|
||||
F: PrimeField,
|
||||
CF: PrimeField,
|
||||
{
|
||||
fn new_variable<T: Borrow<SparseMatrix<F>>>(
|
||||
cs: impl Into<Namespace<CF>>,
|
||||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||
mode: AllocationMode,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
f().and_then(|val| {
|
||||
let cs = cs.into();
|
||||
|
||||
let mut coeffs: Vec<Vec<(NonNativeFieldVar<F, CF>, usize)>> = Vec::new();
|
||||
for row in val.borrow().coeffs.iter() {
|
||||
let mut rowVar: Vec<(NonNativeFieldVar<F, CF>, usize)> = Vec::new();
|
||||
for &(value, col_i) in row.iter() {
|
||||
let coeffVar =
|
||||
NonNativeFieldVar::<F, CF>::new_variable(cs.clone(), || Ok(value), mode)?;
|
||||
rowVar.push((coeffVar, col_i));
|
||||
}
|
||||
coeffs.push(rowVar);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
_f: PhantomData,
|
||||
_cf: PhantomData,
|
||||
n_rows: val.borrow().n_rows,
|
||||
n_cols: val.borrow().n_cols,
|
||||
coeffs,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mat_vec_mul_sparse<F: PrimeField>(M: &SparseMatrix<F>, z: &[F]) -> Vec<F> {
|
||||
assert_eq!(M.n_cols, z.len());
|
||||
let mut res = vec![F::zero(); M.n_rows];
|
||||
for (row_i, row) in M.coeffs.iter().enumerate() {
|
||||
for &(value, col_i) in row.iter() {
|
||||
res[row_i] += value * z[col_i];
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
pub fn dense_matrix_to_sparse<F: PrimeField>(m: Vec<Vec<F>>) -> SparseMatrix<F> {
|
||||
let mut r = SparseMatrix::<F> {
|
||||
n_rows: m.len(),
|
||||
n_cols: m[0].len(),
|
||||
coeffs: Vec::new(),
|
||||
};
|
||||
for m_row in m.iter() {
|
||||
let mut row: Vec<(F, usize)> = Vec::new();
|
||||
for (col_i, value) in m_row.iter().enumerate() {
|
||||
if !value.is_zero() {
|
||||
row.push((*value, col_i));
|
||||
}
|
||||
}
|
||||
r.coeffs.push(row);
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
// just some helpers to define matrices and vectors by hand
|
||||
pub fn to_F_matrix<F: PrimeField>(M: Vec<Vec<usize>>) -> SparseMatrix<F> {
|
||||
dense_matrix_to_sparse(to_F_dense_matrix(M))
|
||||
}
|
||||
pub fn to_F_dense_matrix<F: PrimeField>(M: Vec<Vec<usize>>) -> Vec<Vec<F>> {
|
||||
M.iter()
|
||||
.map(|m| m.iter().map(|r| F::from(*r as u64)).collect())
|
||||
.collect()
|
||||
}
|
||||
pub fn to_F_vec<F: PrimeField>(z: Vec<usize>) -> Vec<F> {
|
||||
z.iter().map(|c| F::from(*c as u64)).collect()
|
||||
}
|
||||
Reference in New Issue
Block a user