diff --git a/halo2_proofs/examples/shuffle.rs b/halo2_proofs/examples/shuffle.rs index 08d16b9f27..9e2a36d3b7 100644 --- a/halo2_proofs/examples/shuffle.rs +++ b/halo2_proofs/examples/shuffle.rs @@ -12,7 +12,7 @@ use halo2_proofs::{ multiopen::{ProverIPA, VerifierIPA}, strategy::AccumulatorStrategy, }, - Rotation, VerificationStrategy, + VerificationStrategy, }, transcript::{ Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, @@ -63,29 +63,23 @@ impl MyConfig { // Second phase let z = meta.advice_column_in(SecondPhase); - meta.create_gate("z should start with 1", |meta| { - let q_first = meta.query_selector(q_first); - let z = meta.query_advice(z, Rotation::cur()); + meta.create_gate("z should start with 1", |_| { let one = Expression::Constant(F::ONE); - vec![q_first * (one - z)] + vec![q_first.expr() * (one - z.cur())] }); - meta.create_gate("z should end with 1", |meta| { - let q_last = meta.query_selector(q_last); - let z = meta.query_advice(z, Rotation::cur()); + meta.create_gate("z should end with 1", |_| { let one = Expression::Constant(F::ONE); - vec![q_last * (one - z)] + vec![q_last.expr() * (one - z.cur())] }); - meta.create_gate("z should have valid transition", |meta| { - let q_shuffle = meta.query_selector(q_shuffle); - let original = original.map(|advice| meta.query_advice(advice, Rotation::cur())); - let shuffled = shuffled.map(|advice| meta.query_advice(advice, Rotation::cur())); - let [theta, gamma] = [theta, gamma].map(|challenge| meta.query_challenge(challenge)); - let [z, z_w] = - [Rotation::cur(), Rotation::next()].map(|rotation| meta.query_advice(z, rotation)); + meta.create_gate("z should have valid transition", |_| { + let q_shuffle = q_shuffle.expr(); + let original = original.map(|advice| advice.cur()); + let shuffled = shuffled.map(|advice| advice.cur()); + let [theta, gamma] = [theta, gamma].map(|challenge| challenge.expr()); // Compress let original = original @@ -99,7 +93,7 @@ impl MyConfig { .reduce(|acc, a| acc * theta.clone() + a) .unwrap(); - vec![q_shuffle * (z * (original + gamma.clone()) - z_w * (shuffled + gamma))] + vec![q_shuffle * (z.cur() * (original + gamma.clone()) - z.next() * (shuffled + gamma))] }); Self { diff --git a/halo2_proofs/src/dev.rs b/halo2_proofs/src/dev.rs index 8aba9cb84b..76928a3359 100644 --- a/halo2_proofs/src/dev.rs +++ b/halo2_proofs/src/dev.rs @@ -871,7 +871,7 @@ impl + Ord> MockProver { &|scalar| Value::Real(scalar), &|_| panic!("virtual selectors are removed during optimization"), &|query| { - let query = self.cs.fixed_queries[query.index]; + let query = self.cs.fixed_queries[query.index.unwrap()]; let column_index = query.0.index(); let rotation = query.1 .0; self.fixed[column_index] @@ -879,7 +879,7 @@ impl + Ord> MockProver { .into() }, &|query| { - let query = self.cs.advice_queries[query.index]; + let query = self.cs.advice_queries[query.index.unwrap()]; let column_index = query.0.index(); let rotation = query.1 .0; self.advice[column_index] @@ -887,7 +887,7 @@ impl + Ord> MockProver { .into() }, &|query| { - let query = self.cs.instance_queries[query.index]; + let query = self.cs.instance_queries[query.index.unwrap()]; let column_index = query.0.index(); let rotation = query.1 .0; Value::Real( diff --git a/halo2_proofs/src/dev/failure.rs b/halo2_proofs/src/dev/failure.rs index f2fbe20562..2eae799339 100644 --- a/halo2_proofs/src/dev/failure.rs +++ b/halo2_proofs/src/dev/failure.rs @@ -70,9 +70,9 @@ impl FailureLocation { expression.evaluate( &|_| vec![], &|_| panic!("virtual selectors are removed during optimization"), - &|query| vec![cs.fixed_queries[query.index].0.into()], - &|query| vec![cs.advice_queries[query.index].0.into()], - &|query| vec![cs.instance_queries[query.index].0.into()], + &|query| vec![cs.fixed_queries[query.index.unwrap()].0.into()], + &|query| vec![cs.advice_queries[query.index.unwrap()].0.into()], + &|query| vec![cs.instance_queries[query.index.unwrap()].0.into()], &|_| vec![], &|a| a, &|mut a, mut b| { diff --git a/halo2_proofs/src/dev/failure/emitter.rs b/halo2_proofs/src/dev/failure/emitter.rs index e84ba8013e..edd61f3060 100644 --- a/halo2_proofs/src/dev/failure/emitter.rs +++ b/halo2_proofs/src/dev/failure/emitter.rs @@ -153,7 +153,7 @@ pub(super) fn expression_to_string( label.clone() } else if query.rotation.0 == 0 { // This is most likely a merged selector - format!("S{}", query.index) + format!("S{}", query.index.unwrap()) } else { // No idea how we'd get here... format!("F{}@{}", query.column_index, query.rotation.0) diff --git a/halo2_proofs/src/dev/util.rs b/halo2_proofs/src/dev/util.rs index d9c9a9d8fb..f09695a4a9 100644 --- a/halo2_proofs/src/dev/util.rs +++ b/halo2_proofs/src/dev/util.rs @@ -12,7 +12,7 @@ use crate::{ pub(crate) struct AnyQuery { /// Query index - pub index: usize, + pub index: Option, /// Column type pub column_type: Any, /// Column index @@ -78,7 +78,7 @@ pub(super) fn load<'a, F: Field, T: ColumnType, Q: Into + Copy>( cells: &'a [Vec>], ) -> impl Fn(Q) -> Value + 'a { move |query| { - let (column, at) = &queries[query.into().index]; + let (column, at) = &queries[query.into().index.unwrap()]; let resolved_row = (row + at.0) % n; cells[column.index()][resolved_row as usize].into() } @@ -91,7 +91,7 @@ pub(super) fn load_instance<'a, F: Field, T: ColumnType, Q: Into + Cop cells: &'a [Vec], ) -> impl Fn(Q) -> Value + 'a { move |query| { - let (column, at) = &queries[query.into().index]; + let (column, at) = &queries[query.into().index.unwrap()]; let resolved_row = (row + at.0) % n; Value::Real(cells[column.index()][resolved_row as usize]) } diff --git a/halo2_proofs/src/plonk/circuit.rs b/halo2_proofs/src/plonk/circuit.rs index bef769b8c0..bb37459c42 100644 --- a/halo2_proofs/src/plonk/circuit.rs +++ b/halo2_proofs/src/plonk/circuit.rs @@ -1,26 +1,29 @@ +use super::{lookup, permutation, Assigned, Error}; +use crate::dev::metadata; +use crate::{ + circuit::{Layouter, Region, Value}, + poly::Rotation, +}; use core::cmp::max; use core::ops::{Add, Mul}; use ff::Field; +use sealed::SealedPhase; +use std::cmp::Ordering; use std::collections::HashMap; +use std::fmt::{Debug, Formatter}; use std::{ convert::TryFrom, ops::{Neg, Sub}, }; -use super::{lookup, permutation, Assigned, Error}; -use crate::dev::metadata; -use crate::{ - circuit::{Layouter, Region, Value}, - poly::Rotation, -}; -use sealed::SealedPhase; - mod compress_selectors; /// A column type pub trait ColumnType: 'static + Sized + Copy + std::fmt::Debug + PartialEq + Eq + Into { + /// Return expression from cell + fn query_cell(&self, index: usize, at: Rotation) -> Expression; } /// A column with an index and type @@ -45,6 +48,31 @@ impl Column { pub fn column_type(&self) -> &C { &self.column_type } + + /// Return expression from column at a relative position + pub fn query_cell(&self, at: Rotation) -> Expression { + self.column_type.query_cell(self.index, at) + } + + /// Return expression from column at the current row + pub fn cur(&self) -> Expression { + self.query_cell(Rotation::cur()) + } + + /// Return expression from column at the next row + pub fn next(&self) -> Expression { + self.query_cell(Rotation::next()) + } + + /// Return expression from column at the previous row + pub fn prev(&self) -> Expression { + self.query_cell(Rotation::prev()) + } + + /// Return expression from column at the specified rotation + pub fn rot(&self, rotation: i32) -> Expression { + self.query_cell(Rotation(rotation)) + } } impl Ord for Column { @@ -234,10 +262,56 @@ impl PartialOrd for Any { } } -impl ColumnType for Advice {} -impl ColumnType for Fixed {} -impl ColumnType for Instance {} -impl ColumnType for Any {} +impl ColumnType for Advice { + fn query_cell(&self, index: usize, at: Rotation) -> Expression { + Expression::Advice(AdviceQuery { + index: None, + column_index: index, + rotation: at, + phase: self.phase, + }) + } +} +impl ColumnType for Fixed { + fn query_cell(&self, index: usize, at: Rotation) -> Expression { + Expression::Fixed(FixedQuery { + index: None, + column_index: index, + rotation: at, + }) + } +} +impl ColumnType for Instance { + fn query_cell(&self, index: usize, at: Rotation) -> Expression { + Expression::Instance(InstanceQuery { + index: None, + column_index: index, + rotation: at, + }) + } +} +impl ColumnType for Any { + fn query_cell(&self, index: usize, at: Rotation) -> Expression { + match self { + Any::Advice(Advice { phase }) => Expression::Advice(AdviceQuery { + index: None, + column_index: index, + rotation: at, + phase: *phase, + }), + Any::Fixed => Expression::Fixed(FixedQuery { + index: None, + column_index: index, + rotation: at, + }), + Any::Instance => Expression::Instance(InstanceQuery { + index: None, + column_index: index, + rotation: at, + }), + } + } +} impl From for Any { fn from(advice: Advice) -> Any { @@ -391,13 +465,18 @@ impl Selector { pub fn is_simple(&self) -> bool { self.1 } + + /// Return expression from selector + pub fn expr(&self) -> Expression { + Expression::Selector(*self) + } } /// Query of fixed column at a certain relative location #[derive(Copy, Clone, Debug)] pub struct FixedQuery { /// Query index - pub(crate) index: usize, + pub(crate) index: Option, /// Column index pub(crate) column_index: usize, /// Rotation of this query @@ -420,7 +499,7 @@ impl FixedQuery { #[derive(Copy, Clone, Debug)] pub struct AdviceQuery { /// Query index - pub(crate) index: usize, + pub(crate) index: Option, /// Column index pub(crate) column_index: usize, /// Rotation of this query @@ -450,7 +529,7 @@ impl AdviceQuery { #[derive(Copy, Clone, Debug)] pub struct InstanceQuery { /// Query index - pub(crate) index: usize, + pub(crate) index: Option, /// Column index pub(crate) column_index: usize, /// Rotation of this query @@ -514,6 +593,11 @@ impl Challenge { pub fn phase(&self) -> u8 { self.phase.0 } + + /// Return Expression + pub fn expr(&self) -> Expression { + Expression::Challenge(*self) + } } /// This trait allows a [`Circuit`] to direct some backend to assign a witness @@ -705,6 +789,59 @@ pub enum Expression { } impl Expression { + /// Make side effects + pub fn query_cells(&mut self, cells: &mut VirtualCells<'_, F>) { + match self { + Expression::Constant(_) => (), + Expression::Selector(selector) => { + if !cells.queried_selectors.contains(selector) { + cells.queried_selectors.push(*selector); + } + } + Expression::Fixed(query) => { + if query.index.is_none() { + let col = Column { + index: query.column_index, + column_type: Fixed, + }; + cells.queried_cells.push((col, query.rotation).into()); + query.index = Some(cells.meta.query_fixed_index(col, query.rotation)); + } + } + Expression::Advice(query) => { + if query.index.is_none() { + let col = Column { + index: query.column_index, + column_type: Advice { phase: query.phase }, + }; + cells.queried_cells.push((col, query.rotation).into()); + query.index = Some(cells.meta.query_advice_index(col, query.rotation)); + } + } + Expression::Instance(query) => { + if query.index.is_none() { + let col = Column { + index: query.column_index, + column_type: Instance, + }; + cells.queried_cells.push((col, query.rotation).into()); + query.index = Some(cells.meta.query_instance_index(col, query.rotation)); + } + } + Expression::Challenge(_) => (), + Expression::Negated(a) => a.query_cells(cells), + Expression::Sum(a, b) => { + a.query_cells(cells); + b.query_cells(cells); + } + Expression::Product(a, b) => { + a.query_cells(cells); + b.query_cells(cells); + } + Expression::Scaled(a, _) => a.query_cells(cells), + }; + } + /// Evaluate the polynomial using the provided closures to perform the /// operations. pub fn evaluate( @@ -1091,43 +1228,43 @@ impl std::fmt::Debug for Expression { Expression::Constant(scalar) => f.debug_tuple("Constant").field(scalar).finish(), Expression::Selector(selector) => f.debug_tuple("Selector").field(selector).finish(), // Skip enum variant and print query struct directly to maintain backwards compatibility. - Expression::Fixed(FixedQuery { - index, - column_index, - rotation, - }) => f - .debug_struct("Fixed") - .field("query_index", index) - .field("column_index", column_index) - .field("rotation", rotation) - .finish(), - Expression::Advice(AdviceQuery { - index, - column_index, - rotation, - phase, - }) => { + Expression::Fixed(query) => { + let mut debug_struct = f.debug_struct("Fixed"); + match query.index { + None => debug_struct.field("query_index", &query.index), + Some(idx) => debug_struct.field("query_index", &idx), + }; + debug_struct + .field("column_index", &query.column_index) + .field("rotation", &query.rotation) + .finish() + } + Expression::Advice(query) => { let mut debug_struct = f.debug_struct("Advice"); + match query.index { + None => debug_struct.field("query_index", &query.index), + Some(idx) => debug_struct.field("query_index", &idx), + }; debug_struct - .field("query_index", index) - .field("column_index", column_index) - .field("rotation", rotation); + .field("column_index", &query.column_index) + .field("rotation", &query.rotation); // Only show advice's phase if it's not in first phase. - if *phase != FirstPhase.to_sealed() { - debug_struct.field("phase", phase); + if query.phase != FirstPhase.to_sealed() { + debug_struct.field("phase", &query.phase); } debug_struct.finish() } - Expression::Instance(InstanceQuery { - index, - column_index, - rotation, - }) => f - .debug_struct("Instance") - .field("query_index", index) - .field("column_index", column_index) - .field("rotation", rotation) - .finish(), + Expression::Instance(query) => { + let mut debug_struct = f.debug_struct("Instance"); + match query.index { + None => debug_struct.field("query_index", &query.index), + Some(idx) => debug_struct.field("query_index", &idx), + }; + debug_struct + .field("column_index", &query.column_index) + .field("rotation", &query.rotation) + .finish() + } Expression::Challenge(challenge) => { f.debug_tuple("Challenge").field(challenge).finish() } @@ -1536,17 +1673,16 @@ impl ConstraintSystem { let mut cells = VirtualCells::new(self); let table_map = table_map(&mut cells) .into_iter() - .map(|(input, table)| { + .map(|(mut input, table)| { if input.contains_simple_selector() { panic!("expression containing simple selector supplied to lookup argument"); } - - let table = cells.query_fixed(table.inner(), Rotation::cur()); - + let mut table = cells.query_fixed(table.inner(), Rotation::cur()); + input.query_cells(&mut cells); + table.query_cells(&mut cells); (input, table) }) .collect(); - let index = self.lookups.len(); self.lookups.push(lookup::Argument::new(name, table_map)); @@ -1564,8 +1700,14 @@ impl ConstraintSystem { table_map: impl FnOnce(&mut VirtualCells<'_, F>) -> Vec<(Expression, Expression)>, ) -> usize { let mut cells = VirtualCells::new(self); - let table_map = table_map(&mut cells); - + let table_map = table_map(&mut cells) + .into_iter() + .map(|(mut input, mut table)| { + input.query_cells(&mut cells); + table.query_cells(&mut cells); + (input, table) + }) + .collect(); let index = self.lookups.len(); self.lookups.push(lookup::Argument::new(name, table_map)); @@ -1695,15 +1837,18 @@ impl ConstraintSystem { ) { let mut cells = VirtualCells::new(self); let constraints = constraints(&mut cells); - let queried_selectors = cells.queried_selectors; - let queried_cells = cells.queried_cells; - let (constraint_names, polys): (_, Vec<_>) = constraints .into_iter() .map(|c| c.into()) - .map(|c| (c.name, c.poly)) + .map(|mut c: Constraint| { + c.poly.query_cells(&mut cells); + (c.name, c.poly) + }) .unzip(); + let queried_selectors = cells.queried_selectors; + let queried_cells = cells.queried_cells; + assert!( !polys.is_empty(), "Gates must contain at least one constraint." @@ -1764,7 +1909,7 @@ impl ConstraintSystem { let column = self.fixed_column(); new_columns.push(column); Expression::Fixed(FixedQuery { - index: self.query_fixed_index(column, Rotation::cur()), + index: Some(self.query_fixed_index(column, Rotation::cur())), column_index: column.index, rotation: Rotation::cur(), }) @@ -2144,7 +2289,7 @@ impl<'a, F: Field> VirtualCells<'a, F> { pub fn query_fixed(&mut self, column: Column, at: Rotation) -> Expression { self.queried_cells.push((column, at).into()); Expression::Fixed(FixedQuery { - index: self.meta.query_fixed_index(column, at), + index: Some(self.meta.query_fixed_index(column, at)), column_index: column.index, rotation: at, }) @@ -2154,7 +2299,7 @@ impl<'a, F: Field> VirtualCells<'a, F> { pub fn query_advice(&mut self, column: Column, at: Rotation) -> Expression { self.queried_cells.push((column, at).into()); Expression::Advice(AdviceQuery { - index: self.meta.query_advice_index(column, at), + index: Some(self.meta.query_advice_index(column, at)), column_index: column.index, rotation: at, phase: column.column_type().phase, @@ -2165,7 +2310,7 @@ impl<'a, F: Field> VirtualCells<'a, F> { pub fn query_instance(&mut self, column: Column, at: Rotation) -> Expression { self.queried_cells.push((column, at).into()); Expression::Instance(InstanceQuery { - index: self.meta.query_instance_index(column, at), + index: Some(self.meta.query_instance_index(column, at)), column_index: column.index, rotation: at, }) diff --git a/halo2_proofs/src/plonk/circuit/compress_selectors.rs b/halo2_proofs/src/plonk/circuit/compress_selectors.rs index 3de6b4dfc2..053ebe3178 100644 --- a/halo2_proofs/src/plonk/circuit/compress_selectors.rs +++ b/halo2_proofs/src/plonk/circuit/compress_selectors.rs @@ -281,7 +281,7 @@ mod tests { let (combination_assignments, selector_assignments) = process::(selectors.clone(), max_degree, || { let tmp = Expression::Fixed(FixedQuery { - index: query, + index: Some(query), column_index: query, rotation: Rotation::cur(), }); @@ -320,7 +320,7 @@ mod tests { &|_| panic!("should not occur in returned expressions"), &|query| { // Should be the correct combination in the expression - assert_eq!(selector.combination_index, query.index); + assert_eq!(selector.combination_index, query.index.unwrap()); assignment }, &|_| panic!("should not occur in returned expressions"), diff --git a/halo2_proofs/src/plonk/lookup/verifier.rs b/halo2_proofs/src/plonk/lookup/verifier.rs index 507db6df46..1dc111f2cc 100644 --- a/halo2_proofs/src/plonk/lookup/verifier.rs +++ b/halo2_proofs/src/plonk/lookup/verifier.rs @@ -120,9 +120,9 @@ impl Evaluated { expression.evaluate( &|scalar| scalar, &|_| panic!("virtual selectors are removed during optimization"), - &|query| fixed_evals[query.index], - &|query| advice_evals[query.index], - &|query| instance_evals[query.index], + &|query| fixed_evals[query.index.unwrap()], + &|query| advice_evals[query.index.unwrap()], + &|query| instance_evals[query.index.unwrap()], &|challenge| challenges[challenge.index()], &|a| -a, &|a, b| a + &b, diff --git a/halo2_proofs/src/plonk/verifier.rs b/halo2_proofs/src/plonk/verifier.rs index f5984d2c79..72ec7203fb 100644 --- a/halo2_proofs/src/plonk/verifier.rs +++ b/halo2_proofs/src/plonk/verifier.rs @@ -275,9 +275,9 @@ where poly.evaluate( &|scalar| scalar, &|_| panic!("virtual selectors are removed during optimization"), - &|query| fixed_evals[query.index], - &|query| advice_evals[query.index], - &|query| instance_evals[query.index], + &|query| fixed_evals[query.index.unwrap()], + &|query| advice_evals[query.index.unwrap()], + &|query| instance_evals[query.index.unwrap()], &|challenge| challenges[challenge.index()], &|a| -a, &|a, b| a + &b,