pasta_curves-source/src/plonk/circuit.rs
2020-12-06 07:19:44 +08:00

563 lines
17 KiB
Rust

use core::cmp::max;
use core::ops::{Add, Mul};
use ff::Field;
use std::collections::BTreeMap;
use std::convert::TryFrom;
use super::{lookup, permutation, Error};
use crate::poly::Rotation;
/// A column type
pub trait ColumnType: 'static + Sized {}
/// A column with an index and type
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Column<C: ColumnType> {
index: usize,
column_type: C,
}
impl<C: ColumnType> Column<C> {
pub(crate) fn index(&self) -> usize {
self.index
}
pub(crate) fn column_type(&self) -> &C {
&self.column_type
}
}
/// An advice column
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Advice;
/// A fixed column
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Fixed;
/// An auxiliary column
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Aux;
/// An enum over the Advice, Fixed, Aux structs
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Any {
/// An Advice variant
Advice,
/// A Fixed variant
Fixed,
/// An Auxiliary variant
Aux,
}
impl ColumnType for Advice {}
impl ColumnType for Fixed {}
impl ColumnType for Aux {}
impl ColumnType for Any {}
impl From<Column<Advice>> for Column<Any> {
fn from(advice: Column<Advice>) -> Column<Any> {
Column {
index: advice.index(),
column_type: Any::Advice,
}
}
}
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<Aux>> for Column<Any> {
fn from(advice: Column<Aux>) -> Column<Any> {
Column {
index: advice.index(),
column_type: Any::Aux,
}
}
}
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 => 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<Aux> {
type Error = &'static str;
fn try_from(any: Column<Any>) -> Result<Self, Self::Error> {
match any.column_type() {
Any::Aux => Ok(Column {
index: any.index(),
column_type: Aux,
}),
_ => Err("Cannot convert into Column<Aux>"),
}
}
}
/// This trait allows a [`Circuit`] to direct some backend to assign a witness
/// for a constraint system.
pub trait Assignment<F: Field> {
/// Assign an advice column value (witness)
fn assign_advice(
&mut self,
column: Column<Advice>,
row: usize,
to: impl FnOnce() -> Result<F, Error>,
) -> Result<(), Error>;
/// Assign a fixed value
fn assign_fixed(
&mut self,
column: Column<Fixed>,
row: usize,
to: impl FnOnce() -> Result<F, Error>,
) -> Result<(), Error>;
/// Assign two advice columns to have the same value
fn copy(
&mut self,
permutation: usize,
left_column: usize,
left_row: usize,
right_column: usize,
right_row: usize,
) -> Result<(), Error>;
}
/// This is a trait that circuits provide implementations for so that the
/// backend prover can ask the circuit to synthesize using some given
/// [`ConstraintSystem`] implementation.
pub trait Circuit<F: Field> {
/// This is a configuration object that stores things like columns.
type Config;
/// The circuit is given an opportunity to describe the exact gate
/// arrangement, column arrangement, etc.
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config;
/// Given the provided `cs`, synthesize the circuit. The concrete type of
/// the caller will be different depending on the context, and they may or
/// may not expect to have a witness present.
fn synthesize(&self, cs: &mut impl Assignment<F>, config: Self::Config) -> Result<(), Error>;
}
/// Low-degree expression representing an identity that must hold over the committed columns.
#[derive(Clone, Debug)]
pub enum Expression<F> {
/// This is a fixed column queried at a certain relative location
Fixed(usize),
/// This is an advice (witness) column queried at a certain relative location
Advice(usize),
/// This is an auxiliary (external) column queried at a certain relative location
Aux(usize),
/// This is the sum of two polynomials
Sum(Box<Expression<F>>, Box<Expression<F>>),
/// This is the product of two polynomials
Product(Box<Expression<F>>, Box<Expression<F>>),
/// This is a scaled polynomial
Scaled(Box<Expression<F>>, F),
}
impl<F: Field> Expression<F> {
/// Evaluate the polynomial using the provided closures to perform the
/// operations.
pub fn evaluate<T>(
&self,
fixed_column: &impl Fn(usize) -> T,
advice_column: &impl Fn(usize) -> T,
aux_column: &impl Fn(usize) -> T,
sum: &impl Fn(T, T) -> T,
product: &impl Fn(T, T) -> T,
scaled: &impl Fn(T, F) -> T,
) -> T {
match self {
Expression::Fixed(index) => fixed_column(*index),
Expression::Advice(index) => advice_column(*index),
Expression::Aux(index) => aux_column(*index),
Expression::Sum(a, b) => {
let a = a.evaluate(
fixed_column,
advice_column,
aux_column,
sum,
product,
scaled,
);
let b = b.evaluate(
fixed_column,
advice_column,
aux_column,
sum,
product,
scaled,
);
sum(a, b)
}
Expression::Product(a, b) => {
let a = a.evaluate(
fixed_column,
advice_column,
aux_column,
sum,
product,
scaled,
);
let b = b.evaluate(
fixed_column,
advice_column,
aux_column,
sum,
product,
scaled,
);
product(a, b)
}
Expression::Scaled(a, f) => {
let a = a.evaluate(
fixed_column,
advice_column,
aux_column,
sum,
product,
scaled,
);
scaled(a, *f)
}
}
}
/// Compute the degree of this polynomial
pub fn degree(&self) -> usize {
match self {
Expression::Fixed(_) => 1,
Expression::Advice(_) => 1,
Expression::Aux(_) => 1,
Expression::Sum(a, b) => max(a.degree(), b.degree()),
Expression::Product(a, b) => a.degree() + b.degree(),
Expression::Scaled(poly, _) => poly.degree(),
}
}
}
impl<F> Add for Expression<F> {
type Output = Expression<F>;
fn add(self, rhs: Expression<F>) -> Expression<F> {
Expression::Sum(Box::new(self), Box::new(rhs))
}
}
impl<F> Mul for Expression<F> {
type Output = Expression<F>;
fn mul(self, rhs: Expression<F>) -> Expression<F> {
Expression::Product(Box::new(self), Box::new(rhs))
}
}
impl<F> Mul<F> for Expression<F> {
type Output = Expression<F>;
fn mul(self, rhs: F) -> Expression<F> {
Expression::Scaled(Box::new(self), rhs)
}
}
/// Represents an index into a vector where each entry corresponds to a distinct
/// point that polynomials are queried at.
#[derive(Copy, Clone, Debug)]
pub(crate) struct PointIndex(pub usize);
/// This is a description of the circuit environment, such as the gate, column and
/// permutation arrangements.
#[derive(Debug, Clone)]
pub struct ConstraintSystem<F> {
pub(crate) num_fixed_columns: usize,
pub(crate) num_advice_columns: usize,
pub(crate) num_aux_columns: usize,
pub(crate) gates: Vec<Expression<F>>,
pub(crate) advice_queries: Vec<(Column<Advice>, Rotation)>,
pub(crate) aux_queries: Vec<(Column<Aux>, Rotation)>,
pub(crate) fixed_queries: Vec<(Column<Fixed>, Rotation)>,
// Mapping from a witness vector rotation to the index in the point vector.
pub(crate) rotations: BTreeMap<Rotation, PointIndex>,
// Vector of permutation arguments, where each corresponds to a sequence of columns
// that are involved in a permutation argument.
pub(crate) permutations: Vec<permutation::Argument>,
// Vector of lookup arguments, where each corresponds to a sequence of
// input columns and a sequence of table columns involved in the lookup.
pub(crate) lookups: Vec<lookup::Argument>,
}
impl<F: Field> Default for ConstraintSystem<F> {
fn default() -> ConstraintSystem<F> {
let mut rotations = BTreeMap::new();
rotations.insert(Rotation::default(), PointIndex(0));
ConstraintSystem {
num_fixed_columns: 0,
num_advice_columns: 0,
num_aux_columns: 0,
gates: vec![],
fixed_queries: Vec::new(),
advice_queries: Vec::new(),
aux_queries: Vec::new(),
rotations,
permutations: Vec::new(),
lookups: Vec::new(),
}
}
}
impl<F: Field> ConstraintSystem<F> {
/// Add a permutation argument for some advice columns
pub fn permutation(&mut self, columns: &[Column<Advice>]) -> usize {
let index = self.permutations.len();
if self.permutations.is_empty() {
self.add_rotation(Rotation(-1));
}
for column in columns {
self.query_advice_index(*column, 0);
}
self.permutations
.push(permutation::Argument::new(columns.to_vec()));
index
}
/// Add a lookup argument for some input columns and table columns.
/// The function will panic if the number of input columns and table
/// columns are not the same.
pub fn lookup(
&mut self,
input_columns: &[Column<Any>],
table_columns: &[Column<Any>],
) -> usize {
assert_eq!(input_columns.len(), table_columns.len());
let index = self.lookups.len();
if self.lookups.is_empty() {
self.add_rotation(Rotation(-1));
}
for input in input_columns {
self.query_any_index(*input, 0);
}
for table in table_columns {
self.query_any_index(*table, 0);
}
self.lookups
.push(lookup::Argument::new(input_columns, table_columns));
index
}
fn query_fixed_index(&mut self, column: Column<Fixed>, at: i32) -> usize {
let at = Rotation(at);
self.add_rotation(at);
// Return existing query, if it exists
for (index, fixed_query) in self.fixed_queries.iter().enumerate() {
if fixed_query == &(column, at) {
return index;
}
}
// Make a new query
let index = self.fixed_queries.len();
self.fixed_queries.push((column, at));
index
}
/// Query a fixed column at a relative position
pub fn query_fixed(&mut self, column: Column<Fixed>, at: i32) -> Expression<F> {
Expression::Fixed(self.query_fixed_index(column, at))
}
pub(crate) fn query_advice_index(&mut self, column: Column<Advice>, at: i32) -> usize {
let at = Rotation(at);
self.add_rotation(at);
// Return existing query, if it exists
for (index, advice_query) in self.advice_queries.iter().enumerate() {
if advice_query == &(column, at) {
return index;
}
}
// Make a new query
let index = self.advice_queries.len();
self.advice_queries.push((column, at));
index
}
/// Query an advice column at a relative position
pub fn query_advice(&mut self, column: Column<Advice>, at: i32) -> Expression<F> {
Expression::Advice(self.query_advice_index(column, at))
}
fn query_aux_index(&mut self, column: Column<Aux>, at: i32) -> usize {
let at = Rotation(at);
self.add_rotation(at);
// Return existing query, if it exists
for (index, aux_query) in self.aux_queries.iter().enumerate() {
if aux_query == &(column, at) {
return index;
}
}
// Make a new query
let index = self.aux_queries.len();
self.aux_queries.push((column, at));
index
}
/// Query an auxiliary column at a relative position
pub fn query_aux(&mut self, column: Column<Aux>, at: i32) -> Expression<F> {
Expression::Aux(self.query_aux_index(column, at))
}
fn query_any_index(&mut self, column: Column<Any>, at: i32) -> 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::Aux => self.query_aux_index(Column::<Aux>::try_from(column).unwrap(), at),
}
}
/// Query an Any column at a relative position
pub fn query_any(&mut self, column: Column<Any>, at: i32) -> Expression<F> {
match column.column_type() {
Any::Advice => Expression::Advice(
self.query_advice_index(Column::<Advice>::try_from(column).unwrap(), at),
),
Any::Fixed => Expression::Fixed(
self.query_fixed_index(Column::<Fixed>::try_from(column).unwrap(), at),
),
Any::Aux => {
Expression::Aux(self.query_aux_index(Column::<Aux>::try_from(column).unwrap(), at))
}
}
}
pub(crate) fn get_advice_query_index(&self, column: Column<Advice>, at: i32) -> usize {
let at = Rotation(at);
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: i32) -> usize {
let at = Rotation(at);
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_aux_query_index(&self, column: Column<Aux>, at: i32) -> usize {
let at = Rotation(at);
for (index, aux_query) in self.aux_queries.iter().enumerate() {
if aux_query == &(column, at) {
return index;
}
}
panic!("get_aux_query_index called for non-existent query");
}
pub(crate) fn get_any_query_index(&self, column: Column<Any>, at: i32) -> 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::Aux => self.get_aux_query_index(Column::<Aux>::try_from(column).unwrap(), at),
}
}
/// Create a new gate
pub fn create_gate(&mut self, f: impl FnOnce(&mut Self) -> Expression<F>) {
let poly = f(self);
self.gates.push(poly);
}
/// Allocate a new fixed column
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
}
/// Allocate a new advice column
pub fn advice_column(&mut self) -> Column<Advice> {
let tmp = Column {
index: self.num_advice_columns,
column_type: Advice,
};
self.num_advice_columns += 1;
tmp
}
/// Allocate a new auxiliary column
pub fn aux_column(&mut self) -> Column<Aux> {
let tmp = Column {
index: self.num_aux_columns,
column_type: Aux,
};
self.num_aux_columns += 1;
tmp
}
fn add_rotation(&mut self, at: Rotation) {
let len = self.rotations.len();
self.rotations.entry(at).or_insert(PointIndex(len));
}
}