implement parallelized polynomial evaluation

This commit is contained in:
Alex Vlasov 2019-02-07 17:19:20 +03:00
parent dc5e5ffe31
commit 056b7873cc
8 changed files with 278 additions and 200 deletions

@ -35,6 +35,10 @@ impl Worker {
log2_floor(self.cpus)
}
pub(crate) fn num_cpus(&self) -> usize {
self.cpus
}
pub fn compute<F, R>(
&self, f: F
) -> WorkerFuture<R::Item, R::Error>

@ -16,6 +16,8 @@ use crate::SynthesisError;
use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::cs::{Circuit};
use super::parameters::VerifyingKey;
use crate::sonic::srs::SRS;
use crate::sonic::util::multiexp;
@ -83,6 +85,25 @@ impl<E: Engine> Batch<E> {
}
}
pub fn new_from_key(vk: &VerifyingKey<E>) -> Self {
Batch {
alpha_x: vec![],
alpha_x_precomp: vk.alpha_x.prepare(),
alpha: vec![],
alpha_precomp: vk.alpha.prepare(),
neg_h: vec![],
neg_h_precomp: vk.neg_h.prepare(),
neg_x_n_minus_d: vec![],
neg_x_n_minus_d_precomp: vk.neg_x_n_minus_d.prepare(),
value: E::Fr::zero(),
g: E::G1Affine::one(),
}
}
pub fn add_opening(&mut self, p: E::G1Affine, mut r: E::Fr, point: E::Fr) {
self.alpha_x.push((p, r));
r.mul_assign(&point);

@ -452,15 +452,7 @@ pub fn generate_parameters_on_srs_and_information<E: Engine>(
Ok(Parameters{
vk: vk,
d: trimmed_srs.d,
g_negative_x: Arc::new(trimmed_srs.g_negative_x),
g_positive_x: Arc::new(trimmed_srs.g_positive_x),
h_negative_x: Arc::new(trimmed_srs.h_negative_x),
h_positive_x: Arc::new(trimmed_srs.h_positive_x),
g_negative_x_alpha: Arc::new(trimmed_srs.g_negative_x_alpha),
g_positive_x_alpha: Arc::new(trimmed_srs.g_positive_x_alpha),
h_negative_x_alpha: Arc::new(trimmed_srs.h_negative_x_alpha),
h_positive_x_alpha: Arc::new(trimmed_srs.h_positive_x_alpha)
srs: trimmed_srs
})
}

@ -16,7 +16,13 @@ mod generator;
pub use self::batch::{Batch};
pub use self::helper::{Aggregate, create_aggregate};
pub use self::verifier::{MultiVerifier};
pub use self::prover::{create_proof, create_advice};
pub use self::prover::{
create_advice,
create_advice_on_information_and_srs,
create_advice_on_srs,
create_proof
};
pub use self::generator::{
CircuitParameters,
generate_parameters,

@ -300,45 +300,38 @@ pub struct PreparedVerifyingKey<E: Engine> {
pub struct Parameters<E: Engine> {
pub vk: VerifyingKey<E>,
pub d: usize,
pub srs: SRS<E>,
// pub d: usize,
// g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}
pub g_negative_x: Arc<Vec<E::G1Affine>>,
// // g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}
// pub g_negative_x: Arc<Vec<E::G1Affine>>,
// g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}
pub g_positive_x: Arc<Vec<E::G1Affine>>,
// // g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}
// pub g_positive_x: Arc<Vec<E::G1Affine>>,
// g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}
pub h_negative_x: Arc<Vec<E::G2Affine>>,
// // g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}
// pub h_negative_x: Arc<Vec<E::G2Affine>>,
// g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}
pub h_positive_x: Arc<Vec<E::G2Affine>>,
// // g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}
// pub h_positive_x: Arc<Vec<E::G2Affine>>,
// alpha*(g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}})
pub g_negative_x_alpha: Arc<Vec<E::G1Affine>>,
// // alpha*(g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}})
// pub g_negative_x_alpha: Arc<Vec<E::G1Affine>>,
// alpha*(g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}})
pub g_positive_x_alpha: Arc<Vec<E::G1Affine>>,
// // alpha*(g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}})
// pub g_positive_x_alpha: Arc<Vec<E::G1Affine>>,
// alpha*(h^{x^0}, h^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}})
pub h_negative_x_alpha: Arc<Vec<E::G2Affine>>,
// // alpha*(h^{x^0}, h^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}})
// pub h_negative_x_alpha: Arc<Vec<E::G2Affine>>,
// alpha*(h^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}})
pub h_positive_x_alpha: Arc<Vec<E::G2Affine>>,
// // alpha*(h^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}})
// pub h_positive_x_alpha: Arc<Vec<E::G2Affine>>,
}
impl<E: Engine> PartialEq for Parameters<E> {
fn eq(&self, other: &Parameters<E>) -> bool {
self.vk == other.vk &&
self.d == other.d &&
self.g_negative_x == other.g_negative_x &&
self.g_positive_x == other.g_positive_x &&
self.h_negative_x == other.h_negative_x &&
self.h_positive_x == other.h_positive_x &&
self.g_negative_x_alpha == other.g_negative_x_alpha &&
self.g_positive_x_alpha == other.g_positive_x_alpha &&
self.h_negative_x_alpha == other.h_negative_x_alpha &&
self.h_positive_x_alpha == other.h_positive_x_alpha
self.srs == other.srs
}
}
@ -349,48 +342,7 @@ impl<E: Engine> Parameters<E> {
) -> io::Result<()>
{
self.vk.write(&mut writer)?;
assert_eq!(self.d + 1, self.g_negative_x.len());
assert_eq!(self.d + 1, self.g_positive_x.len());
assert_eq!(self.d + 1, self.h_negative_x.len());
assert_eq!(self.d + 1, self.h_positive_x.len());
assert_eq!(self.d, self.g_negative_x_alpha.len());
assert_eq!(self.d, self.g_positive_x_alpha.len());
assert_eq!(self.d + 1, self.h_negative_x_alpha.len());
assert_eq!(self.d + 1, self.h_positive_x_alpha.len());
writer.write_u32::<BigEndian>(self.d as u32)?;
for g in &self.g_negative_x[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
for g in &self.g_positive_x[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
for g in &self.h_negative_x[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
for g in &self.h_positive_x[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
for g in &self.g_negative_x_alpha[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
for g in &self.g_positive_x_alpha[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
for g in &self.h_negative_x_alpha[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
for g in &self.h_positive_x_alpha[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
self.srs.write(&mut writer)?;
Ok(())
}
@ -400,107 +352,12 @@ impl<E: Engine> Parameters<E> {
checked: bool
) -> io::Result<Self>
{
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(repr.as_mut())?;
if checked {
repr
.into_affine()
} else {
repr
.into_affine_unchecked()
}
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})
};
let read_g2 = |reader: &mut R| -> io::Result<E::G2Affine> {
let mut repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(repr.as_mut())?;
if checked {
repr
.into_affine()
} else {
repr
.into_affine_unchecked()
}
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})
};
let vk = VerifyingKey::<E>::read(&mut reader)?;
let mut g_negative_x = vec![];
let mut g_positive_x = vec![];
let mut h_negative_x = vec![];
let mut h_positive_x = vec![];
let mut g_negative_x_alpha = vec![];
let mut g_positive_x_alpha = vec![];
let mut h_negative_x_alpha = vec![];
let mut h_positive_x_alpha = vec![];
let d = reader.read_u32::<BigEndian>()? as usize;
{
for _ in 0..(d+1) {
g_negative_x.push(read_g1(&mut reader)?);
}
for _ in 0..(d+1) {
g_positive_x.push(read_g1(&mut reader)?);
}
}
{
for _ in 0..(d+1) {
h_negative_x.push(read_g2(&mut reader)?);
}
for _ in 0..(d+1) {
h_positive_x.push(read_g2(&mut reader)?);
}
}
{
for _ in 0..d {
g_negative_x_alpha.push(read_g1(&mut reader)?);
}
for _ in 0..d {
g_positive_x_alpha.push(read_g1(&mut reader)?);
}
}
{
for _ in 0..(d+1) {
h_negative_x_alpha.push(read_g2(&mut reader)?);
}
for _ in 0..(d+1) {
h_positive_x_alpha.push(read_g2(&mut reader)?);
}
}
let srs = SRS::<E>::read(&mut reader, checked)?;
Ok(Parameters {
vk: vk,
d: d,
g_negative_x: Arc::new(g_negative_x),
g_positive_x: Arc::new(g_positive_x),
h_negative_x: Arc::new(h_negative_x),
h_positive_x: Arc::new(h_positive_x),
g_negative_x_alpha: Arc::new(g_negative_x_alpha),
g_positive_x_alpha: Arc::new(g_positive_x_alpha),
h_negative_x_alpha: Arc::new(h_negative_x_alpha),
h_positive_x_alpha: Arc::new(h_positive_x_alpha)
srs: srs
})
}
}

@ -5,6 +5,7 @@ use std::marker::PhantomData;
use super::{Proof, SxyAdvice};
use super::batch::Batch;
use super::poly::{SxEval, SyEval};
use super::parameters::{Parameters};
use crate::SynthesisError;
@ -14,31 +15,13 @@ use crate::sonic::cs::{Backend, SynthesisDriver};
use crate::sonic::cs::{Circuit, Variable, Coeff};
use crate::sonic::srs::SRS;
pub fn create_advice<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
pub fn create_advice_on_information_and_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
proof: &Proof<E>,
srs: &SRS<E>
srs: &SRS<E>,
n: usize
) -> SxyAdvice<E>
{
// annoying, but we need n to compute s(z, y), and this isn't
// precomputed anywhere yet
let n = {
struct CountN {
n: usize
}
impl<'a, E: Engine> Backend<E> for &'a mut CountN {
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
}
let mut tmp = CountN{n:0};
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
tmp.n
};
let z: E::Fr;
let y: E::Fr;
{
@ -111,6 +94,44 @@ pub fn create_advice<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
}
}
pub fn create_advice<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
proof: &Proof<E>,
parameters: &Parameters<E>,
) -> SxyAdvice<E>
{
let n = parameters.vk.n;
create_advice_on_information_and_srs::<E, C, S>(circuit, proof, &parameters.srs, n)
}
pub fn create_advice_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
proof: &Proof<E>,
srs: &SRS<E>
) -> SxyAdvice<E>
{
// annoying, but we need n to compute s(z, y), and this isn't
// precomputed anywhere yet
let n = {
struct CountN {
n: usize
}
impl<'a, E: Engine> Backend<E> for &'a mut CountN {
fn new_multiplication_gate(&mut self) {
self.n += 1;
}
}
let mut tmp = CountN{n:0};
S::synthesize(&mut tmp, circuit).unwrap(); // TODO
tmp.n
};
create_advice_on_information_and_srs::<E, C, S>(circuit, proof, srs, n)
}
pub fn create_proof<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
srs: &SRS<E>

@ -71,6 +71,118 @@ where
}
}
extern crate crossbeam;
use self::crossbeam::channel::{unbounded, RecvError};
pub fn evaluate_at_consequitive_powers<'a, F: Field> (
coeffs: &[F],
first_power: F,
base: F
) -> F
{
use crate::multicore::Worker;
let (s, r) = unbounded();
let worker = Worker::new();
worker.scope(coeffs.len(), |scope, chunk| {
for (i, coeffs) in coeffs.chunks(chunk).enumerate()
{
let s = s.clone();
scope.spawn(move |_| {
let mut current_power = base.pow(&[(i*chunk) as u64]);
current_power.mul_assign(&first_power);
let mut acc = F::zero();
for p in coeffs {
let mut tmp = *p;
tmp.mul_assign(&current_power);
acc.add_assign(&tmp);
current_power.mul_assign(&base);
}
s.send(acc).expect("must send");
});
}
});
drop(s);
// all threads in a scope have done working, so we can safely read
let mut result = F::zero();
loop {
let v = r.recv();
match v {
Ok(value) => {
result.add_assign(&value);
},
Err(RecvError) => {
break;
}
}
}
result
}
pub fn mut_evaluate_at_consequitive_powers<'a, F: Field> (
coeffs: &mut [F],
first_power: F,
base: F
) -> F
{
use crate::multicore::Worker;
let (s, r) = unbounded();
let worker = Worker::new();
worker.scope(coeffs.len(), |scope, chunk| {
for (i, coeffs) in coeffs.chunks_mut(chunk).enumerate()
{
let s = s.clone();
scope.spawn(move |_| {
let mut current_power = base.pow(&[(i*chunk) as u64]);
current_power.mul_assign(&first_power);
let mut acc = F::zero();
for mut p in coeffs {
p.mul_assign(&current_power);
acc.add_assign(&p);
current_power.mul_assign(&base);
}
s.send(acc).expect("must send");
});
}
});
drop(s);
// all threads in a scope have done working, so we can safely read
let mut result = F::zero();
loop {
let v = r.recv();
match v {
Ok(value) => {
result.add_assign(&value);
},
Err(RecvError) => {
break;
}
}
}
result
}
pub fn multiexp<
'a,
G: CurveAffine,
@ -421,3 +533,68 @@ fn test_mul() {
assert_eq!(serial_res.len(), parallel_res.len());
assert_eq!(serial_res, parallel_res);
}
#[test]
fn test_eval_at_powers() {
use rand::{self, Rand, Rng};
use pairing::bls12_381::Bls12;
use pairing::bls12_381::Fr;
const SAMPLES: usize = 100000;
let rng = &mut rand::thread_rng();
let a = (0..SAMPLES).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let x: Fr = rng.gen();
let n: u32 = rng.gen();
let mut acc = Fr::zero();
{
let mut tmp = x.pow(&[n as u64]);
for coeff in a.iter() {
let mut c = *coeff;
c.mul_assign(&tmp);
acc.add_assign(&c);
tmp.mul_assign(&x);
}
}
let first_power = x.pow(&[n as u64]);
let acc_parallel = evaluate_at_consequitive_powers(&a[..], first_power, x);
assert_eq!(acc_parallel, acc);
}
#[test]
fn test_mut_eval_at_powers() {
use rand::{self, Rand, Rng};
use pairing::bls12_381::Bls12;
use pairing::bls12_381::Fr;
const SAMPLES: usize = 100000;
let rng = &mut rand::thread_rng();
let mut a = (0..SAMPLES).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
let mut b = a.clone();
let x: Fr = rng.gen();
let n: u32 = rng.gen();
let mut acc = Fr::zero();
{
let mut tmp = x.pow(&[n as u64]);
for mut coeff in a.iter_mut() {
coeff.mul_assign(&tmp);
acc.add_assign(&coeff);
tmp.mul_assign(&x);
}
}
let first_power = x.pow(&[n as u64]);
let acc_parallel = mut_evaluate_at_consequitive_powers(&mut b[..], first_power, x);
assert_eq!(acc_parallel, acc);
assert!(a == b);
}

