diff --git a/.github/workflows/ci_main.yml b/.github/workflows/ci_main.yml index 400bff09..df387bc3 100644 --- a/.github/workflows/ci_main.yml +++ b/.github/workflows/ci_main.yml @@ -59,7 +59,7 @@ jobs: command: tarpaulin args: --all-features --timeout 600 --out Xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3.1.0 + uses: codecov/codecov-action@v3.1.4 doc-links: name: Intra-doc links diff --git a/Cargo.lock b/Cargo.lock index ec56f19b..1f0b21ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -220,9 +220,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.73" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -526,7 +529,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 1.0.91", ] [[package]] @@ -537,7 +540,7 @@ checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ "darling_core", "quote", - "syn", + "syn 1.0.91", ] [[package]] @@ -569,7 +572,7 @@ dependencies = [ "derive_builder_core", "proc-macro2", "quote", - "syn", + "syn 1.0.91", ] [[package]] @@ -581,7 +584,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 1.0.91", ] [[package]] @@ -655,6 +658,18 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "fam" +version = "0.1.0" +dependencies = [ + "cc", + "ff", + "group", + "halo2curves 0.3.1 (git+https://github.com/scroll-tech/halo2curves.git?branch=0.3.1-derive-serde)", + "rayon", + "serde", +] + [[package]] name = "fastrand" version = "1.7.0" @@ -834,7 +849,7 @@ checksum = "729f9bd3449d77e7831a18abfb7ba2f99ee813dfd15b8c2167c9a54ba20aa99d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.91", ] [[package]] @@ -882,6 +897,7 @@ dependencies = [ "criterion", "crossbeam", "env_logger", + "fam", "ff", "getrandom", "group", @@ -1460,11 +1476,11 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "proc-macro2" -version = "1.0.37" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -1510,9 +1526,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.18" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1564,21 +1580,19 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -1730,9 +1744,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.136" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] @@ -1749,13 +1763,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.31", ] [[package]] @@ -1872,6 +1886,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "syn" +version = "2.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tabbycat" version = "0.1.2" @@ -1938,7 +1963,7 @@ checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.91", ] [[package]] @@ -1982,7 +2007,7 @@ checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.91", ] [[package]] @@ -2024,6 +2049,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + [[package]] name = "unicode-width" version = "0.1.9" @@ -2095,7 +2126,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 1.0.91", "wasm-bindgen-shared", ] @@ -2117,7 +2148,7 @@ checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.91", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/halo2_proofs/Cargo.toml b/halo2_proofs/Cargo.toml index 9afcc0ca..3857e92a 100644 --- a/halo2_proofs/Cargo.toml +++ b/halo2_proofs/Cargo.toml @@ -58,6 +58,7 @@ cfg-if = "0.1" poseidon = { git = "https://github.com/scroll-tech/poseidon.git", branch = "scroll-dev-0220" } num-integer = "0.1" num-bigint = { version = "0.4", features = ["rand"] } +fam = { path = "../../../dompute/fam-rs" } crossbeam = "0.8.0" # Developer tooling dependencies @@ -74,7 +75,9 @@ assert_matches = "1.5" criterion = "0.3" gumdrop = "0.8" proptest = "1" -rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } +rand_core = { version = "0.6", default-features = false, features = [ + "getrandom", +] } [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies] getrandom = { version = "0.2", features = ["js"] } @@ -90,6 +93,7 @@ gwc = [] parallel_syn = [] phase-check = [] profile = ["ark-std/print-trace"] +mock-batch-inv = [] [lib] bench = false diff --git a/halo2_proofs/src/arithmetic.rs b/halo2_proofs/src/arithmetic.rs index c06575b5..dfd20633 100644 --- a/halo2_proofs/src/arithmetic.rs +++ b/halo2_proofs/src/arithmetic.rs @@ -12,95 +12,7 @@ pub use halo2curves::{CurveAffine, CurveExt, FieldExt, Group}; pub const SPARSE_TWIDDLE_DEGREE: u32 = 10; -fn multiexp_serial(coeffs: &[C::Scalar], bases: &[C], acc: &mut C::Curve) { - let coeffs: Vec<_> = coeffs.iter().map(|a| a.to_repr()).collect(); - - let c = if bases.len() < 4 { - 1 - } else if bases.len() < 32 { - 3 - } else { - (f64::from(bases.len() as u32)).ln().ceil() as usize - }; - - fn get_at(segment: usize, c: usize, bytes: &F::Repr) -> usize { - let skip_bits = segment * c; - let skip_bytes = skip_bits / 8; - - if skip_bytes >= 32 { - return 0; - } - - let mut v = [0; 8]; - for (v, o) in v.iter_mut().zip(bytes.as_ref()[skip_bytes..].iter()) { - *v = *o; - } - - let mut tmp = u64::from_le_bytes(v); - tmp >>= skip_bits - (skip_bytes * 8); - tmp = tmp % (1 << c); - - tmp as usize - } - - let segments = (256 / c) + 1; - - for current_segment in (0..segments).rev() { - for _ in 0..c { - *acc = acc.double(); - } - - #[derive(Clone, Copy)] - enum Bucket { - None, - Affine(C), - Projective(C::Curve), - } - - impl Bucket { - fn add_assign(&mut self, other: &C) { - *self = match *self { - Bucket::None => Bucket::Affine(*other), - Bucket::Affine(a) => Bucket::Projective(a + *other), - Bucket::Projective(mut a) => { - a += *other; - Bucket::Projective(a) - } - } - } - - fn add(self, mut other: C::Curve) -> C::Curve { - match self { - Bucket::None => other, - Bucket::Affine(a) => { - other += a; - other - } - Bucket::Projective(a) => other + &a, - } - } - } - - let mut buckets: Vec> = vec![Bucket::None; (1 << c) - 1]; - - for (coeff, base) in coeffs.iter().zip(bases.iter()) { - let coeff = get_at::(current_segment, c, coeff); - if coeff != 0 { - buckets[coeff - 1].add_assign(base); - } - } - - // Summation by parts - // e.g. 3a + 2b + 1c = a + - // (a) + b + - // ((a) + b) + c - let mut running_sum = C::Curve::identity(); - for exp in buckets.into_iter().rev() { - running_sum = exp.add(running_sum); - *acc = *acc + &running_sum; - } - } -} +pub use fam::arithmetic::{best_fft, best_multiexp}; /// Performs a small multi-exponentiation operation. /// Uses the double-and-add algorithm with doublings shared across points. @@ -126,297 +38,6 @@ pub fn small_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::C acc } -/// Performs a multi-exponentiation operation. -/// -/// This function will panic if coeffs and bases have a different length. -/// -/// This will use multithreading if beneficial. -pub fn best_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve { - assert_eq!(coeffs.len(), bases.len()); - - let num_threads = multicore::current_num_threads(); - if coeffs.len() > num_threads { - let chunk = coeffs.len() / num_threads; - let num_chunks = coeffs.chunks(chunk).len(); - let mut results = vec![C::Curve::identity(); num_chunks]; - multicore::scope(|scope| { - let chunk = coeffs.len() / num_threads; - - for ((coeffs, bases), acc) in coeffs - .chunks(chunk) - .zip(bases.chunks(chunk)) - .zip(results.iter_mut()) - { - scope.spawn(move |_| { - multiexp_serial(coeffs, bases, acc); - }); - } - }); - results.iter().fold(C::Curve::identity(), |a, b| a + b) - } else { - let mut acc = C::Curve::identity(); - multiexp_serial(coeffs, bases, &mut acc); - acc - } -} - -/// Performs a radix-$2$ Fast-Fourier Transformation (FFT) on a vector of size -/// $n = 2^k$, when provided `log_n` = $k$ and an element of multiplicative -/// order $n$ called `omega` ($\omega$). The result is that the vector `a`, when -/// interpreted as the coefficients of a polynomial of degree $n - 1$, is -/// transformed into the evaluations of this polynomial at each of the $n$ -/// distinct powers of $\omega$. This transformation is invertible by providing -/// $\omega^{-1}$ in place of $\omega$ and dividing each resulting field element -/// by $n$. -/// -/// This will use multithreading if beneficial. -pub fn best_fft(a: &mut [G], omega: G::Scalar, log_n: u32) { - let threads = multicore::current_num_threads(); - let log_split = log2_floor(threads) as usize; - let n = a.len() as usize; - let sub_n = n >> log_split; - let split_m = 1 << log_split; - - if sub_n < split_m { - serial_fft(a, omega, log_n); - } else { - parallel_fft(a, omega, log_n); - } -} - -fn bitreverse(mut n: usize, l: usize) -> usize { - let mut r = 0; - for _ in 0..l { - r = (r << 1) | (n & 1); - n >>= 1; - } - r -} - -fn serial_fft(a: &mut [G], omega: G::Scalar, log_n: u32) { - let n = a.len() as u32; - assert_eq!(n, 1 << log_n); - - for k in 0..n as usize { - let rk = bitreverse(k, log_n as usize); - if k < rk { - a.swap(rk as usize, k as usize); - } - } - - let mut m = 1; - for _ in 0..log_n { - let w_m = omega.pow_vartime([u64::from(n / (2 * m)), 0, 0, 0]); - - let mut k = 0; - while k < n { - let mut w = G::Scalar::one(); - for j in 0..m { - let mut t = a[(k + j + m) as usize]; - t.group_scale(&w); - a[(k + j + m) as usize] = a[(k + j) as usize]; - a[(k + j + m) as usize].group_sub(&t); - a[(k + j) as usize].group_add(&t); - w *= &w_m; - } - - k += 2 * m; - } - - m *= 2; - } -} - -fn serial_split_fft( - a: &mut [G], - twiddle_lut: &[G::Scalar], - twiddle_scale: usize, - log_n: u32, -) { - let n = a.len() as u32; - assert_eq!(n, 1 << log_n); - - let mut m = 1; - for _ in 0..log_n { - let omega_idx = twiddle_scale * n as usize / (2 * m as usize); // 1/2, 1/4, 1/8, ... - let low_idx = omega_idx % (1 << SPARSE_TWIDDLE_DEGREE); - let high_idx = omega_idx >> SPARSE_TWIDDLE_DEGREE; - let mut w_m = twiddle_lut[low_idx]; - if high_idx > 0 { - w_m = w_m * twiddle_lut[(1 << SPARSE_TWIDDLE_DEGREE) + high_idx]; - } - - let mut k = 0; - while k < n { - let mut w = G::Scalar::one(); - for j in 0..m { - let mut t = a[(k + j + m) as usize]; - t.group_scale(&w); - a[(k + j + m) as usize] = a[(k + j) as usize]; - a[(k + j + m) as usize].group_sub(&t); - a[(k + j) as usize].group_add(&t); - w *= &w_m; - } - - k += 2 * m; - } - - m *= 2; - } -} - -fn split_radix_fft( - tmp: &mut [G], - a: &[G], - twiddle_lut: &[G::Scalar], - n: usize, - sub_fft_offset: usize, - log_split: usize, -) { - let split_m = 1 << log_split; - let sub_n = n >> log_split; - - // we use out-place bitreverse here, split_m <= num_threads, so the buffer spase is small - // and it's is good for data locality - let mut t1 = vec![G::group_zero(); split_m]; - // if unsafe code is allowed, a 10% performance improvement can be achieved - // let mut t1: Vec = Vec::with_capacity(split_m as usize); - // unsafe{ t1.set_len(split_m as usize); } - for i in 0..split_m { - t1[bitreverse(i, log_split)] = a[(i * sub_n + sub_fft_offset)]; - } - serial_split_fft(&mut t1, twiddle_lut, sub_n, log_split as u32); - - let sparse_degree = SPARSE_TWIDDLE_DEGREE; - let omega_idx = sub_fft_offset as usize; - let low_idx = omega_idx % (1 << sparse_degree); - let high_idx = omega_idx >> sparse_degree; - let mut omega = twiddle_lut[low_idx]; - if high_idx > 0 { - omega = omega * twiddle_lut[(1 << sparse_degree) + high_idx]; - } - let mut w_m = G::Scalar::one(); - for i in 0..split_m { - t1[i].group_scale(&w_m); - tmp[i] = t1[i]; - w_m = w_m * omega; - } -} - -pub fn generate_twiddle_lookup_table( - omega: F, - log_n: u32, - sparse_degree: u32, - with_last_level: bool, -) -> Vec { - let without_last_level = !with_last_level; - let is_lut_len_large = sparse_degree > log_n; - - // dense - if is_lut_len_large { - let mut twiddle_lut = vec![F::zero(); (1 << log_n) as usize]; - parallelize(&mut twiddle_lut, |twiddle_lut, start| { - let mut w_n = omega.pow_vartime([start as u64, 0, 0, 0]); - for twiddle_lut in twiddle_lut.iter_mut() { - *twiddle_lut = w_n; - w_n = w_n * omega; - } - }); - return twiddle_lut; - } - - // sparse - let low_degree_lut_len = 1 << sparse_degree; - let high_degree_lut_len = 1 << (log_n - sparse_degree - without_last_level as u32); - let mut twiddle_lut = vec![F::zero(); (low_degree_lut_len + high_degree_lut_len) as usize]; - parallelize( - &mut twiddle_lut[..low_degree_lut_len], - |twiddle_lut, start| { - let mut w_n = omega.pow_vartime([start as u64, 0, 0, 0]); - for twiddle_lut in twiddle_lut.iter_mut() { - *twiddle_lut = w_n; - w_n = w_n * omega; - } - }, - ); - let high_degree_omega = omega.pow_vartime([(1 << sparse_degree) as u64, 0, 0, 0]); - parallelize( - &mut twiddle_lut[low_degree_lut_len..], - |twiddle_lut, start| { - let mut w_n = high_degree_omega.pow_vartime([start as u64, 0, 0, 0]); - for twiddle_lut in twiddle_lut.iter_mut() { - *twiddle_lut = w_n; - w_n = w_n * high_degree_omega; - } - }, - ); - twiddle_lut -} - -pub fn parallel_fft(a: &mut [G], omega: G::Scalar, log_n: u32) { - let n = a.len() as usize; - assert_eq!(n, 1 << log_n); - - let log_split = log2_floor(multicore::current_num_threads()) as usize; - let split_m = 1 << log_split; - let sub_n = n >> log_split as usize; - let twiddle_lut = generate_twiddle_lookup_table(omega, log_n, SPARSE_TWIDDLE_DEGREE, true); - - // split fft - let mut tmp = vec![G::group_zero(); n]; - // if unsafe code is allowed, a 10% performance improvement can be achieved - // let mut tmp: Vec = Vec::with_capacity(n); - // unsafe{ tmp.set_len(n); } - multicore::scope(|scope| { - let a = &*a; - let twiddle_lut = &*twiddle_lut; - for (chunk_idx, tmp) in tmp.chunks_mut(sub_n).enumerate() { - scope.spawn(move |_| { - let split_fft_offset = (chunk_idx * sub_n) >> log_split; - for (i, tmp) in tmp.chunks_mut(split_m).enumerate() { - let split_fft_offset = split_fft_offset + i; - split_radix_fft(tmp, a, twiddle_lut, n, split_fft_offset, log_split); - } - }); - } - }); - - // shuffle - parallelize(a, |a, start| { - for (idx, a) in a.iter_mut().enumerate() { - let idx = start + idx; - let i = idx / sub_n; - let j = idx % sub_n; - *a = tmp[j * split_m + i]; - } - }); - - // sub fft - let new_omega = omega.pow_vartime([split_m as u64, 0, 0, 0]); - multicore::scope(|scope| { - for a in a.chunks_mut(sub_n) { - scope.spawn(move |_| { - serial_fft(a, new_omega, log_n - log_split as u32); - }); - } - }); - - // copy & unshuffle - let mask = (1 << log_split) - 1; - parallelize(&mut tmp, |tmp, start| { - for (idx, tmp) in tmp.iter_mut().enumerate() { - let idx = start + idx; - *tmp = a[idx]; - } - }); - parallelize(a, |a, start| { - for (idx, a) in a.iter_mut().enumerate() { - let idx = start + idx; - *a = tmp[sub_n * (idx & mask) + (idx >> log_split)]; - } - }); -} - /// Convert coefficient bases group elements to lagrange basis by inverse FFT. pub fn g_to_lagrange(g_projective: Vec, k: u32) -> Vec { let n_inv = C::Scalar::TWO_INV.pow_vartime([k as u64, 0, 0, 0]); diff --git a/halo2_proofs/src/dev.rs b/halo2_proofs/src/dev.rs index 7f420149..94d7da90 100644 --- a/halo2_proofs/src/dev.rs +++ b/halo2_proofs/src/dev.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use std::time::{Duration, Instant}; use blake2b_simd::blake2b; -use ff::Field; +use ff::{BatchInvert, Field}; use crate::plonk::permutation::keygen::Assembly; use crate::plonk::sealed::SealedPhase; @@ -97,16 +97,143 @@ impl Region { } /// The value of a particular cell within the circuit. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug)] pub enum CellValue { /// An unassigned cell. Unassigned, /// A cell that has been assigned a value. Assigned(F), + /// A value stored as a fraction to enable batch inversion. + #[cfg(feature = "mock-batch-inv")] + Rational(F, F), /// A unique poisoned cell. Poison(usize), } +impl PartialEq for CellValue { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Unassigned, Self::Unassigned) => true, + (Self::Assigned(a), Self::Assigned(b)) => a == b, + #[cfg(feature = "mock-batch-inv")] + (Self::Rational(a, b), Self::Rational(c, d)) => *a * d == *b * c, + #[cfg(feature = "mock-batch-inv")] + (Self::Assigned(a), Self::Rational(n, d)) => *a * *d == *n, + #[cfg(feature = "mock-batch-inv")] + (Self::Rational(n, d), Self::Assigned(a)) => *a * *d == *n, + (Self::Poison(a), Self::Poison(b)) => a == b, + _ => false, + } + } +} + +#[cfg(feature = "mock-batch-inv")] +impl CellValue { + /// Returns the numerator. + pub fn numerator(&self) -> Option { + match self { + Self::Rational(numerator, _) => Some(*numerator), + _ => None, + } + } + + /// Returns the denominator + pub fn denominator(&self) -> Option { + match self { + Self::Rational(_, denominator) => Some(*denominator), + _ => None, + } + } +} + +#[cfg(feature = "mock-batch-inv")] +impl From> for CellValue { + fn from(value: Assigned) -> Self { + match value { + Assigned::Zero => CellValue::Unassigned, + Assigned::Trivial(value) => CellValue::Assigned(value), + Assigned::Rational(numerator, denominator) => { + CellValue::Rational(numerator, denominator) + } + } + } +} + +#[cfg(feature = "mock-batch-inv")] +fn calculate_assigned_values( + cell_values: &mut [CellValue], + inv_denoms: &[Option], +) { + assert_eq!(inv_denoms.len(), cell_values.len()); + for (value, inv_den) in cell_values.iter_mut().zip(inv_denoms.iter()) { + // if numerator and denominator exist, calculate the assigned value + // otherwise, return the original CellValue + *value = match value { + CellValue::Rational(numerator, _) => CellValue::Assigned(*numerator * inv_den.unwrap()), + _ => *value, + }; + } +} + +#[cfg(feature = "mock-batch-inv")] +fn batch_invert_cellvalues(cell_values: &mut [Vec>]) { + let mut denominators: Vec<_> = cell_values + .iter() + .map(|f| { + f.par_iter() + .map(|value| value.denominator()) + .collect::>() + }) + .collect(); + let denominators_len: usize = denominators.iter().map(|f| f.len()).sum(); + + let mut_denominators = denominators + .iter_mut() + .flat_map(|f| { + f.iter_mut() + // If the denominator is trivial, we can skip it, reducing the + // size of the batch inversion. + .filter_map(|d| d.as_mut()) + }) + .collect::>(); + + log::info!( + "num of denominators: {} / {}", + mut_denominators.len(), + denominators_len + ); + if mut_denominators.is_empty() { + return; + } + + let num_threads = rayon::current_num_threads(); + let chunk_size = (mut_denominators.len() + num_threads - 1) / num_threads; + let mut_denominators = + mut_denominators + .into_iter() + .enumerate() + .fold(vec![vec![]], |mut acc, (i, denom)| { + let len = acc.len(); + if i % chunk_size == 0 { + acc.push(vec![denom]) + } else { + acc[len - 1].push(denom); + } + acc + }); + rayon::scope(|scope| { + for chunk in mut_denominators { + scope.spawn(|_| { + chunk.batch_invert(); + }); + } + }); + + for (cell_values, inv_denoms) in cell_values.iter_mut().zip(denominators.iter()) { + calculate_assigned_values(cell_values, inv_denoms); + } +} + /// A value within an expression. #[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] enum Value { @@ -120,6 +247,8 @@ impl From> for Value { // Cells that haven't been explicitly assigned to, default to zero. CellValue::Unassigned => Value::Real(F::zero()), CellValue::Assigned(v) => Value::Real(v), + #[cfg(feature = "mock-batch-inv")] + CellValue::Rational(n, d) => Value::Real(n * d.invert().unwrap()), CellValue::Poison(_) => Value::Poison, } } @@ -540,7 +669,7 @@ impl<'a, F: Field + Group> Assignment for MockProver<'a, F> { fn assign_advice( &mut self, - _: A, + anno: A, column: Column, row: usize, to: V, @@ -551,6 +680,7 @@ impl<'a, F: Field + Group> Assignment for MockProver<'a, F> { A: FnOnce() -> AR, AR: Into, { + // column of 2nd phase does not need to be assigned when synthesis at 1st phase if self.current_phase.0 < column.column_type().phase.0 { return Ok(()); } @@ -578,7 +708,25 @@ impl<'a, F: Field + Group> Assignment for MockProver<'a, F> { .or_default(); } - let assigned = CellValue::Assigned(to().into_field().evaluate().assign()?); + let advice_anno = anno().into(); + #[cfg(not(feature = "mock-batch-inv"))] + let val_res = to().into_field().evaluate().assign(); + #[cfg(feature = "mock-batch-inv")] + let val_res = to().into_field().assign(); + if val_res.is_err() { + log::debug!( + "[{}] assign to advice {:?} at row {} failed at phase {:?}", + advice_anno, + column, + row, + self.current_phase + ); + } + #[cfg(not(feature = "mock-batch-inv"))] + let assigned = CellValue::Assigned(val_res?); + #[cfg(feature = "mock-batch-inv")] + let assigned = CellValue::from(val_res?); + *self .advice .get_mut(column.index()) @@ -641,15 +789,23 @@ impl<'a, F: Field + Group> Assignment for MockProver<'a, F> { .or_default(); } - let fix_cell = self + let assigned = self .fixed .get_mut(column.index()) .and_then(|v| v.get_mut(row - self.rw_rows.start)) .ok_or(Error::BoundsFailure); - if fix_cell.is_err() { + if assigned.is_err() { println!("fix cell is none: {}, row: {}", column.index(), row); } - *fix_cell? = CellValue::Assigned(to().into_field().evaluate().assign()?); + + #[cfg(not(feature = "mock-batch-inv"))] + { + *assigned? = CellValue::Assigned(to().into_field().evaluate().assign()?); + } + #[cfg(feature = "mock-batch-inv")] + { + *assigned? = CellValue::from(to().into_field().assign()?); + } Ok(()) } @@ -704,10 +860,11 @@ impl<'a, F: Field + Group> Assignment for MockProver<'a, F> { } fn get_challenge(&self, challenge: Challenge) -> circuit::Value { - match self.challenges.get(challenge.index()) { - None => circuit::Value::unknown(), - Some(v) => circuit::Value::known(*v), + if self.current_phase <= challenge.phase { + return circuit::Value::unknown(); } + + circuit::Value::known(self.challenges[challenge.index()]) } fn push_namespace(&mut self, _: N) @@ -829,12 +986,16 @@ impl<'a, F: FieldExt> MockProver<'a, F> { for current_phase in prover.cs.phases() { prover.current_phase = current_phase; prover.advice_prev = last_advice; - ConcreteCircuit::FloorPlanner::synthesize( + let syn_res = ConcreteCircuit::FloorPlanner::synthesize( &mut prover, circuit, config.clone(), constants.clone(), - )?; + ); + if syn_res.is_err() { + log::error!("mock prover syn failed at phase {:?}", current_phase); + } + syn_res?; for (index, phase) in prover.cs.challenge_phase.iter().enumerate() { if current_phase == *phase { @@ -860,6 +1021,11 @@ impl<'a, F: FieldExt> MockProver<'a, F> { panic!("wrong phase assignment"); } } + if current_phase.0 < prover.cs.max_phase() { + // only keep the regions that we got during last phase's synthesis + // as we do not need to verify these regions. + prover.regions.clear(); + } last_advice = prover.advice_vec.as_ref().clone(); } } @@ -875,6 +1041,18 @@ impl<'a, F: FieldExt> MockProver<'a, F> { .cs .compress_selectors(prover.selectors_vec.as_ref().clone()); prover.cs = cs; + + // batch invert + #[cfg(feature = "mock-batch-inv")] + { + batch_invert_cellvalues( + Arc::get_mut(&mut prover.advice_vec).expect("get_mut prover.advice_vec"), + ); + batch_invert_cellvalues( + Arc::get_mut(&mut prover.fixed_vec).expect("get_mut prover.fixed_vec"), + ); + } + // add selector polys Arc::get_mut(&mut prover.fixed_vec) .expect("get_mut prover.fixed_vec") .extend(selector_polys.into_iter().map(|poly| { @@ -1308,6 +1486,7 @@ impl<'a, F: FieldExt> MockProver<'a, F> { // Check that within each region, all cells used in instantiated gates have been // assigned to. + log::debug!("regions.len() = {}", self.regions.len()); let selector_errors = self.regions.iter().enumerate().flat_map(|(r_i, r)| { r.enabled_selectors.iter().flat_map(move |(selector, at)| { // Find the gates enabled by this selector diff --git a/halo2_proofs/src/plonk/circuit.rs b/halo2_proofs/src/plonk/circuit.rs index b64e1034..8f1ec3da 100644 --- a/halo2_proofs/src/plonk/circuit.rs +++ b/halo2_proofs/src/plonk/circuit.rs @@ -509,7 +509,7 @@ impl TableColumn { #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Challenge { index: usize, - phase: sealed::Phase, + pub(crate) phase: sealed::Phase, } impl Challenge { diff --git a/halo2_proofs/src/plonk/evaluation.rs b/halo2_proofs/src/plonk/evaluation.rs index c9c9a5cb..d3a63eb0 100644 --- a/halo2_proofs/src/plonk/evaluation.rs +++ b/halo2_proofs/src/plonk/evaluation.rs @@ -28,157 +28,16 @@ use std::{ use super::{ConstraintSystem, Expression}; +pub use fam::{ + graph::GraphEvaluator, + value_source::{Calculation, CalculationInfo, ValueSource}, +}; + /// Return the index in the polynomial of size `isize` after rotation `rot`. fn get_rotation_idx(idx: usize, rot: i32, rot_scale: i32, isize: i32) -> usize { (((idx as i32) + (rot * rot_scale)).rem_euclid(isize)) as usize } -/// Value used in a calculation -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)] -pub enum ValueSource { - /// This is a constant value - Constant(usize), - /// This is an intermediate value - Intermediate(usize), - /// This is a fixed column - Fixed(usize, usize), - /// This is an advice (witness) column - Advice(usize, usize), - /// This is an instance (external) column - Instance(usize, usize), - /// This is a challenge - Challenge(usize), - /// beta - Beta(), - /// gamma - Gamma(), - /// theta - Theta(), - /// y - Y(), - /// Previous value - PreviousValue(), -} - -impl Default for ValueSource { - fn default() -> Self { - ValueSource::Constant(0) - } -} - -impl ValueSource { - /// Get the value for this source - pub fn get( - &self, - rotations: &[usize], - constants: &[F], - intermediates: &[F], - fixed_values: &[Polynomial], - advice_values: &[Polynomial], - instance_values: &[Polynomial], - challenges: &[F], - beta: &F, - gamma: &F, - theta: &F, - y: &F, - previous_value: &F, - ) -> F { - match self { - ValueSource::Constant(idx) => constants[*idx], - ValueSource::Intermediate(idx) => intermediates[*idx], - ValueSource::Fixed(column_index, rotation) => { - fixed_values[*column_index][rotations[*rotation]] - } - ValueSource::Advice(column_index, rotation) => { - advice_values[*column_index][rotations[*rotation]] - } - ValueSource::Instance(column_index, rotation) => { - instance_values[*column_index][rotations[*rotation]] - } - ValueSource::Challenge(index) => challenges[*index], - ValueSource::Beta() => *beta, - ValueSource::Gamma() => *gamma, - ValueSource::Theta() => *theta, - ValueSource::Y() => *y, - ValueSource::PreviousValue() => *previous_value, - } - } -} - -/// Calculation -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Calculation { - /// This is an addition - Add(ValueSource, ValueSource), - /// This is a subtraction - Sub(ValueSource, ValueSource), - /// This is a product - Mul(ValueSource, ValueSource), - /// This is a square - Square(ValueSource), - /// This is a double - Double(ValueSource), - /// This is a negation - Negate(ValueSource), - /// This is Horner's rule: `val = a; val = val * c + b[]` - Horner(ValueSource, Vec, ValueSource), - /// This is a simple assignment - Store(ValueSource), -} - -impl Calculation { - /// Get the resulting value of this calculation - pub fn evaluate( - &self, - rotations: &[usize], - constants: &[F], - intermediates: &[F], - fixed_values: &[Polynomial], - advice_values: &[Polynomial], - instance_values: &[Polynomial], - challenges: &[F], - beta: &F, - gamma: &F, - theta: &F, - y: &F, - previous_value: &F, - ) -> F { - let get_value = |value: &ValueSource| { - value.get( - rotations, - constants, - intermediates, - fixed_values, - advice_values, - instance_values, - challenges, - beta, - gamma, - theta, - y, - previous_value, - ) - }; - match self { - Calculation::Add(a, b) => get_value(a) + get_value(b), - Calculation::Sub(a, b) => get_value(a) - get_value(b), - Calculation::Mul(a, b) => get_value(a) * get_value(b), - Calculation::Square(v) => get_value(v).square(), - Calculation::Double(v) => get_value(v).double(), - Calculation::Negate(v) => -get_value(v), - Calculation::Horner(start_value, parts, factor) => { - let factor = get_value(factor); - let mut value = get_value(start_value); - for part in parts.iter() { - value = value * factor + get_value(part); - } - value - } - Calculation::Store(v) => get_value(v), - } - } -} - /// Evaluator #[derive(Clone, Default, Debug)] pub struct Evaluator { @@ -188,19 +47,6 @@ pub struct Evaluator { pub lookups: Vec>, } -/// GraphEvaluator -#[derive(Clone, Debug)] -pub struct GraphEvaluator { - /// Constants - pub constants: Vec, - /// Rotations - pub rotations: Vec, - /// Calculations - pub calculations: Vec, - /// Number of intermediates - pub num_intermediates: usize, -} - /// EvaluationData #[derive(Default, Debug)] pub struct EvaluationData { @@ -210,15 +56,6 @@ pub struct EvaluationData { pub rotations: Vec, } -/// CaluclationInfo -#[derive(Clone, Debug)] -pub struct CalculationInfo { - /// Calculation - pub calculation: Calculation, - /// Target - pub target: usize, -} - impl Evaluator { /// Creates a new evaluation structure pub fn new(cs: &ConstraintSystem) -> Self { @@ -230,7 +67,7 @@ impl Evaluator { parts.extend( gate.polynomials() .iter() - .map(|poly| ev.custom_gates.add_expression(poly)), + .map(|poly| add_expression(&mut ev.custom_gates, poly)), ); } ev.custom_gates.add_calculation(Calculation::Horner( @@ -246,7 +83,7 @@ impl Evaluator { let mut evaluate_lc = |expressions: &Vec>| { let parts = expressions .iter() - .map(|expr| graph.add_expression(expr)) + .map(|expr| add_expression(&mut graph, expr)) .collect(); graph.add_calculation(Calculation::Horner( ValueSource::Constant(0), @@ -344,40 +181,28 @@ impl Evaluator { // Core expression evaluations let num_threads = multicore::current_num_threads(); - for (((advice, instance), lookups), permutation) in advice + for (round, (((advice, instance), lookups), permutation)) in advice .iter() .zip(instance.iter()) .zip(lookups.iter()) .zip(permutations.iter()) + .enumerate() { // Custom gates - multicore::scope(|scope| { - let chunk_size = (size + num_threads - 1) / num_threads; - for (thread_idx, values) in values.chunks_mut(chunk_size).enumerate() { - let start = thread_idx * chunk_size; - scope.spawn(move |_| { - let mut eval_data = self.custom_gates.instance(); - for (i, value) in values.iter_mut().enumerate() { - let idx = start + i; - *value = self.custom_gates.evaluate( - &mut eval_data, - fixed, - advice, - instance, - challenges, - &beta, - &gamma, - &theta, - &y, - value, - idx, - rot_scale, - isize, - ); - } - }); - } - }); + self.custom_gates.evaluate( + &mut values, + fixed, + advice, + instance, + &challenges, + &beta, + &gamma, + &theta, + &y, + rot_scale, + isize, + round, + ); // Permutations let sets = &permutation.sets; @@ -509,30 +334,29 @@ impl Evaluator { lookup.permuted_table_poly.clone(), current_extended_omega, ); + let mut table_values = vec![C::ScalarExt::zero(); values.len()]; + self.lookups[n].evaluate( + &mut table_values, + fixed, + advice, + instance, + &challenges, + &beta, + &gamma, + &theta, + &y, + rot_scale, + isize, + round, + ); // Lookup constraints parallelize(&mut values, |values, start| { let lookup_evaluator = &self.lookups[n]; - let mut eval_data = lookup_evaluator.instance(); for (i, value) in values.iter_mut().enumerate() { let idx = start + i; - let table_value = lookup_evaluator.evaluate( - &mut eval_data, - fixed, - advice, - instance, - challenges, - &beta, - &gamma, - &theta, - &y, - &C::ScalarExt::zero(), - idx, - rot_scale, - isize, - ); - + let table_value = table_values[idx]; let r_next = get_rotation_idx(idx, 1, rot_scale, isize); let r_prev = get_rotation_idx(idx, -1, rot_scale, isize); @@ -582,229 +406,272 @@ impl Evaluator { } } -impl Default for GraphEvaluator { - fn default() -> Self { - Self { - // Fixed positions to allow easy access - constants: vec![ - C::ScalarExt::zero(), - C::ScalarExt::one(), - C::ScalarExt::from(2u64), - ], - rotations: Vec::new(), - calculations: Vec::new(), - num_intermediates: 0, +/// Generates an optimized evaluation for the expression +fn add_expression( + graph: &mut GraphEvaluator, + expr: &Expression, +) -> ValueSource { + match expr { + Expression::Constant(scalar) => graph.add_constant(scalar), + Expression::Selector(_selector) => unreachable!(), + Expression::Fixed(query) => { + let rot_idx = graph.add_rotation(&query.rotation); + graph.add_calculation(Calculation::Store(ValueSource::Fixed( + query.column_index, + rot_idx, + ))) } - } -} - -impl GraphEvaluator { - /// Adds a rotation - fn add_rotation(&mut self, rotation: &Rotation) -> usize { - let position = self.rotations.iter().position(|&c| c == rotation.0); - match position { - Some(pos) => pos, - None => { - self.rotations.push(rotation.0); - self.rotations.len() - 1 - } + Expression::Advice(query) => { + let rot_idx = graph.add_rotation(&query.rotation); + graph.add_calculation(Calculation::Store(ValueSource::Advice( + query.column_index, + rot_idx, + ))) } - } - - /// Adds a constant - fn add_constant(&mut self, constant: &C::ScalarExt) -> ValueSource { - let position = self.constants.iter().position(|&c| c == *constant); - ValueSource::Constant(match position { - Some(pos) => pos, - None => { - self.constants.push(*constant); - self.constants.len() - 1 - } - }) - } - - /// Adds a calculation. - /// Currently does the simplest thing possible: just stores the - /// resulting value so the result can be reused when that calculation - /// is done multiple times. - fn add_calculation(&mut self, calculation: Calculation) -> ValueSource { - let existing_calculation = self - .calculations - .iter() - .find(|c| c.calculation == calculation); - match existing_calculation { - Some(existing_calculation) => ValueSource::Intermediate(existing_calculation.target), - None => { - let target = self.num_intermediates; - self.calculations.push(CalculationInfo { - calculation, - target, - }); - self.num_intermediates += 1; - ValueSource::Intermediate(target) - } + Expression::Instance(query) => { + let rot_idx = graph.add_rotation(&query.rotation); + graph.add_calculation(Calculation::Store(ValueSource::Instance( + query.column_index, + rot_idx, + ))) } - } - - /// Generates an optimized evaluation for the expression - fn add_expression(&mut self, expr: &Expression) -> ValueSource { - match expr { - Expression::Constant(scalar) => self.add_constant(scalar), - Expression::Selector(_selector) => unreachable!(), - Expression::Fixed(query) => { - let rot_idx = self.add_rotation(&query.rotation); - self.add_calculation(Calculation::Store(ValueSource::Fixed( - query.column_index, - rot_idx, - ))) - } - Expression::Advice(query) => { - let rot_idx = self.add_rotation(&query.rotation); - self.add_calculation(Calculation::Store(ValueSource::Advice( - query.column_index, - rot_idx, - ))) - } - Expression::Instance(query) => { - let rot_idx = self.add_rotation(&query.rotation); - self.add_calculation(Calculation::Store(ValueSource::Instance( - query.column_index, - rot_idx, - ))) + Expression::Challenge(challenge) => graph.add_calculation(Calculation::Store( + ValueSource::Challenge(challenge.index()), + )), + Expression::Negated(a) => match **a { + Expression::Constant(scalar) => graph.add_constant(&-scalar), + _ => { + let result_a = add_expression(graph, a); + match result_a { + ValueSource::Constant(0) => result_a, + _ => graph.add_calculation(Calculation::Negate(result_a)), + } } - Expression::Challenge(challenge) => self.add_calculation(Calculation::Store( - ValueSource::Challenge(challenge.index()), - )), - Expression::Negated(a) => match **a { - Expression::Constant(scalar) => self.add_constant(&-scalar), - _ => { - let result_a = self.add_expression(a); - match result_a { - ValueSource::Constant(0) => result_a, - _ => self.add_calculation(Calculation::Negate(result_a)), + }, + Expression::Sum(a, b) => { + // Undo subtraction stored as a + (-b) in expressions + match &**b { + Expression::Negated(b_int) => { + let result_a = add_expression(graph, a); + let result_b = add_expression(graph, b_int); + if result_a == ValueSource::Constant(0) { + graph.add_calculation(Calculation::Negate(result_b)) + } else if result_b == ValueSource::Constant(0) { + result_a + } else { + graph.add_calculation(Calculation::Sub(result_a, result_b)) } } - }, - Expression::Sum(a, b) => { - // Undo subtraction stored as a + (-b) in expressions - match &**b { - Expression::Negated(b_int) => { - let result_a = self.add_expression(a); - let result_b = self.add_expression(b_int); - if result_a == ValueSource::Constant(0) { - self.add_calculation(Calculation::Negate(result_b)) - } else if result_b == ValueSource::Constant(0) { - result_a - } else { - self.add_calculation(Calculation::Sub(result_a, result_b)) - } - } - _ => { - let result_a = self.add_expression(a); - let result_b = self.add_expression(b); - if result_a == ValueSource::Constant(0) { - result_b - } else if result_b == ValueSource::Constant(0) { - result_a - } else if result_a <= result_b { - self.add_calculation(Calculation::Add(result_a, result_b)) - } else { - self.add_calculation(Calculation::Add(result_b, result_a)) - } + _ => { + let result_a = add_expression(graph, a); + let result_b = add_expression(graph, b); + if result_a == ValueSource::Constant(0) { + result_b + } else if result_b == ValueSource::Constant(0) { + result_a + } else if result_a <= result_b { + graph.add_calculation(Calculation::Add(result_a, result_b)) + } else { + graph.add_calculation(Calculation::Add(result_b, result_a)) } } } - Expression::Product(a, b) => { - let result_a = self.add_expression(a); - let result_b = self.add_expression(b); - if result_a == ValueSource::Constant(0) || result_b == ValueSource::Constant(0) { - ValueSource::Constant(0) - } else if result_a == ValueSource::Constant(1) { - result_b - } else if result_b == ValueSource::Constant(1) { - result_a - } else if result_a == ValueSource::Constant(2) { - self.add_calculation(Calculation::Double(result_b)) - } else if result_b == ValueSource::Constant(2) { - self.add_calculation(Calculation::Double(result_a)) - } else if result_a == result_b { - self.add_calculation(Calculation::Square(result_a)) - } else if result_a <= result_b { - self.add_calculation(Calculation::Mul(result_a, result_b)) - } else { - self.add_calculation(Calculation::Mul(result_b, result_a)) - } - } - Expression::Scaled(a, f) => { - if *f == C::ScalarExt::zero() { - ValueSource::Constant(0) - } else if *f == C::ScalarExt::one() { - self.add_expression(a) - } else { - let cst = self.add_constant(f); - let result_a = self.add_expression(a); - self.add_calculation(Calculation::Mul(result_a, cst)) - } - } } - } - - /// Creates a new evaluation structure - pub fn instance(&self) -> EvaluationData { - EvaluationData { - intermediates: vec![C::ScalarExt::zero(); self.num_intermediates], - rotations: vec![0usize; self.rotations.len()], - } - } - - pub fn evaluate( - &self, - data: &mut EvaluationData, - fixed: &[Polynomial], - advice: &[Polynomial], - instance: &[Polynomial], - challenges: &[C::ScalarExt], - beta: &C::ScalarExt, - gamma: &C::ScalarExt, - theta: &C::ScalarExt, - y: &C::ScalarExt, - previous_value: &C::ScalarExt, - idx: usize, - rot_scale: i32, - isize: i32, - ) -> C::ScalarExt { - // All rotation index values - for (rot_idx, rot) in self.rotations.iter().enumerate() { - data.rotations[rot_idx] = get_rotation_idx(idx, *rot, rot_scale, isize); - } - - // All calculations, with cached intermediate results - for calc in self.calculations.iter() { - data.intermediates[calc.target] = calc.calculation.evaluate( - &data.rotations, - &self.constants, - &data.intermediates, - fixed, - advice, - instance, - challenges, - beta, - gamma, - theta, - y, - previous_value, - ); + Expression::Product(a, b) => { + let result_a = add_expression(graph, a); + let result_b = add_expression(graph, b); + if result_a == ValueSource::Constant(0) || result_b == ValueSource::Constant(0) { + ValueSource::Constant(0) + } else if result_a == ValueSource::Constant(1) { + result_b + } else if result_b == ValueSource::Constant(1) { + result_a + } else if result_a == ValueSource::Constant(2) { + graph.add_calculation(Calculation::Double(result_b)) + } else if result_b == ValueSource::Constant(2) { + graph.add_calculation(Calculation::Double(result_a)) + } else if result_a == result_b { + graph.add_calculation(Calculation::Square(result_a)) + } else if result_a <= result_b { + graph.add_calculation(Calculation::Mul(result_a, result_b)) + } else { + graph.add_calculation(Calculation::Mul(result_b, result_a)) + } } - - // Return the result of the last calculation (if any) - if let Some(calc) = self.calculations.last() { - data.intermediates[calc.target] - } else { - C::ScalarExt::zero() + Expression::Scaled(a, f) => { + if *f == C::ScalarExt::zero() { + ValueSource::Constant(0) + } else if *f == C::ScalarExt::one() { + add_expression(graph, a) + } else { + let cst = graph.add_constant(f); + let result_a = add_expression(graph, a); + graph.add_calculation(Calculation::Mul(result_a, cst)) + } } } } +// impl GraphEvaluator { +// /// Generates an optimized evaluation for the expression +// fn add_expression(&mut self, expr: &Expression) -> ValueSource { +// match expr { +// Expression::Constant(scalar) => self.add_constant(scalar), +// Expression::Selector(_selector) => unreachable!(), +// Expression::Fixed(query) => { +// let rot_idx = self.add_rotation(&query.rotation); +// self.add_calculation(Calculation::Store(ValueSource::Fixed( +// query.column_index, +// rot_idx, +// ))) +// } +// Expression::Advice(query) => { +// let rot_idx = self.add_rotation(&query.rotation); +// self.add_calculation(Calculation::Store(ValueSource::Advice( +// query.column_index, +// rot_idx, +// ))) +// } +// Expression::Instance(query) => { +// let rot_idx = self.add_rotation(&query.rotation); +// self.add_calculation(Calculation::Store(ValueSource::Instance( +// query.column_index, +// rot_idx, +// ))) +// } +// Expression::Challenge(challenge) => self.add_calculation(Calculation::Store( +// ValueSource::Challenge(challenge.index()), +// )), +// Expression::Negated(a) => match **a { +// Expression::Constant(scalar) => self.add_constant(&-scalar), +// _ => { +// let result_a = self.add_expression(a); +// match result_a { +// ValueSource::Constant(0) => result_a, +// _ => self.add_calculation(Calculation::Negate(result_a)), +// } +// } +// }, +// Expression::Sum(a, b) => { +// // Undo subtraction stored as a + (-b) in expressions +// match &**b { +// Expression::Negated(b_int) => { +// let result_a = self.add_expression(a); +// let result_b = self.add_expression(b_int); +// if result_a == ValueSource::Constant(0) { +// self.add_calculation(Calculation::Negate(result_b)) +// } else if result_b == ValueSource::Constant(0) { +// result_a +// } else { +// self.add_calculation(Calculation::Sub(result_a, result_b)) +// } +// } +// _ => { +// let result_a = self.add_expression(a); +// let result_b = self.add_expression(b); +// if result_a == ValueSource::Constant(0) { +// result_b +// } else if result_b == ValueSource::Constant(0) { +// result_a +// } else if result_a <= result_b { +// self.add_calculation(Calculation::Add(result_a, result_b)) +// } else { +// self.add_calculation(Calculation::Add(result_b, result_a)) +// } +// } +// } +// } +// Expression::Product(a, b) => { +// let result_a = self.add_expression(a); +// let result_b = self.add_expression(b); +// if result_a == ValueSource::Constant(0) || result_b == ValueSource::Constant(0) { +// ValueSource::Constant(0) +// } else if result_a == ValueSource::Constant(1) { +// result_b +// } else if result_b == ValueSource::Constant(1) { +// result_a +// } else if result_a == ValueSource::Constant(2) { +// self.add_calculation(Calculation::Double(result_b)) +// } else if result_b == ValueSource::Constant(2) { +// self.add_calculation(Calculation::Double(result_a)) +// } else if result_a == result_b { +// self.add_calculation(Calculation::Square(result_a)) +// } else if result_a <= result_b { +// self.add_calculation(Calculation::Mul(result_a, result_b)) +// } else { +// self.add_calculation(Calculation::Mul(result_b, result_a)) +// } +// } +// Expression::Scaled(a, f) => { +// if *f == C::ScalarExt::zero() { +// ValueSource::Constant(0) +// } else if *f == C::ScalarExt::one() { +// self.add_expression(a) +// } else { +// let cst = self.add_constant(f); +// let result_a = self.add_expression(a); +// self.add_calculation(Calculation::Mul(result_a, cst)) +// } +// } +// } +// } + +// /// Creates a new evaluation structure +// pub fn instance(&self) -> EvaluationData { +// EvaluationData { +// intermediates: vec![C::ScalarExt::zero(); self.num_intermediates], +// rotations: vec![0usize; self.rotations.len()], +// } +// } + +// pub fn evaluate( +// &self, +// data: &mut EvaluationData, +// fixed: &[Polynomial], +// advice: &[Polynomial], +// instance: &[Polynomial], +// challenges: &[C::ScalarExt], +// beta: &C::ScalarExt, +// gamma: &C::ScalarExt, +// theta: &C::ScalarExt, +// y: &C::ScalarExt, +// previous_value: &C::ScalarExt, +// idx: usize, +// rot_scale: i32, +// isize: i32, +// ) -> C::ScalarExt { +// // All rotation index values +// for (rot_idx, rot) in self.rotations.iter().enumerate() { +// data.rotations[rot_idx] = get_rotation_idx(idx, *rot, rot_scale, isize); +// } + +// // All calculations, with cached intermediate results +// for calc in self.calculations.iter() { +// data.intermediates[calc.target] = calc.calculation.evaluate( +// &data.rotations, +// &self.constants, +// &data.intermediates, +// fixed, +// advice, +// instance, +// challenges, +// beta, +// gamma, +// theta, +// y, +// previous_value, +// ); +// } + +// // Return the result of the last calculation (if any) +// if let Some(calc) = self.calculations.last() { +// data.intermediates[calc.target] +// } else { +// C::ScalarExt::zero() +// } +// } +// } + /// Simple evaluation of an expression pub fn evaluate( expression: &Expression, diff --git a/halo2_proofs/src/poly.rs b/halo2_proofs/src/poly.rs index 84089482..e71b0594 100644 --- a/halo2_proofs/src/poly.rs +++ b/halo2_proofs/src/poly.rs @@ -31,6 +31,7 @@ pub mod kzg; mod multiopen_test; pub use domain::*; +pub use fam::value_source::Rotation; pub use query::{ProverQuery, VerifierQuery}; pub use strategy::{Guard, VerificationStrategy}; @@ -303,26 +304,3 @@ impl<'a, F: Field, B: Basis> Sub for &'a Polynomial { res } } - -/// Describes the relative rotation of a vector. Negative numbers represent -/// reverse (leftmost) rotations and positive numbers represent forward (rightmost) -/// rotations. Zero represents no rotation. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Rotation(pub i32); - -impl Rotation { - /// The current location in the evaluation domain - pub fn cur() -> Rotation { - Rotation(0) - } - - /// The previous location in the evaluation domain - pub fn prev() -> Rotation { - Rotation(-1) - } - - /// The next location in the evaluation domain - pub fn next() -> Rotation { - Rotation(1) - } -} diff --git a/rust-toolchain b/rust-toolchain index af92bdd9..bf867e0a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.63.0 +nightly