use core::{
cmp::max,
ops::{Add, Mul},
};
use std::{
collections::HashMap,
convert::TryFrom,
fmt::Debug,
iter::{Product, Sum},
ops::{Neg, Sub},
};
use ff::Field;
use sealed::SealedPhase;
use super::{lookup, permutation, trash, Error};
use crate::{
circuit::{layouter::SyncDeps, Layouter, Region, Value},
dev::metadata,
plonk::trash::Argument,
poly::Rotation,
utils::rational::Rational,
};
pub trait ColumnType:
'static + Sized + Copy + std::fmt::Debug + PartialEq + Eq + Into<Any>
{
fn query_cell<F: Field>(&self, index: usize, at: Rotation) -> Expression<F>;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Column<C: ColumnType> {
index: usize,
column_type: C,
}
impl<C: ColumnType> Column<C> {
#[cfg(test)]
pub(crate) fn new(index: usize, column_type: C) -> Self {
Column { index, column_type }
}
pub fn index(&self) -> usize {
self.index
}
pub fn column_type(&self) -> &C {
&self.column_type
}
pub fn query_cell<F: Field>(&self, at: Rotation) -> Expression<F> {
self.column_type.query_cell(self.index, at)
}
pub fn cur<F: Field>(&self) -> Expression<F> {
self.query_cell(Rotation::cur())
}
pub fn next<F: Field>(&self) -> Expression<F> {
self.query_cell(Rotation::next())
}
pub fn prev<F: Field>(&self) -> Expression<F> {
self.query_cell(Rotation::prev())
}
pub fn rot<F: Field>(&self, rotation: i32) -> Expression<F> {
self.query_cell(Rotation(rotation))
}
}
impl<C: ColumnType> Ord for Column<C> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self.column_type.into().cmp(&other.column_type.into()) {
std::cmp::Ordering::Equal => self.index.cmp(&other.index),
order => order,
}
}
}
impl<C: ColumnType> PartialOrd for Column<C> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
pub(crate) mod sealed {
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Phase(pub(super) u8);
impl Phase {
pub fn prev(&self) -> Option<Phase> {
self.0.checked_sub(1).map(Phase)
}
}
impl SealedPhase for Phase {
fn to_sealed(self) -> Phase {
self
}
}
pub trait SealedPhase {
fn to_sealed(self) -> Phase;
}
}
pub trait Phase: SealedPhase {}
impl<P: SealedPhase> Phase for P {}
#[derive(Debug)]
pub struct FirstPhase;
impl SealedPhase for super::FirstPhase {
fn to_sealed(self) -> sealed::Phase {
sealed::Phase(0)
}
}
#[derive(Debug)]
pub struct SecondPhase;
impl SealedPhase for super::SecondPhase {
fn to_sealed(self) -> sealed::Phase {
sealed::Phase(1)
}
}
#[derive(Debug)]
pub struct ThirdPhase;
impl SealedPhase for super::ThirdPhase {
fn to_sealed(self) -> sealed::Phase {
sealed::Phase(2)
}
}
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
pub struct Advice {
pub(crate) phase: sealed::Phase,
}
impl Default for Advice {
fn default() -> Advice {
Advice {
phase: FirstPhase.to_sealed(),
}
}
}
impl Advice {
pub fn new<P: Phase>(phase: P) -> Advice {
Advice {
phase: phase.to_sealed(),
}
}
pub fn phase(&self) -> u8 {
self.phase.0
}
}
impl std::fmt::Debug for Advice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut debug_struct = f.debug_struct("Advice");
if self.phase != FirstPhase.to_sealed() {
debug_struct.field("phase", &self.phase);
}
debug_struct.finish()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Fixed;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Instance;
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
pub enum Any {
Advice(Advice),
Fixed,
Instance,
}
impl Any {
pub fn advice() -> Any {
Any::Advice(Advice::default())
}
pub fn advice_in<P: Phase>(phase: P) -> Any {
Any::Advice(Advice::new(phase))
}
}
impl std::fmt::Debug for Any {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Any::Advice(advice) => {
let mut debug_struct = f.debug_struct("Advice");
if advice.phase != FirstPhase.to_sealed() {
debug_struct.field("phase", &advice.phase);
}
debug_struct.finish()
}
Any::Fixed => f.debug_struct("Fixed").finish(),
Any::Instance => f.debug_struct("Instance").finish(),
}
}
}
impl Ord for Any {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self, other) {
(Any::Instance, Any::Instance) | (Any::Fixed, Any::Fixed) => std::cmp::Ordering::Equal,
(Any::Advice(lhs), Any::Advice(rhs)) => lhs.phase.cmp(&rhs.phase),
(Any::Instance, Any::Advice(_))
| (Any::Advice(_), Any::Fixed)
| (Any::Instance, Any::Fixed) => std::cmp::Ordering::Less,
(Any::Fixed, Any::Instance)
| (Any::Fixed, Any::Advice(_))
| (Any::Advice(_), Any::Instance) => std::cmp::Ordering::Greater,
}
}
}
impl PartialOrd for Any {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl ColumnType for Advice {
fn query_cell<F: Field>(&self, index: usize, at: Rotation) -> Expression<F> {
Expression::Advice(AdviceQuery {
index: None,
column_index: index,
rotation: at,
phase: self.phase,
})
}
}
impl ColumnType for Fixed {
fn query_cell<F: Field>(&self, index: usize, at: Rotation) -> Expression<F> {
Expression::Fixed(FixedQuery {
index: None,
column_index: index,
rotation: at,
})
}
}
impl ColumnType for Instance {
fn query_cell<F: Field>(&self, index: usize, at: Rotation) -> Expression<F> {
Expression::Instance(InstanceQuery {
index: None,
column_index: index,
rotation: at,
})
}
}
impl ColumnType for Any {
fn query_cell<F: Field>(&self, index: usize, at: Rotation) -> Expression<F> {
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<Advice> for Any {
fn from(advice: Advice) -> Any {
Any::Advice(advice)
}
}
impl From<Fixed> for Any {
fn from(_: Fixed) -> Any {
Any::Fixed
}
}
impl From<Instance> for Any {
fn from(_: Instance) -> Any {
Any::Instance
}
}
impl From<Column<Advice>> for Column<Any> {
fn from(advice: Column<Advice>) -> Column<Any> {
Column {
index: advice.index(),
column_type: Any::Advice(advice.column_type),
}
}
}
impl From<Column<Fixed>> for Column<Any> {
fn from(advice: Column<Fixed>) -> Column<Any> {
Column {
index: advice.index(),
column_type: Any::Fixed,
}
}
}
impl From<Column<Instance>> for Column<Any> {
fn from(advice: Column<Instance>) -> Column<Any> {
Column {
index: advice.index(),
column_type: Any::Instance,
}
}
}
impl TryFrom<Column<Any>> for Column<Advice> {
type Error = &'static str;
fn try_from(any: Column<Any>) -> Result<Self, Self::Error> {
match any.column_type() {
Any::Advice(advice) => Ok(Column {
index: any.index(),
column_type: *advice,
}),
_ => Err("Cannot convert into Column<Advice>"),
}
}
}
impl TryFrom<Column<Any>> for Column<Fixed> {
type Error = &'static str;
fn try_from(any: Column<Any>) -> Result<Self, Self::Error> {
match any.column_type() {
Any::Fixed => Ok(Column {
index: any.index(),
column_type: Fixed,
}),
_ => Err("Cannot convert into Column<Fixed>"),
}
}
}
impl TryFrom<Column<Any>> for Column<Instance> {
type Error = &'static str;
fn try_from(any: Column<Any>) -> Result<Self, Self::Error> {
match any.column_type() {
Any::Instance => Ok(Column {
index: any.index(),
column_type: Instance,
}),
_ => Err("Cannot convert into Column<Instance>"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Selector(pub(crate) usize, bool);
impl Selector {
pub fn enable<F: Field>(&self, region: &mut Region<F>, offset: usize) -> Result<(), Error> {
region.enable_selector(|| "", self, offset)
}
pub fn is_simple(&self) -> bool {
self.1
}
pub fn index(&self) -> usize {
self.0
}
pub fn expr<F: Field>(&self) -> Expression<F> {
Expression::Selector(*self)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct FixedQuery {
pub(crate) index: Option<usize>,
pub(crate) column_index: usize,
pub(crate) rotation: Rotation,
}
impl FixedQuery {
pub fn index(&self) -> Option<usize> {
self.index
}
pub fn column_index(&self) -> usize {
self.column_index
}
pub fn rotation(&self) -> Rotation {
self.rotation
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct AdviceQuery {
pub index: Option<usize>,
pub(crate) column_index: usize,
pub(crate) rotation: Rotation,
pub(crate) phase: sealed::Phase,
}
impl AdviceQuery {
pub fn column_index(&self) -> usize {
self.column_index
}
pub fn rotation(&self) -> Rotation {
self.rotation
}
pub fn phase(&self) -> u8 {
self.phase.0
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct InstanceQuery {
pub index: Option<usize>,
pub(crate) column_index: usize,
pub(crate) rotation: Rotation,
}
impl InstanceQuery {
pub fn column_index(&self) -> usize {
self.column_index
}
pub fn rotation(&self) -> Rotation {
self.rotation
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct TableColumn {
inner: Column<Fixed>,
}
impl TableColumn {
pub fn inner(&self) -> Column<Fixed> {
self.inner
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Challenge {
index: usize,
pub(crate) phase: sealed::Phase,
}
impl Challenge {
pub fn index(&self) -> usize {
self.index
}
pub fn phase(&self) -> u8 {
self.phase.0
}
pub fn expr<F: Field>(&self) -> Expression<F> {
Expression::Challenge(*self)
}
}
pub trait Assignment<F: Field> {
fn enter_region<NR, N>(&mut self, name_fn: N)
where
NR: Into<String>,
N: FnOnce() -> NR;
fn annotate_column<A, AR>(&mut self, annotation: A, column: Column<Any>)
where
A: FnOnce() -> AR,
AR: Into<String>;
fn exit_region(&mut self);
fn enable_selector<A, AR>(
&mut self,
annotation: A,
selector: &Selector,
row: usize,
) -> Result<(), Error>
where
A: FnOnce() -> AR,
AR: Into<String>;
fn query_instance(&self, column: Column<Instance>, row: usize) -> Result<Value<F>, Error>;
fn assign_advice<V, VR, A, AR>(
&mut self,
annotation: A,
column: Column<Advice>,
row: usize,
to: V,
) -> Result<(), Error>
where
V: FnOnce() -> Value<VR>,
VR: Into<Rational<F>>,
A: FnOnce() -> AR,
AR: Into<String>;
fn assign_fixed<V, VR, A, AR>(
&mut self,
annotation: A,
column: Column<Fixed>,
row: usize,
to: V,
) -> Result<(), Error>
where
V: FnOnce() -> Value<VR>,
VR: Into<Rational<F>>,
A: FnOnce() -> AR,
AR: Into<String>;
fn copy(
&mut self,
left_column: Column<Any>,
left_row: usize,
right_column: Column<Any>,
right_row: usize,
) -> Result<(), Error>;
fn fill_from_row(
&mut self,
column: Column<Fixed>,
row: usize,
to: Value<Rational<F>>,
) -> Result<(), Error>;
fn get_challenge(&self, challenge: Challenge) -> Value<F>;
fn push_namespace<NR, N>(&mut self, name_fn: N)
where
NR: Into<String>,
N: FnOnce() -> NR;
fn pop_namespace(&mut self, gadget_name: Option<String>);
}
pub trait FloorPlanner {
fn synthesize<F: Field, CS: Assignment<F> + SyncDeps, C: Circuit<F>>(
cs: &mut CS,
circuit: &C,
config: C::Config,
constants: Vec<Column<Fixed>>,
) -> Result<(), Error>;
}
pub trait Circuit<F: Field> {
type Config: Clone;
type FloorPlanner: FloorPlanner;
#[cfg(feature = "circuit-params")]
type Params: Default;
fn without_witnesses(&self) -> Self;
#[cfg(feature = "circuit-params")]
fn params(&self) -> Self::Params {
Self::Params::default()
}
#[cfg(feature = "circuit-params")]
fn configure_with_params(
meta: &mut ConstraintSystem<F>,
_params: Self::Params,
) -> Self::Config {
Self::configure(meta)
}
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config;
fn synthesize(&self, config: Self::Config, layouter: impl Layouter<F>) -> Result<(), Error>;
}
#[derive(Clone, PartialEq, Eq)]
pub enum Expression<F> {
Constant(F),
Selector(Selector),
Fixed(FixedQuery),
Advice(AdviceQuery),
Instance(InstanceQuery),
Challenge(Challenge),
Negated(Box<Expression<F>>),
Sum(Box<Expression<F>>, Box<Expression<F>>),
Product(Box<Expression<F>>, Box<Expression<F>>),
Scaled(Box<Expression<F>>, F),
}
impl<F: Field> Expression<F> {
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),
};
}
#[allow(clippy::too_many_arguments)]
pub fn evaluate<T>(
&self,
constant: &impl Fn(F) -> T,
selector_column: &impl Fn(Selector) -> T,
fixed_column: &impl Fn(FixedQuery) -> T,
advice_column: &impl Fn(AdviceQuery) -> T,
instance_column: &impl Fn(InstanceQuery) -> T,
challenge: &impl Fn(Challenge) -> T,
negated: &impl Fn(T) -> T,
sum: &impl Fn(T, T) -> T,
product: &impl Fn(T, T) -> T,
scaled: &impl Fn(T, F) -> T,
) -> T {
match self {
Expression::Constant(scalar) => constant(*scalar),
Expression::Selector(selector) => selector_column(*selector),
Expression::Fixed(query) => fixed_column(*query),
Expression::Advice(query) => advice_column(*query),
Expression::Instance(query) => instance_column(*query),
Expression::Challenge(value) => challenge(*value),
Expression::Negated(a) => {
let a = a.evaluate(
constant,
selector_column,
fixed_column,
advice_column,
instance_column,
challenge,
negated,
sum,
product,
scaled,
);
negated(a)
}
Expression::Sum(a, b) => {
let a = a.evaluate(
constant,
selector_column,
fixed_column,
advice_column,
instance_column,
challenge,
negated,
sum,
product,
scaled,
);
let b = b.evaluate(
constant,
selector_column,
fixed_column,
advice_column,
instance_column,
challenge,
negated,
sum,
product,
scaled,
);
sum(a, b)
}
Expression::Product(a, b) => {
let a = a.evaluate(
constant,
selector_column,
fixed_column,
advice_column,
instance_column,
challenge,
negated,
sum,
product,
scaled,
);
let b = b.evaluate(
constant,
selector_column,
fixed_column,
advice_column,
instance_column,
challenge,
negated,
sum,
product,
scaled,
);
product(a, b)
}
Expression::Scaled(a, f) => {
let a = a.evaluate(
constant,
selector_column,
fixed_column,
advice_column,
instance_column,
challenge,
negated,
sum,
product,
scaled,
);
scaled(a, *f)
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn evaluate_lazy<T: PartialEq>(
&self,
constant: &impl Fn(F) -> T,
selector_column: &impl Fn(Selector) -> T,
fixed_column: &impl Fn(FixedQuery) -> T,
advice_column: &impl Fn(AdviceQuery) -> T,
instance_column: &impl Fn(InstanceQuery) -> T,
challenge: &impl Fn(Challenge) -> T,
negated: &impl Fn(T) -> T,
sum: &impl Fn(T, T) -> T,
product: &impl Fn(T, T) -> T,
scaled: &impl Fn(T, F) -> T,
zero: &T,
) -> T {
match self {
Expression::Constant(scalar) => constant(*scalar),
Expression::Selector(selector) => selector_column(*selector),
Expression::Fixed(query) => fixed_column(*query),
Expression::Advice(query) => advice_column(*query),
Expression::Instance(query) => instance_column(*query),
Expression::Challenge(value) => challenge(*value),
Expression::Negated(a) => {
let a = a.evaluate_lazy(
constant,
selector_column,
fixed_column,
advice_column,
instance_column,
challenge,
negated,
sum,
product,
scaled,
zero,
);
negated(a)
}
Expression::Sum(a, b) => {
let a = a.evaluate_lazy(
constant,
selector_column,
fixed_column,
advice_column,
instance_column,
challenge,
negated,
sum,
product,
scaled,
zero,
);
let b = b.evaluate_lazy(
constant,
selector_column,
fixed_column,
advice_column,
instance_column,
challenge,
negated,
sum,
product,
scaled,
zero,
);
sum(a, b)
}
Expression::Product(a, b) => {
let (a, b) = if a.complexity() <= b.complexity() {
(a, b)
} else {
(b, a)
};
let a = a.evaluate_lazy(
constant,
selector_column,
fixed_column,
advice_column,
instance_column,
challenge,
negated,
sum,
product,
scaled,
zero,
);
if a == *zero {
a
} else {
let b = b.evaluate_lazy(
constant,
selector_column,
fixed_column,
advice_column,
instance_column,
challenge,
negated,
sum,
product,
scaled,
zero,
);
product(a, b)
}
}
Expression::Scaled(a, f) => {
let a = a.evaluate_lazy(
constant,
selector_column,
fixed_column,
advice_column,
instance_column,
challenge,
negated,
sum,
product,
scaled,
zero,
);
scaled(a, *f)
}
}
}
fn write_identifier<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
match self {
Expression::Constant(scalar) => write!(writer, "{scalar:?}"),
Expression::Selector(selector) => write!(writer, "selector[{}]", selector.0),
Expression::Fixed(query) => {
write!(
writer,
"fixed[{}][{}]",
query.column_index, query.rotation.0
)
}
Expression::Advice(query) => {
write!(
writer,
"advice[{}][{}]",
query.column_index, query.rotation.0
)
}
Expression::Instance(query) => {
write!(
writer,
"instance[{}][{}]",
query.column_index, query.rotation.0
)
}
Expression::Challenge(challenge) => {
write!(writer, "challenge[{}]", challenge.index())
}
Expression::Negated(a) => {
writer.write_all(b"(-")?;
a.write_identifier(writer)?;
writer.write_all(b")")
}
Expression::Sum(a, b) => {
writer.write_all(b"(")?;
a.write_identifier(writer)?;
writer.write_all(b"+")?;
b.write_identifier(writer)?;
writer.write_all(b")")
}
Expression::Product(a, b) => {
writer.write_all(b"(")?;
a.write_identifier(writer)?;
writer.write_all(b"*")?;
b.write_identifier(writer)?;
writer.write_all(b")")
}
Expression::Scaled(a, f) => {
a.write_identifier(writer)?;
write!(writer, "*{f:?}")
}
}
}
pub fn identifier(&self) -> String {
let mut cursor = std::io::Cursor::new(Vec::new());
self.write_identifier(&mut cursor).unwrap();
String::from_utf8(cursor.into_inner()).unwrap()
}
pub fn degree(&self) -> usize {
match self {
Expression::Constant(_) => 0,
Expression::Selector(_) => 1,
Expression::Fixed(_) => 1,
Expression::Advice(_) => 1,
Expression::Instance(_) => 1,
Expression::Challenge(_) => 0,
Expression::Negated(poly) => poly.degree(),
Expression::Sum(a, b) => max(a.degree(), b.degree()),
Expression::Product(a, b) => a.degree() + b.degree(),
Expression::Scaled(poly, _) => poly.degree(),
}
}
pub fn complexity(&self) -> usize {
match self {
Expression::Constant(_) => 0,
Expression::Selector(_) => 1,
Expression::Fixed(_) => 1,
Expression::Advice(_) => 1,
Expression::Instance(_) => 1,
Expression::Challenge(_) => 0,
Expression::Negated(poly) => poly.complexity() + 5,
Expression::Sum(a, b) => a.complexity() + b.complexity() + 15,
Expression::Product(a, b) => a.complexity() + b.complexity() + 30,
Expression::Scaled(poly, _) => poly.complexity() + 30,
}
}
pub fn square(self) -> Self {
self.clone() * self
}
fn contains_simple_selector(&self) -> bool {
self.evaluate(
&|_| false,
&|selector| selector.is_simple(),
&|_| false,
&|_| false,
&|_| false,
&|_| false,
&|a| a,
&|a, b| a || b,
&|a, b| a || b,
&|a, _| a,
)
}
}
impl<F: std::fmt::Debug> std::fmt::Debug for Expression<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expression::Constant(scalar) => f.debug_tuple("Constant").field(scalar).finish(),
Expression::Selector(selector) => f.debug_tuple("Selector").field(selector).finish(),
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("column_index", &query.column_index)
.field("rotation", &query.rotation);
if query.phase != FirstPhase.to_sealed() {
debug_struct.field("phase", &query.phase);
}
debug_struct.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()
}
Expression::Negated(poly) => f.debug_tuple("Negated").field(poly).finish(),
Expression::Sum(a, b) => f.debug_tuple("Sum").field(a).field(b).finish(),
Expression::Product(a, b) => f.debug_tuple("Product").field(a).field(b).finish(),
Expression::Scaled(poly, scalar) => {
f.debug_tuple("Scaled").field(poly).field(scalar).finish()
}
}
}
}
impl<F: Field + From<u64>> From<u64> for Expression<F> {
fn from(value: u64) -> Self {
Expression::Constant(F::from(value))
}
}
impl<F: Field> Neg for Expression<F> {
type Output = Expression<F>;
fn neg(self) -> Self::Output {
Expression::Negated(Box::new(self))
}
}
impl<F: Field> Neg for &Expression<F> {
type Output = Expression<F>;
fn neg(self) -> Self::Output {
-self.clone()
}
}
impl<F: Field> Add for Expression<F> {
type Output = Expression<F>;
fn add(self, rhs: Expression<F>) -> Expression<F> {
if self.contains_simple_selector() || rhs.contains_simple_selector() {
panic!("attempted to use a simple selector in an addition");
}
if self == Expression::Constant(F::ZERO) {
return rhs;
}
if rhs == Expression::Constant(F::ZERO) {
return self;
}
Expression::Sum(Box::new(self), Box::new(rhs))
}
}
impl<'a, F: Field> Add for &'a Expression<F> {
type Output = Expression<F>;
fn add(self, rhs: &'a Expression<F>) -> Expression<F> {
self.clone() + rhs.clone()
}
}
impl<F: Field> Add<Expression<F>> for &Expression<F> {
type Output = Expression<F>;
fn add(self, rhs: Expression<F>) -> Expression<F> {
self.clone() + rhs
}
}
impl<'a, F: Field> Add<&'a Expression<F>> for Expression<F> {
type Output = Expression<F>;
fn add(self, rhs: &'a Expression<F>) -> Expression<F> {
self + rhs.clone()
}
}
impl<F: Field> Sub for Expression<F> {
type Output = Expression<F>;
fn sub(self, rhs: Expression<F>) -> Expression<F> {
if self.contains_simple_selector() || rhs.contains_simple_selector() {
panic!("attempted to use a simple selector in a subtraction");
}
if self == Expression::Constant(F::ZERO) {
return -rhs;
}
if rhs == Expression::Constant(F::ZERO) {
return self;
}
Expression::Sum(Box::new(self), Box::new(-rhs))
}
}
impl<'a, F: Field> Sub for &'a Expression<F> {
type Output = Expression<F>;
fn sub(self, rhs: &'a Expression<F>) -> Expression<F> {
self.clone() - rhs.clone()
}
}
impl<F: Field> Sub<Expression<F>> for &Expression<F> {
type Output = Expression<F>;
fn sub(self, rhs: Expression<F>) -> Expression<F> {
self.clone() - rhs
}
}
impl<'a, F: Field> Sub<&'a Expression<F>> for Expression<F> {
type Output = Expression<F>;
fn sub(self, rhs: &'a Expression<F>) -> Expression<F> {
self - rhs.clone()
}
}
impl<F: Field> Mul for Expression<F> {
type Output = Expression<F>;
fn mul(self, rhs: Expression<F>) -> Expression<F> {
if self.contains_simple_selector() && rhs.contains_simple_selector() {
panic!("attempted to multiply two expressions containing simple selectors");
}
if self == Expression::Constant(F::ZERO) || rhs == Expression::Constant(F::ZERO) {
return Expression::Constant(F::ZERO);
}
if self == Expression::Constant(F::ONE) {
return rhs;
}
if rhs == Expression::Constant(F::ONE) {
return self;
}
Expression::Product(Box::new(self), Box::new(rhs))
}
}
impl<'a, F: Field> Mul for &'a Expression<F> {
type Output = Expression<F>;
fn mul(self, rhs: &'a Expression<F>) -> Expression<F> {
self.clone() * rhs.clone()
}
}
impl<F: Field> Mul<Expression<F>> for &Expression<F> {
type Output = Expression<F>;
fn mul(self, rhs: Expression<F>) -> Expression<F> {
self.clone() * rhs
}
}
impl<'a, F: Field> Mul<&'a Expression<F>> for Expression<F> {
type Output = Expression<F>;
fn mul(self, rhs: &'a Expression<F>) -> Expression<F> {
self * rhs.clone()
}
}
impl<F: Field> Mul<F> for Expression<F> {
type Output = Expression<F>;
fn mul(self, rhs: F) -> Expression<F> {
Expression::Scaled(Box::new(self), rhs)
}
}
impl<F: Field> Mul<F> for &Expression<F> {
type Output = Expression<F>;
fn mul(self, rhs: F) -> Expression<F> {
self.clone() * rhs
}
}
impl<F: Field> Sum<Self> for Expression<F> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(|acc, x| acc + x).unwrap_or(Expression::Constant(F::ZERO))
}
}
impl<F: Field> Product<Self> for Expression<F> {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(|acc, x| acc * x).unwrap_or(Expression::Constant(F::ONE))
}
}
#[derive(Clone, Debug)]
pub struct VirtualCell {
pub(crate) column: Column<Any>,
pub(crate) rotation: Rotation,
}
impl<Col: Into<Column<Any>>> From<(Col, Rotation)> for VirtualCell {
fn from((column, rotation): (Col, Rotation)) -> Self {
VirtualCell {
column: column.into(),
rotation,
}
}
}
#[derive(Debug)]
pub struct Constraint<F: Field> {
name: String,
poly: Expression<F>,
}
impl<F: Field> From<Expression<F>> for Constraint<F> {
fn from(poly: Expression<F>) -> Self {
Constraint {
name: "".to_string(),
poly,
}
}
}
impl<F: Field, S: AsRef<str>> From<(S, Expression<F>)> for Constraint<F> {
fn from((name, poly): (S, Expression<F>)) -> Self {
Constraint {
name: name.as_ref().to_string(),
poly,
}
}
}
impl<F: Field> From<Expression<F>> for Vec<Constraint<F>> {
fn from(poly: Expression<F>) -> Self {
vec![Constraint {
name: "".to_string(),
poly,
}]
}
}
#[derive(Debug)]
enum SelectorType {
Multiplicative(Selector),
Additive(Selector),
None,
}
#[derive(Debug)]
pub struct Constraints<F: Field> {
selector: SelectorType,
constraints: Vec<Constraint<F>>,
}
impl<F: Field> Constraints<F> {
pub fn with_selector<I: Into<Constraint<F>>>(selector: Selector, constraints: Vec<I>) -> Self {
Constraints {
selector: SelectorType::Multiplicative(selector),
constraints: constraints.into_iter().map(|c| c.into()).collect(),
}
}
pub fn with_additive_selector<I: Into<Constraint<F>>>(
selector: Selector,
constraints: Vec<I>,
) -> Self {
Constraints {
selector: SelectorType::Additive(selector),
constraints: constraints.into_iter().map(|c| c.into()).collect(),
}
}
pub fn without_selector<I: Into<Constraint<F>>>(constraints: Vec<I>) -> Self {
Constraints {
selector: SelectorType::None,
constraints: constraints.into_iter().map(|c| c.into()).collect(),
}
}
}
#[derive(Clone, Debug)]
pub struct Gate<F: Field> {
name: String,
constraint_names: Vec<String>,
polys: Vec<Expression<F>>,
queried_selectors: Vec<Selector>,
queried_cells: Vec<VirtualCell>,
}
impl<F: Field> Gate<F> {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn constraint_name(&self, constraint_index: usize) -> &str {
self.constraint_names[constraint_index].as_str()
}
pub fn polynomials(&self) -> &[Expression<F>] {
&self.polys
}
pub(crate) fn queried_selectors(&self) -> &[Selector] {
&self.queried_selectors
}
pub(crate) fn queried_cells(&self) -> &[VirtualCell] {
&self.queried_cells
}
}
#[derive(Debug, Clone)]
pub struct ConstraintSystem<F: Field> {
pub(crate) num_fixed_columns: usize,
pub(crate) num_advice_columns: usize,
pub(crate) num_instance_columns: usize,
pub(crate) num_selectors: usize,
pub(crate) num_challenges: usize,
pub(crate) unblinded_advice_columns: Vec<usize>,
pub(crate) advice_column_phase: Vec<sealed::Phase>,
pub(crate) challenge_phase: Vec<sealed::Phase>,
pub(crate) gates: Vec<Gate<F>>,
pub(crate) advice_queries: Vec<(Column<Advice>, Rotation)>,
num_advice_queries: Vec<usize>,
pub(crate) instance_queries: Vec<(Column<Instance>, Rotation)>,
pub(crate) fixed_queries: Vec<(Column<Fixed>, Rotation)>,
pub(crate) permutation: permutation::Argument,
pub(crate) lookups: Vec<lookup::Argument<F>>,
pub(crate) trashcans: Vec<trash::Argument<F>>,
pub(crate) general_column_annotations: HashMap<metadata::Column, String>,
pub(crate) constants: Vec<Column<Fixed>>,
pub(crate) minimum_degree: Option<usize>,
}
#[allow(dead_code)]
pub struct PinnedConstraintSystem<'a, F: Field> {
num_fixed_columns: &'a usize,
num_advice_columns: &'a usize,
num_instance_columns: &'a usize,
num_selectors: &'a usize,
num_challenges: &'a usize,
advice_column_phase: &'a Vec<sealed::Phase>,
challenge_phase: &'a Vec<sealed::Phase>,
gates: PinnedGates<'a, F>,
advice_queries: &'a Vec<(Column<Advice>, Rotation)>,
instance_queries: &'a Vec<(Column<Instance>, Rotation)>,
fixed_queries: &'a Vec<(Column<Fixed>, Rotation)>,
permutation: &'a permutation::Argument,
lookups: &'a Vec<lookup::Argument<F>>,
trashcans: &'a Vec<trash::Argument<F>>,
constants: &'a Vec<Column<Fixed>>,
minimum_degree: &'a Option<usize>,
}
impl<F: Field> std::fmt::Debug for PinnedConstraintSystem<'_, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let &Self {
num_fixed_columns,
num_advice_columns,
num_instance_columns,
num_selectors,
num_challenges,
advice_column_phase,
challenge_phase,
gates,
advice_queries,
instance_queries,
fixed_queries,
permutation,
lookups,
trashcans,
constants,
minimum_degree,
} = &self;
let mut debug_struct = f.debug_struct("PinnedConstraintSystem");
debug_struct
.field("num_fixed_columns", num_fixed_columns)
.field("num_advice_columns", num_advice_columns)
.field("num_instance_columns", num_instance_columns)
.field("num_selectors", num_selectors);
if *num_challenges > &0 {
debug_struct
.field("num_challenges", num_challenges)
.field("advice_column_phase", advice_column_phase)
.field("challenge_phase", challenge_phase);
}
debug_struct
.field("gates", &gates)
.field("advice_queries", advice_queries)
.field("instance_queries", instance_queries)
.field("fixed_queries", fixed_queries)
.field("permutation", permutation)
.field("lookups", lookups)
.field("trashcans", trashcans);
debug_struct
.field("constants", constants)
.field("minimum_degree", minimum_degree);
debug_struct.finish()
}
}
struct PinnedGates<'a, F: Field>(&'a Vec<Gate<F>>);
impl<F: Field> std::fmt::Debug for PinnedGates<'_, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_list()
.entries(self.0.iter().flat_map(|gate| gate.polynomials().iter()))
.finish()
}
}
impl<F: Field> Default for ConstraintSystem<F> {
fn default() -> ConstraintSystem<F> {
ConstraintSystem {
num_fixed_columns: 0,
num_advice_columns: 0,
num_instance_columns: 0,
num_selectors: 0,
num_challenges: 0,
unblinded_advice_columns: Vec::new(),
advice_column_phase: Vec::new(),
challenge_phase: Vec::new(),
gates: vec![],
fixed_queries: Vec::new(),
advice_queries: Vec::new(),
num_advice_queries: Vec::new(),
instance_queries: Vec::new(),
permutation: permutation::Argument::new(),
lookups: Vec::new(),
trashcans: Vec::new(),
general_column_annotations: HashMap::new(),
constants: vec![],
minimum_degree: None,
}
}
}
impl<F: Field> ConstraintSystem<F> {
pub fn pinned(&self) -> PinnedConstraintSystem<'_, F> {
PinnedConstraintSystem {
num_fixed_columns: &self.num_fixed_columns,
num_advice_columns: &self.num_advice_columns,
num_instance_columns: &self.num_instance_columns,
num_selectors: &self.num_selectors,
num_challenges: &self.num_challenges,
advice_column_phase: &self.advice_column_phase,
challenge_phase: &self.challenge_phase,
gates: PinnedGates(&self.gates),
fixed_queries: &self.fixed_queries,
advice_queries: &self.advice_queries,
instance_queries: &self.instance_queries,
permutation: &self.permutation,
lookups: &self.lookups,
trashcans: &self.trashcans,
constants: &self.constants,
minimum_degree: &self.minimum_degree,
}
}
pub fn enable_constant(&mut self, column: Column<Fixed>) {
if !self.constants.contains(&column) {
self.constants.push(column);
self.enable_equality(column);
}
}
pub fn enable_equality<C: Into<Column<Any>>>(&mut self, column: C) {
let column = column.into();
self.query_any_index(column, Rotation::cur());
self.permutation.add_column(column);
}
pub fn lookup<S: AsRef<str>>(
&mut self,
name: S,
table_map: impl FnOnce(&mut VirtualCells<'_, F>) -> Vec<(Expression<F>, TableColumn)>,
) -> usize {
let mut cells = VirtualCells::new(self);
let table_map = table_map(&mut cells)
.into_iter()
.map(|(mut input, table)| {
if input.contains_simple_selector() {
panic!("expression containing simple selector supplied to lookup argument");
}
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.as_ref(), table_map));
index
}
pub fn lookup_any<S: AsRef<str>>(
&mut self,
name: S,
table_map: impl FnOnce(&mut VirtualCells<'_, F>) -> Vec<(Expression<F>, Expression<F>)>,
) -> usize {
let mut cells = VirtualCells::new(self);
let table_map = table_map(&mut cells)
.into_iter()
.map(|(mut input, mut table)| {
if input.contains_simple_selector() {
panic!("expression containing simple selector supplied to lookup argument");
}
if table.contains_simple_selector() {
panic!("expression containing simple selector supplied to lookup argument");
}
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.as_ref(), table_map));
index
}
fn query_fixed_index(&mut self, column: Column<Fixed>, at: Rotation) -> usize {
for (index, fixed_query) in self.fixed_queries.iter().enumerate() {
if fixed_query == &(column, at) {
return index;
}
}
let index = self.fixed_queries.len();
self.fixed_queries.push((column, at));
index
}
pub(crate) fn query_advice_index(&mut self, column: Column<Advice>, at: Rotation) -> usize {
for (index, advice_query) in self.advice_queries.iter().enumerate() {
if advice_query == &(column, at) {
return index;
}
}
let index = self.advice_queries.len();
self.advice_queries.push((column, at));
self.num_advice_queries[column.index] += 1;
index
}
fn query_instance_index(&mut self, column: Column<Instance>, at: Rotation) -> usize {
for (index, instance_query) in self.instance_queries.iter().enumerate() {
if instance_query == &(column, at) {
return index;
}
}
let index = self.instance_queries.len();
self.instance_queries.push((column, at));
index
}
fn query_any_index(&mut self, column: Column<Any>, at: Rotation) -> usize {
match column.column_type() {
Any::Advice(_) => {
self.query_advice_index(Column::<Advice>::try_from(column).unwrap(), at)
}
Any::Fixed => self.query_fixed_index(Column::<Fixed>::try_from(column).unwrap(), at),
Any::Instance => {
self.query_instance_index(Column::<Instance>::try_from(column).unwrap(), at)
}
}
}
pub(crate) fn get_advice_query_index(&self, column: Column<Advice>, at: Rotation) -> usize {
for (index, advice_query) in self.advice_queries.iter().enumerate() {
if advice_query == &(column, at) {
return index;
}
}
panic!("get_advice_query_index called for non-existent query");
}
pub(crate) fn get_fixed_query_index(&self, column: Column<Fixed>, at: Rotation) -> usize {
for (index, fixed_query) in self.fixed_queries.iter().enumerate() {
if fixed_query == &(column, at) {
return index;
}
}
panic!("get_fixed_query_index called for non-existent query");
}
pub(crate) fn get_instance_query_index(&self, column: Column<Instance>, at: Rotation) -> usize {
for (index, instance_query) in self.instance_queries.iter().enumerate() {
if instance_query == &(column, at) {
return index;
}
}
panic!("get_instance_query_index called for non-existent query");
}
pub(crate) fn get_any_query_index(&self, column: Column<Any>, at: Rotation) -> usize {
match column.column_type() {
Any::Advice(_) => {
self.get_advice_query_index(Column::<Advice>::try_from(column).unwrap(), at)
}
Any::Fixed => {
self.get_fixed_query_index(Column::<Fixed>::try_from(column).unwrap(), at)
}
Any::Instance => {
self.get_instance_query_index(Column::<Instance>::try_from(column).unwrap(), at)
}
}
}
pub fn set_minimum_degree(&mut self, degree: usize) {
self.minimum_degree = Some(degree);
}
pub fn create_gate(
&mut self,
name: &'static str,
constraints: impl FnOnce(&mut VirtualCells<'_, F>) -> Constraints<F>,
) {
let mut cells = VirtualCells::new(self);
let constraints = constraints(&mut cells);
assert!(
!constraints.constraints.is_empty(),
"Gates must contain at least one constraint."
);
let (constraint_names, polys): (_, Vec<_>) = cells
.apply_selector_to_constraints(constraints)
.into_iter()
.map(|mut c: Constraint<F>| {
c.poly.query_cells(&mut cells);
(c.name, c.poly)
})
.unzip();
let queried_selectors = cells.queried_selectors;
let queried_cells = cells.queried_cells;
self.gates.push(Gate {
name: name.into(),
constraint_names,
polys,
queried_selectors,
queried_cells,
});
}
pub fn directly_convert_selectors_to_fixed(
mut self,
selectors: Vec<Vec<bool>>,
) -> (Self, Vec<Vec<F>>) {
assert_eq!(selectors.len(), self.num_selectors);
let (polys, selector_replacements): (Vec<_>, Vec<_>) = selectors
.into_iter()
.map(|selector| {
let poly =
selector.iter().map(|b| if *b { F::ONE } else { F::ZERO }).collect::<Vec<_>>();
let column = self.fixed_column();
let rotation = Rotation::cur();
let expr = Expression::Fixed(FixedQuery {
index: Some(self.query_fixed_index(column, rotation)),
column_index: column.index,
rotation,
});
(poly, expr)
})
.unzip();
self.replace_selectors_with_fixed(&selector_replacements);
self.num_selectors = 0;
(self, polys)
}
fn replace_selectors_with_fixed(&mut self, selector_replacements: &[Expression<F>]) {
fn replace_selectors<F: Field>(
expr: &mut Expression<F>,
selector_replacements: &[Expression<F>],
must_be_nonsimple: bool,
) {
*expr = expr.evaluate(
&|constant| Expression::Constant(constant),
&|selector| {
if must_be_nonsimple {
assert!(!selector.is_simple());
}
selector_replacements[selector.0].clone()
},
&|query| Expression::Fixed(query),
&|query| Expression::Advice(query),
&|query| Expression::Instance(query),
&|challenge| Expression::Challenge(challenge),
&|a| -a,
&|a, b| a + b,
&|a, b| a * b,
&|a, f| a * f,
);
}
for expr in self.gates.iter_mut().flat_map(|gate| gate.polys.iter_mut()) {
replace_selectors(expr, selector_replacements, false);
}
for expr in self.lookups.iter_mut().flat_map(|lookup| {
lookup.input_expressions.iter_mut().chain(lookup.table_expressions.iter_mut())
}) {
replace_selectors(expr, selector_replacements, true);
}
for trash in self.trashcans.iter_mut() {
replace_selectors(&mut trash.selector, selector_replacements, true);
for expr in trash.constraint_expressions.iter_mut() {
replace_selectors(expr, selector_replacements, true);
}
}
}
pub fn selector(&mut self) -> Selector {
let index = self.num_selectors;
self.num_selectors += 1;
Selector(index, true)
}
pub fn complex_selector(&mut self) -> Selector {
let index = self.num_selectors;
self.num_selectors += 1;
Selector(index, false)
}
pub fn lookup_table_column(&mut self) -> TableColumn {
TableColumn {
inner: self.fixed_column(),
}
}
pub fn annotate_lookup_column<A, AR>(&mut self, column: TableColumn, annotation: A)
where
A: Fn() -> AR,
AR: Into<String>,
{
self.general_column_annotations.insert(
metadata::Column::from((Any::Fixed, column.inner().index)),
annotation().into(),
);
}
pub fn annotate_lookup_any_column<A, AR, T>(&mut self, column: T, annotation: A)
where
A: Fn() -> AR,
AR: Into<String>,
T: Into<Column<Any>>,
{
let col_any = column.into();
self.general_column_annotations.insert(
metadata::Column::from((col_any.column_type, col_any.index)),
annotation().into(),
);
}
pub fn fixed_column(&mut self) -> Column<Fixed> {
let tmp = Column {
index: self.num_fixed_columns,
column_type: Fixed,
};
self.num_fixed_columns += 1;
tmp
}
pub fn unblinded_advice_column(&mut self) -> Column<Advice> {
self.unblinded_advice_column_in(FirstPhase)
}
pub fn advice_column(&mut self) -> Column<Advice> {
self.advice_column_in(FirstPhase)
}
pub fn unblinded_advice_column_in<P: Phase>(&mut self, phase: P) -> Column<Advice> {
let phase = phase.to_sealed();
if let Some(previous_phase) = phase.prev() {
self.assert_phase_exists(
previous_phase,
format!("Column<Advice> in later phase {phase:?}").as_str(),
);
}
let tmp = Column {
index: self.num_advice_columns,
column_type: Advice { phase },
};
self.unblinded_advice_columns.push(tmp.index);
self.num_advice_columns += 1;
self.num_advice_queries.push(0);
self.advice_column_phase.push(phase);
tmp
}
pub fn advice_column_in<P: Phase>(&mut self, phase: P) -> Column<Advice> {
let phase = phase.to_sealed();
if let Some(previous_phase) = phase.prev() {
self.assert_phase_exists(
previous_phase,
format!("Column<Advice> in later phase {phase:?}").as_str(),
);
}
let tmp = Column {
index: self.num_advice_columns,
column_type: Advice { phase },
};
self.num_advice_columns += 1;
self.num_advice_queries.push(0);
self.advice_column_phase.push(phase);
tmp
}
pub fn instance_column(&mut self) -> Column<Instance> {
let tmp = Column {
index: self.num_instance_columns,
column_type: Instance,
};
self.num_instance_columns += 1;
tmp
}
pub fn challenge_usable_after<P: Phase>(&mut self, phase: P) -> Challenge {
let phase = phase.to_sealed();
self.assert_phase_exists(
phase,
format!("Challenge usable after phase {phase:?}").as_str(),
);
let tmp = Challenge {
index: self.num_challenges,
phase,
};
self.num_challenges += 1;
self.challenge_phase.push(phase);
tmp
}
fn assert_phase_exists(&self, phase: sealed::Phase, resource: &str) {
self.advice_column_phase
.iter()
.find(|advice_column_phase| **advice_column_phase == phase)
.unwrap_or_else(|| {
panic!(
"No Column<Advice> is used in phase {phase:?} while allocating a new {resource:?}"
)
});
}
pub fn phases(&self) -> impl Iterator<Item = sealed::Phase> {
let max_phase =
self.advice_column_phase.iter().max().map(|phase| phase.0).unwrap_or_default();
(0..=max_phase).map(sealed::Phase)
}
pub fn degree(&self) -> usize {
[
Some(self.permutation.required_degree()),
self.lookups.iter().map(|l| l.required_degree()).max(),
self.trashcans.iter().map(|l| l.required_degree()).max(),
self.gates
.iter()
.flat_map(|gate| gate.polynomials().iter().map(|poly| poly.degree()))
.max(),
self.minimum_degree,
]
.iter()
.filter_map(|&d| d)
.max()
.unwrap_or(1)
}
pub fn blinding_factors(&self) -> usize {
let factors = *self.num_advice_queries.iter().max().unwrap_or(&1);
let factors = std::cmp::max(3, factors);
let factors = factors + self.trashcans.len();
let factors = factors + 1;
let factors = factors + 1;
if factors > i32::MAX as usize {
panic!("Number of blinding factors overflowed max expected value");
}
factors
}
pub fn minimum_rows(&self) -> usize {
self.blinding_factors() + 1 + 1 + 1 }
pub fn num_fixed_columns(&self) -> usize {
self.num_fixed_columns
}
pub fn num_advice_columns(&self) -> usize {
self.num_advice_columns
}
pub fn num_instance_columns(&self) -> usize {
self.num_instance_columns
}
pub fn num_selectors(&self) -> usize {
self.num_selectors
}
pub fn num_challenges(&self) -> usize {
self.num_challenges
}
pub fn advice_column_phase(&self) -> Vec<u8> {
self.advice_column_phase.iter().map(|phase| phase.0).collect()
}
pub fn challenge_phase(&self) -> Vec<u8> {
self.challenge_phase.iter().map(|phase| phase.0).collect()
}
pub fn gates(&self) -> &Vec<Gate<F>> {
&self.gates
}
pub fn general_column_annotations(&self) -> &HashMap<metadata::Column, String> {
&self.general_column_annotations
}
pub fn advice_queries(&self) -> &Vec<(Column<Advice>, Rotation)> {
&self.advice_queries
}
pub fn instance_queries(&self) -> &Vec<(Column<Instance>, Rotation)> {
&self.instance_queries
}
pub fn fixed_queries(&self) -> &Vec<(Column<Fixed>, Rotation)> {
&self.fixed_queries
}
pub fn permutation(&self) -> &permutation::Argument {
&self.permutation
}
pub fn lookups(&self) -> &Vec<lookup::Argument<F>> {
&self.lookups
}
pub fn trashcans(&self) -> &Vec<trash::Argument<F>> {
&self.trashcans
}
pub fn constants(&self) -> &Vec<Column<Fixed>> {
&self.constants
}
}
#[derive(Debug)]
pub struct VirtualCells<'a, F: Field> {
meta: &'a mut ConstraintSystem<F>,
queried_selectors: Vec<Selector>,
queried_cells: Vec<VirtualCell>,
}
impl<'a, F: Field> VirtualCells<'a, F> {
fn new(meta: &'a mut ConstraintSystem<F>) -> Self {
VirtualCells {
meta,
queried_selectors: vec![],
queried_cells: vec![],
}
}
pub fn query_selector(&mut self, selector: Selector) -> Expression<F> {
self.queried_selectors.push(selector);
Expression::Selector(selector)
}
pub fn query_fixed(&mut self, column: Column<Fixed>, at: Rotation) -> Expression<F> {
self.queried_cells.push((column, at).into());
Expression::Fixed(FixedQuery {
index: Some(self.meta.query_fixed_index(column, at)),
column_index: column.index,
rotation: at,
})
}
pub fn query_advice(&mut self, column: Column<Advice>, at: Rotation) -> Expression<F> {
self.queried_cells.push((column, at).into());
Expression::Advice(AdviceQuery {
index: Some(self.meta.query_advice_index(column, at)),
column_index: column.index,
rotation: at,
phase: column.column_type().phase,
})
}
pub fn query_instance(&mut self, column: Column<Instance>, at: Rotation) -> Expression<F> {
self.queried_cells.push((column, at).into());
Expression::Instance(InstanceQuery {
index: Some(self.meta.query_instance_index(column, at)),
column_index: column.index,
rotation: at,
})
}
pub fn query_any<C: Into<Column<Any>>>(&mut self, column: C, at: Rotation) -> Expression<F> {
let column = column.into();
match column.column_type() {
Any::Advice(_) => self.query_advice(Column::<Advice>::try_from(column).unwrap(), at),
Any::Fixed => self.query_fixed(Column::<Fixed>::try_from(column).unwrap(), at),
Any::Instance => self.query_instance(Column::<Instance>::try_from(column).unwrap(), at),
}
}
pub fn query_challenge(&mut self, challenge: Challenge) -> Expression<F> {
Expression::Challenge(challenge)
}
fn apply_selector_to_constraints(&mut self, c: Constraints<F>) -> Vec<Constraint<F>> {
match c.selector {
SelectorType::Multiplicative(s) => {
let q = self.query_selector(s);
c.constraints
.into_iter()
.map(|constraint| Constraint {
name: constraint.name,
poly: q.clone() * constraint.poly,
})
.collect()
}
SelectorType::Additive(s) => {
let q = self.query_selector(s);
let names: Vec<_> = c.constraints.iter().map(|c| c.name.clone()).collect();
let polys: Vec<_> = c.constraints.into_iter().map(|c| c.poly).collect();
(self.meta.trashcans).push(Argument::new(names.join("&"), q, polys));
vec![]
}
SelectorType::None => c.constraints,
}
}
}
#[cfg(test)]
mod tests {
use midnight_curves::Fq as Scalar;
use super::Expression;
#[test]
fn iter_sum() {
let exprs: Vec<Expression<Scalar>> = vec![
Expression::from(1),
Expression::from(2),
Expression::from(3),
];
let happened: Expression<Scalar> = exprs.into_iter().sum();
let expected: Expression<Scalar> = Expression::Sum(
Box::new(Expression::Sum(
Box::new(Expression::from(1)),
Box::new(Expression::from(2)),
)),
Box::new(Expression::from(3)),
);
assert_eq!(happened, expected);
}
#[test]
fn iter_product() {
let exprs: Vec<Expression<Scalar>> = vec![
Expression::from(2),
Expression::from(3),
Expression::from(6),
];
let happened: Expression<Scalar> = exprs.into_iter().product();
let expected: Expression<Scalar> = Expression::Product(
Box::new(Expression::Product(
Box::new(Expression::from(2)),
Box::new(Expression::from(3)),
)),
Box::new(Expression::from(6)),
);
assert_eq!(happened, expected);
}
}