@ -479,7 +479,7 @@ fn test_sonic_mimc() {
use bellman::sonic::cs::Basic;
use bellman::sonic::sonic::AdaptorCircuit;
use bellman::sonic::helped::{create_proof, create_advice, create_aggregate, MultiVerifier};
use bellman::sonic::helped::{create_proof, create_advice, create_aggregate, MultiVerifier, create_advice_on_srs};
println!("creating proof");
let start = Instant::now();
@ -488,7 +488,7 @@ fn test_sonic_mimc() {
println!("creating advice");
let start = Instant::now();
let advice = create_advice::<Bls12, _, Basic>(&AdaptorCircuit(circuit.clone()), &proof, &srs);
let advice = create_advice_on_srs::<Bls12, _, Basic>(&AdaptorCircuit(circuit.clone()), &proof, &srs);
println!("done in {:?}", start.elapsed());
println!("creating aggregate for {} proofs", samples);
@ -578,7 +578,7 @@ fn test_inputs_into_sonic_mimc() {
use bellman::sonic::cs::Basic;
use bellman::sonic::sonic::AdaptorCircuit;
use bellman::sonic::helped::{create_proof, get_circuit_parameters, create_advice, create_aggregate, MultiVerifier};
use bellman::sonic::helped::{create_proof, get_circuit_parameters, create_advice, create_aggregate, MultiVerifier, create_advice_on_srs};
let info = get_circuit_parameters::<Bn256, _>(circuit.clone()).expect("Must get circuit info");
println!("{:?}", info);
@ -590,7 +590,7 @@ fn test_inputs_into_sonic_mimc() {
println!("creating advice");
let start = Instant::now();
let advice = create_advice::<Bn256, _, Basic>(&AdaptorCircuit(circuit.clone()), &proof, &srs);
let advice = create_advice_on_srs::<Bn256, _, Basic>(&AdaptorCircuit(circuit.clone()), &proof, &srs);
println!("done in {:?}", start.elapsed());
println!("creating aggregate for {} proofs", samples);