From bb55c8444280ba5cf67460dc7453ccc5ab93b0c7 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Tue, 12 Mar 2024 18:46:47 +0800 Subject: [PATCH 01/24] ccs: first commit --- src/ccs/mod.rs | 1 + src/lib.rs | 1 + 2 files changed, 2 insertions(+) create mode 100644 src/ccs/mod.rs diff --git a/src/ccs/mod.rs b/src/ccs/mod.rs new file mode 100644 index 00000000..db89e612 --- /dev/null +++ b/src/ccs/mod.rs @@ -0,0 +1 @@ +pub mod circuit; diff --git a/src/lib.rs b/src/lib.rs index 853e71ca..d1f44fe6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +pub mod ccs; pub mod field; pub mod frontend; pub mod pil; From 3f45e673bebb856144976df29f51ad3ec1f36727 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Wed, 13 Mar 2024 18:19:21 +0800 Subject: [PATCH 02/24] ccs: minor --- src/ccs/circuit.rs | 290 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 src/ccs/circuit.rs diff --git a/src/ccs/circuit.rs b/src/ccs/circuit.rs new file mode 100644 index 00000000..b3980b5d --- /dev/null +++ b/src/ccs/circuit.rs @@ -0,0 +1,290 @@ +use halo2_proofs::arithmetic::Field; +use std::hash::Hash; + +pub fn hadamard_product + Hash>(vec1: &Vec, vec2: &Vec) -> Vec { + assert_eq!(vec1.len(), vec2.len()); + vec1.iter() + .zip(vec2.iter()) + .map(|(&v1, &v2)| v1 * v2) + .collect() +} + +pub fn vec_add + Hash>(vec: &Vec>) -> Vec { + assert!(vec.len() > 1); + vec.iter().fold(vec![F::ZERO; vec[0].len()], |acc, vec| { + acc.iter().zip(vec.iter()).map(|(&a, &v)| a + v).collect() + }) +} + +// input vector z = [1, x, w] +pub struct Z> { + n: usize, + l: usize, + values: Vec, +} + +impl + Hash> Z { + pub fn new(n: usize, l: usize) -> Self { + assert!(n > l); + Self { + n, + l, + values: Vec::new(), + } + } + + pub fn write(&mut self, inputs: &Vec, witnesses: &Vec) { + assert_eq!(inputs.len(), self.l); + assert_eq!(witnesses.len(), self.n - self.l - 1); + + // todo: order + let mut witnesses = witnesses.clone(); + let mut inputs = inputs.clone(); + let mut values = vec![F::ONE]; + values.append(&mut inputs); + values.append(&mut witnesses); + self.values = values; + } +} + +pub struct Matrix { + n: usize, + m: usize, + values: Vec>, +} + +impl + Hash> Matrix { + pub fn new(n: usize, m: usize) -> Self { + Self { + n, + m, + values: Vec::new(), + } + } + + pub fn write(&mut self, values: &[(usize, usize, F)]) { + let mut matrix: Vec> = (0..self.m) + .map(|_| (0..self.n).map(|_| F::ZERO).collect()) + .collect(); + for &(row, col, value) in values.iter() { + assert!(row < self.m); + assert!(col < self.n); + matrix[row][col] = value; + } + self.values = matrix.clone(); + } + + pub fn get(&self, row: usize, col: usize) -> F { + assert!(row < self.m); + assert!(col < self.n); + self.values[row][col] + } + + pub fn size(&self) -> (usize, usize) { + (self.m, self.n) + } +} + +impl + Hash> Matrix { + pub fn matrix_vector_product(&self, z: &Vec) -> Vec { + (0..self.values.len()) + .map(|i| self.hadamard_product_and_sum(i, z)) + .collect() + } + + fn hadamard_product_and_sum(&self, index: usize, z: &Vec) -> F { + assert!(index < self.values.len()); + assert_eq!(self.values[index].len(), z.len()); + hadamard_product(&self.values[index], z).iter().sum() + } +} + +pub struct CCSCircuit> { + n: usize, + m: usize, + nn: usize, + l: usize, + t: usize, + q: usize, + d: usize, + matrices: Vec>, + selectors: Vec>, + constants: Vec, +} + +impl + Hash> CCSCircuit { + pub fn new(n: usize, m: usize, t: usize, q: usize, l: usize) -> Self { + assert!(n > l); + + let matrices = (0..t).map(|_| Matrix::new(n, m)).collect(); + + let selectors = (0..q).map(|_| Vec::new()).collect(); + + let constants = (0..q).map(|_| F::ZERO).collect(); + + let nn = 0; + + Self { + n, + m, + l, + t, + q, + d: 0, + nn, + matrices, + selectors, + constants, + } + } + + pub fn read_nn(&self) -> usize { + self.nn + } + + pub fn public_num(&self) -> usize { + self.l + } + + pub fn witness_num(&self) -> usize { + self.n - self.l - 1 + } + + pub fn write( + &mut self, + matrices: &Vec>, + selectors: &Vec>, + constants: &Vec, + ) { + let mut degree = 0; + let mut nn = 0; + + assert_eq!(constants.len(), self.q); + self.constants = constants.clone(); + + assert_eq!(selectors.len(), self.q); + for selector in selectors.iter() { + for &s in selector { + assert!(s < self.t) + } + degree = degree.max(selector.len()) + } + self.selectors = selectors.clone(); + + assert_eq!(matrices.len(), self.t); + + self.matrices = matrices + .iter() + .map(|cells| { + for &cell in cells.iter() { + assert!(cell.0 < self.m); + assert!(cell.1 < self.n); + if cell.2 != F::ZERO { + nn += 1; + } + } + + let mut matrix = Matrix::new(self.n, self.m); + matrix.write(cells); + matrix + }) + .collect(); + + self.d = degree; + self.nn = nn; + } + + pub fn is_satisfied(&self, z: &Z) -> bool { + assert_eq!(self.selectors.len(), self.q); + assert_eq!(self.constants.len(), self.q); + + let prod_vec: Vec> = self + .constants + .iter() + .zip(self.selectors.iter()) + .map(|(&c, selector)| { + let hadamard_prod_vec: Vec> = selector + .iter() + .map(|&s| { + assert!(s < self.matrices.len()); + self.matrices[s].matrix_vector_product(&z.values) + }) + .collect(); + hadamard_prod_vec + .iter() + .fold(vec![c; self.m], |acc, vec| hadamard_product(&acc, vec)) + }) + .collect(); + let sum_vec = vec_add(&prod_vec); + + assert_eq!(sum_vec.len(), self.m); + + for &sum in sum_vec.iter() { + if sum != F::ZERO { + return false; + } + } + true + } +} + +#[cfg(test)] +mod tests { + use std::vec; + + use super::*; + use halo2_proofs::halo2curves::bn256::Fr; + + #[test] + fn test_ccs() { + let n = 7; + let m = 4; + let t = 8; + let q = 5; + let l = 3; + + let mut ccs_circuit: CCSCircuit = CCSCircuit::new(n, m, t, q, l); + let m0 = vec![ + (0, 1, Fr::ONE), + (1, 2, Fr::ONE), + (2, 3, Fr::ONE), + (3, 6, Fr::ONE), + ]; + let m1 = vec![ + (0, 1, Fr::ONE), + (1, 2, Fr::ONE), + (2, 4, Fr::ONE), + (3, 6, Fr::ONE), + ]; + let m2 = vec![ + (0, 1, Fr::ONE), + (1, 2, Fr::ONE), + (2, 5, Fr::ONE), + (3, 6, Fr::ONE), + ]; + let m3 = vec![(0, 0, Fr::ONE), (1, 0, Fr::ONE)]; + let m4 = vec![(2, 0, Fr::from(2))]; + let m5 = vec![(2, 0, Fr::from(2))]; + let m6 = vec![ + (0, 0, Fr::ONE.neg()), + (1, 0, Fr::ONE.neg()), + (2, 0, Fr::ONE.neg()), + ]; + let m7 = vec![(0, 0, Fr::ZERO)]; + let matrices = vec![m0, m1, m2, m3, m4, m5, m6, m7]; + + let selectors = vec![vec![3, 0, 1], vec![4, 0], vec![5, 1], vec![6, 2], vec![7]]; + let constants: Vec = vec![Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE]; + ccs_circuit.write(&matrices, &selectors, &constants); + + let mut z = Z::new(n, l); + z.write( + &vec![Fr::ZERO, Fr::ONE, Fr::from(2)], + &vec![Fr::from(3), Fr::from(10), Fr::from(43)], + ); + + let result = ccs_circuit.is_satisfied(&z); + + println!("result = {}", result); + } +} From 14f8931e40c6b0b8650bd66f37e7a11296acc093 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Wed, 3 Apr 2024 00:57:20 +0800 Subject: [PATCH 03/24] tmp update --- src/plonkish/backend/ccs.rs | 56 +++++++++++++++++++++++++++++++++++++ src/plonkish/backend/mod.rs | 1 + 2 files changed, 57 insertions(+) create mode 100644 src/plonkish/backend/ccs.rs diff --git a/src/plonkish/backend/ccs.rs b/src/plonkish/backend/ccs.rs new file mode 100644 index 00000000..9af2881a --- /dev/null +++ b/src/plonkish/backend/ccs.rs @@ -0,0 +1,56 @@ + + +use std::hash::Hash; + +use halo2_proofs::{arithmetic::Field, plonk::Assignment}; + +use crate::{ + plonkish::ir::{Circuit, assignments::Assignments}, + util::UUID, + ccs::circuit::CCSCircuit, +}; + + +pub struct ChiquitoCCS> { + pub debug: bool, + circuit: Circuit, + ir_id: UUID, + // ccs: +} + +impl + Hash> ChiquitoCCS { + pub fn new(circuit: Circuit) -> ChiquitoCCS { + let ir_id = circuit.ast_id; + Self { + debug: true, + circuit, + ir_id, + } + } + + pub fn configure(&mut self) { + + } +} + + +pub struct ChiquitoCCSCircuit> { + compiled: ChiquitoCCS, + witness: Option>, +} + +impl + Hash> ChiquitoCCSCircuit { + pub fn new(compiled: ChiquitoCCS, witness: Option>) -> Self { + Self { compiled, witness } + } + + pub fn instance(&self) { + + } +} + + +#[allow(non_snake_case)] +pub fn chiquito2CCS + Hash>(circuit: Circuit) -> ChiquitoCCS { + ChiquitoCCS::new(circuit) +} \ No newline at end of file diff --git a/src/plonkish/backend/mod.rs b/src/plonkish/backend/mod.rs index 84f7c9f2..f53a7068 100644 --- a/src/plonkish/backend/mod.rs +++ b/src/plonkish/backend/mod.rs @@ -1,2 +1,3 @@ pub mod halo2; pub mod plaf; +pub mod ccs; \ No newline at end of file From 90e4b567db64dba0b16e6ee7357f500778bb6965 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Fri, 14 Jun 2024 01:47:36 +0800 Subject: [PATCH 04/24] ccs: first commit --- Cargo.toml | 7 + examples/fibonacci_ccs.rs | 211 ++++++++++++ rust-toolchain | 2 +- src/ccs/backend/ccs.rs | 524 ++++++++++++++++++++++++++++++ src/ccs/backend/mod.rs | 1 + src/ccs/circuit.rs | 290 ----------------- src/ccs/compiler/cell_manager.rs | 170 ++++++++++ src/ccs/compiler/mod.rs | 232 +++++++++++++ src/ccs/compiler/step_selector.rs | 91 ++++++ src/ccs/compiler/unit.rs | 116 +++++++ src/ccs/ir/assignments.rs | 261 +++++++++++++++ src/ccs/ir/circuit.rs | 93 ++++++ src/ccs/ir/mod.rs | 83 +++++ src/ccs/mod.rs | 4 +- src/plonkish/backend/ccs.rs | 56 ---- src/plonkish/backend/mod.rs | 1 - 16 files changed, 1793 insertions(+), 349 deletions(-) create mode 100644 examples/fibonacci_ccs.rs create mode 100644 src/ccs/backend/ccs.rs create mode 100644 src/ccs/backend/mod.rs delete mode 100644 src/ccs/circuit.rs create mode 100644 src/ccs/compiler/cell_manager.rs create mode 100644 src/ccs/compiler/mod.rs create mode 100644 src/ccs/compiler/step_selector.rs create mode 100644 src/ccs/compiler/unit.rs create mode 100644 src/ccs/ir/assignments.rs create mode 100644 src/ccs/ir/circuit.rs create mode 100644 src/ccs/ir/mod.rs delete mode 100644 src/plonkish/backend/ccs.rs diff --git a/Cargo.toml b/Cargo.toml index b5622a6a..57abd130 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ authors = ["Leo Lara "] [patch.crates-io] halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v0.3.0" } +ark-grumpkin = { git = "https://github.com/arnaucube/ark-curves-cherry-picked", branch="cherry-pick"} [patch."https://github.com/scroll-tech/halo2.git"] halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v0.3.0" } @@ -30,5 +31,11 @@ hyperplonk_benchmark = { git = "https://github.com/qwang98/plonkish.git", branch plonkish_backend = { git = "https://github.com/qwang98/plonkish.git", branch = "main", package = "plonkish_backend" } regex = "1" +ark-ff = "^0.4.0" +ark-std = "^0.4.0" +ark-bn254 = "0.4.0" +folding-schemes = { git = "https://github.com/privacy-scaling-explorations/sonobe.git", commit = "ed1faee6bfdf9eb484a0b5bc6196896cc250abed"} + + [dev-dependencies] rand_chacha = "0.3" diff --git a/examples/fibonacci_ccs.rs b/examples/fibonacci_ccs.rs new file mode 100644 index 00000000..129a2bf0 --- /dev/null +++ b/examples/fibonacci_ccs.rs @@ -0,0 +1,211 @@ +use std::hash::Hash; + +use chiquito::{ + ccs::{ + backend::ccs::{chiquito2CCS, ChiquitoCCSCircuit}, + compiler::{ + cell_manager::SingleRowCellManager, + compile, // input for constructing the compiler + config, + step_selector::SimpleStepSelectorBuilder, + }, + ir::{assignments::AssignmentGenerator, circuit::Circuit}, + }, + field::Field, + frontend::dsl::circuit, + poly::ToField, + sbpir::SBPIR, +}; + +// the main circuit function: returns the compiled IR of a Chiquito circuit +// Generic types F, (), (u64, 64) stand for: +// 1. type that implements a field trait +// 2. empty trace arguments, i.e. (), because there are no external inputs to the Chiquito circuit +// 3. two witness generation arguments both of u64 type, i.e. (u64, u64) + +type FiboReturn = (Circuit, Option>, SBPIR); + +fn fibo_circuit_ccs + Hash>() -> FiboReturn { + // PLONKish table for the Fibonacci circuit: + // | a | b | c | + // | 1 | 1 | 2 | + // | 1 | 2 | 3 | + // | 2 | 3 | 5 | + // | 3 | 5 | 8 | + // ... + + use chiquito::frontend::dsl::cb::*; // functions for constraint building + + let fibo = circuit::("fibonacci", |ctx| { + // the following objects (forward signals, steptypes) are defined on the circuit-level + + // forward signals can have constraints across different steps + let a = ctx.forward("a"); + let b = ctx.forward("b"); + + // define step type + let fibo_step = ctx.step_type_def("fibo step", |ctx| { + // the following objects (constraints, transition constraints, witness generation + // function) are defined on the step type-level + + // internal signals can only have constraints within the same step + let c = ctx.internal("c"); + + // in setup we define the constraints of the step + ctx.setup(move |ctx| { + // regular constraints are for signals without rotation only + + // `auto_eq` creates a constraint and also auto generates the witness of the left + // side. + ctx.auto_eq(c, a + b); + + // transition constraints accepts forward signals as well + // constrain that b is equal to the next instance of a by calling `eq` function from + // constraint builder and `next` on forward signal + ctx.transition(eq(b, a.next())); + // constrain that c is equal to the next instance of c, by calling `next` on forward + // signal + ctx.transition(eq(c, b.next())); + }); + + // witness generation (wg) function is Turing complete and allows arbitrary user defined + // logics for assigning witness values wg function is defined here but no + // witness value is assigned yet + ctx.wg(move |ctx, (a_value, b_value): (u32, u32)| { + // println!("fib line wg: {} {} {}", a_value, b_value, a_value + b_value); + // assign arbitrary input values from witness generation function to witnesses + ctx.assign(a, a_value.field()); + ctx.assign(b, b_value.field()); + + // c is auto generated by `auto_eq` + }) + }); + + ctx.pragma_num_steps(16); + + // trace function is responsible for adding step instantiations defined in step_type_def + // function above trace function is Turing complete and allows arbitrary user + // defined logics for assigning witness values + ctx.trace(move |ctx: _, _| { + // add function adds a step instantiation to the main circuit and calls witness + // generation function defined in step_type_def input values for witness + // generation function are (1, 1) in this step instance + ctx.add(&fibo_step, (1, 1)); + let mut a = 1; + let mut b = 2; + + for _i in 1..16 { + ctx.add(&fibo_step, (a, b)); + + let prev_a = a; + a = b; + b += prev_a; + } + }) + }); + + let compiled = compile( + config(SingleRowCellManager {}, SimpleStepSelectorBuilder {}), + &fibo, + ); + + (compiled.0, compiled.1, fibo) +} + +// After compiling Chiquito AST to an IR, it is further parsed by a Chiquito Halo2 backend and +// integrated into a Halo2 circuit, which is done by the boilerplate code below. + +use ark_bn254::Fr as FFr; +use ark_std::log2; +use folding_schemes::{ccs::CCS, utils::vec::dense_matrix_to_sparse}; +use halo2_proofs::halo2curves::{bn256::Fr, ff::PrimeField}; +fn main() { + let (chiquito, wit_gen, _) = fibo_circuit_ccs::(); + let compiled = chiquito2CCS(chiquito); + + let (witness, steps) = wit_gen.map(|g| g.generate(())).unzip(); + let circuit = ChiquitoCCSCircuit::new(compiled, witness, steps); + + let (ccs, z) = circuit.configure(); + let result = ccs.is_satisfied(&z); + println!("fibonacci {:#?}", result); + + let (chiquito, wit_gen, _) = fibo_circuit_ccs::(); + let compiled = chiquito2CCS(chiquito); + let (witness, steps) = wit_gen.map(|g| g.generate(())).unzip(); + let circuit = ChiquitoCCSCircuit::new(compiled, witness, steps); + let (circuit, z) = circuit.configure(); + + let ccs = CCS { + m: circuit.m, + n: circuit.n, + l: circuit.l, + t: circuit.t, + q: circuit.q, + d: circuit.d, + s: log2(circuit.m) as usize, + s_prime: log2(circuit.n) as usize, + M: circuit + .matrics + .iter() + .map(|matrix| { + let values = matrix + .values() + .iter() + .map(|m| { + m.iter() + .map(|r| { + // todo() + if Fr::ONE.neg().eq(r) { + FFr::from(-1) + } else { + let mut array = [0u8; 8]; + array.copy_from_slice(&r.to_repr().as_ref()[0..8]); + FFr::from(u64::from_le_bytes(array)) + } + }) + .collect() + }) + .collect(); + dense_matrix_to_sparse(values) + }) + .collect(), + S: circuit + .selectors + .iter() + .map(|selectors| selectors.iter().map(|(idx, _)| *idx).collect()) + .collect(), + c: (0..circuit.q).map(|_| FFr::from(1)).collect(), + }; + + let assignments = z + .assignments + .iter() + .map(|r| { + if Fr::ONE.neg().eq(r) { + FFr::from(-1) + } else { + let mut array = [0u8; 8]; + array.copy_from_slice(&r.to_repr().as_ref()[0..8]); + FFr::from(u64::from_le_bytes(array)) + } + }) + .collect(); + let public_inputs = z + .public_inputs + .iter() + .map(|r| { + if Fr::ONE.neg().eq(r) { + FFr::from(-1) + } else { + let mut array = [0u8; 8]; + array.copy_from_slice(&r.to_repr().as_ref()[0..8]); + FFr::from(u64::from_le_bytes(array)) + } + }) + .collect(); + let inputs = [vec![FFr::from(1)], assignments, public_inputs].concat(); + + let result = ccs.check_relation(&inputs); + println!("sonobe fibonacci = {:?}", result); +} diff --git a/rust-toolchain b/rust-toolchain index c5f61037..32a59aa4 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2024-02-14 +nightly-2024-02-13 diff --git a/src/ccs/backend/ccs.rs b/src/ccs/backend/ccs.rs new file mode 100644 index 00000000..41f25509 --- /dev/null +++ b/src/ccs/backend/ccs.rs @@ -0,0 +1,524 @@ +use std::{collections::HashMap, hash::Hash}; + +use crate::{ + ccs::ir::{ + assignments::{Assignments, StepsID}, + circuit::Circuit, + }, + field::Field, + util::UUID, +}; + +pub type PositionMap = HashMap<(usize, u128), usize>; + +#[allow(non_snake_case)] +pub fn chiquito2CCS + Hash>(circuit: Circuit) -> ChiquitoCCS { + ChiquitoCCS::new(circuit) +} + +pub struct ChiquitoCCS> { + pub debug: bool, + pub circuit: Circuit, + pub ir_id: UUID, +} + +impl + Hash> ChiquitoCCS { + pub fn new(circuit: Circuit) -> ChiquitoCCS { + let ir_id = circuit.ast_id; + Self { + debug: true, + circuit, + ir_id, + } + } +} + +pub struct ChiquitoCCSCircuit> { + pub compiled: ChiquitoCCS, + pub steps_id: Option, + pub witness: Option>, +} + +impl + Hash> ChiquitoCCSCircuit { + pub fn new( + compiled: ChiquitoCCS, + witness: Option>, + steps_id: Option, + ) -> Self { + Self { + compiled, + witness, + steps_id, + } + } + + pub fn instance(&self) -> HashMap<(usize, UUID), F> { + if !self.compiled.circuit.exposed.is_empty() { + if let Some(witness) = &self.witness { + return self.compiled.circuit.instance(witness); + } + } + HashMap::new() + } +} + +impl + Hash> ChiquitoCCSCircuit { + pub fn configure(&self) -> (CCSCircuit, Z) { + let (ccs, witness_pos, public_pos) = CCSCircuit::from_circuit(self); + let mut z: Z = Z::new(ccs.n, ccs.l); + + z.write( + &self.instance(), + self.witness.as_deref().unwrap(), + &witness_pos, + &public_pos, + ); + (ccs, z) + } +} + +#[derive(Default)] +pub struct CCSCircuit { + pub n: usize, + pub m: usize, + pub l: usize, + pub t: usize, + pub q: usize, + pub d: usize, + pub matrics: Vec>, + pub selectors: Vec>, + pub constants: Vec, +} + +impl + Hash> CCSCircuit { + pub fn new(n: usize, m: usize, t: usize, q: usize, l: usize, d: usize) -> Self { + assert!(n > l); + + let matrics = Vec::new(); + + let selectors = (0..q).map(|_| Vec::new()).collect(); + + let constants = (0..q).map(|_| F::ZERO).collect(); + + Self { + n, + m, + l, + t, + q, + d, + matrics, + selectors, + constants, + } + } + + pub fn from_circuit(circuit: &ChiquitoCCSCircuit) -> (Self, PositionMap, PositionMap) { + let mut m: usize = circuit + .steps_id + .as_ref() + .map(|steps_idx| { + steps_idx + .0 + .iter() + .map(|idx| circuit.compiled.circuit.matrics.get(idx).unwrap().len()) + .sum() + }) + .unwrap(); + let selectors = circuit.compiled.circuit.selectors.clone(); + let constants = (0..circuit.compiled.circuit.q).map(|_| F::ONE).collect(); + + let mut matrix_num = 0; + for selector in selectors.iter() { + for (idx, _) in selector.iter() { + matrix_num = matrix_num.max(*idx + 1); + } + } + + let mut witness_pos = HashMap::new(); + let mut offset = 0; + witness_pos.insert((0, 0), offset); // 1 + offset += 1; + if let Some(steps_id) = circuit.steps_id.as_ref() { + for (idx, step_idx) in steps_id.0.iter().enumerate() { + let witnesses = circuit.compiled.circuit.witness.get(step_idx).unwrap(); + for id in witnesses.iter() { + witness_pos.insert((idx, *id), offset); + offset += 1; + } + } + } + let mut public_pos = HashMap::new(); + for (idx, id) in circuit.compiled.circuit.exposed.iter() { + public_pos.insert((*idx, *id), offset); + offset += 1; + } + // todo: remove public values + + let n = witness_pos.len() + public_pos.len(); + let mut matrix_vec = vec![Matrix::new(n, m); matrix_num]; + + let mut row = 0; + if let Some(steps_id) = circuit.steps_id.as_ref() { + for (idx, step_id) in steps_id.0.iter().enumerate() { + // step + let matrics = circuit.compiled.circuit.matrics.get(step_id).unwrap(); + + for matrics in matrics.iter() { + // poly + if idx == steps_id.0.len() - 1 { + let mut skip = false; + for (matrics, _) in matrics.iter() { + if skip { + break; + } + for (_, _, next) in matrics.concat().iter() { + if *next { + skip = true; + break; + } + } + } + if skip { + m -= 1; + continue; + } + } + + for (matrix_chunks, index) in matrics.iter() { + // chunk + assert!(*index <= selectors.len()); + let selectors = selectors[*index].clone(); + assert_eq!(matrix_chunks.len(), selectors.len()); + + for (items, (selector, _)) in matrix_chunks.iter().zip(selectors.iter()) { + // one row in on matrix + let mut values: Vec<(usize, usize, F)> = Vec::new(); + for (v, id, next) in items.iter() { + let idx = if *next { idx + 1 } else { idx }; + let col = if *id == 0 { + witness_pos.get(&(0, 0)) + } else { + witness_pos.get(&(idx, *id)) + }; + match col { + None => continue, + Some(col) => values.push((row, *col, *v)), + } + } + matrix_vec[*selector].write(&values); + } + } + row += 1; + } + } + }; + + for matrix in matrix_vec.iter_mut() { + if row < matrix.m { + matrix.remove_rows(row); + } + } + + ( + Self { + n, + m, + l: public_pos.len(), + t: circuit.compiled.circuit.t, + q: circuit.compiled.circuit.q, + d: circuit.compiled.circuit.d, + matrics: matrix_vec, + selectors, + constants, + }, + witness_pos, + public_pos, + ) + } + + pub fn public_num(&self) -> usize { + self.l + } + + pub fn witness_num(&self) -> usize { + self.n - self.l - 1 + } + + pub fn write( + &mut self, + matrics: &[Vec<(usize, usize, F)>], + selectors: &[Vec<(usize, F)>], + constants: &[F], + ) { + let mut degree = 0; + let mut nn = 0; + + assert_eq!(constants.len(), self.q); + self.constants = constants.to_owned().clone(); + + assert_eq!(selectors.len(), self.q); + for selector in selectors.iter() { + for &(s, _) in selector { + assert!(s < self.t) + } + degree = degree.max(selector.len()) + } + self.selectors = selectors.to_owned().clone(); + + assert_eq!(matrics.len(), self.t); + + self.matrics = matrics + .iter() + .map(|cells| { + for &cell in cells.iter() { + assert!(cell.0 < self.m); + assert!(cell.1 < self.n); + if cell.2 != F::ZERO { + nn += 1; + } + } + + let mut matrix = Matrix::new(self.n, self.m); + matrix.write(cells); + matrix + }) + .collect(); + + self.d = degree; + } + + pub fn is_satisfied(&self, z: &Z) -> bool { + assert_eq!(self.selectors.len(), self.q); + assert_eq!(self.constants.len(), self.q); + + let mut witnesses = z.assignments.clone(); + let mut inputs = z.public_inputs.clone(); + + let mut values = vec![F::ONE]; + values.append(&mut inputs); + values.append(&mut witnesses); + + let prod_vec: Vec> = self + .constants + .iter() + .zip(self.selectors.iter()) + .map(|(&c, selector)| { + let hadamard_prod_vec: Vec> = selector + .iter() + .map(|&(s, _)| { + assert!(s < self.matrics.len()); + self.matrics[s].matrix_vector_product(&values) + }) + .collect(); + hadamard_prod_vec + .iter() + .fold(vec![c; self.m], |acc, vec| hadamard_product(&acc, vec)) + }) + .collect(); + let sum_vec = vec_add(&prod_vec); + + assert_eq!(sum_vec.len(), self.m); + + for &sum in sum_vec.iter() { + if sum != F::ZERO { + return false; + } + } + + true + } +} + +pub fn hadamard_product + Hash>(vec1: &[F], vec2: &[F]) -> Vec { + assert_eq!(vec1.len(), vec2.len()); + vec1.iter() + .zip(vec2.iter()) + .map(|(&v1, &v2)| v1 * v2) + .collect() +} + +pub fn vec_add + Hash>(vec: &[Vec]) -> Vec { + assert!(vec.len() > 1); + vec.iter().fold(vec![F::ZERO; vec[0].len()], |acc, vec| { + acc.iter().zip(vec.iter()).map(|(&a, &v)| a + v).collect() + }) +} + +#[derive(Debug, Clone)] +pub struct Matrix { + n: usize, + m: usize, + values: Vec>, +} + +impl Matrix { + pub fn new(n: usize, m: usize) -> Self { + Self { + n, + m, + values: vec![vec![F::ZERO; n]; m], + } + } + + pub fn write(&mut self, values: &[(usize, usize, F)]) { + for &(row, col, value) in values.iter() { + assert!(row < self.m); + assert!(col < self.n); + self.values[row][col] = value; + } + } + + pub fn get(&self, row: usize, col: usize) -> F { + assert!(row < self.m); + assert!(col < self.n); + self.values[row][col] + } + + pub fn remove_rows(&mut self, m: usize) { + assert!(m <= self.m); + self.values = self.values[0..m].to_owned(); + self.m = m; + } + + pub fn size(&self) -> (usize, usize) { + (self.m, self.n) + } + + pub fn values(&self) -> Vec> { + self.values.clone() + } +} + +impl + Hash> Matrix { + pub fn matrix_vector_product(&self, z: &[F]) -> Vec { + (0..self.values.len()) + .map(|i| self.hadamard_product_and_sum(i, z)) + .collect() + } + + fn hadamard_product_and_sum(&self, index: usize, z: &[F]) -> F { + assert!(index < self.values.len()); + assert_eq!(self.values[index].len(), z.len()); + hadamard_product(&self.values[index], z).iter().sum() + } +} + +// input vector z = [1, x, w] +pub struct Z> { + pub n: usize, + pub l: usize, + pub assignments: Vec, + pub public_inputs: Vec, +} + +impl + Hash> Z { + pub fn new(n: usize, l: usize) -> Self { + assert!(n > l); + Self { + n, + l, + assignments: Default::default(), + public_inputs: Default::default(), + } + } + + pub fn write( + &mut self, + inputs: &HashMap<(usize, UUID), F>, + witnesses: &[HashMap], + witness_pos: &PositionMap, + public_pos: &PositionMap, + ) { + assert_eq!(inputs.len(), self.l); + + let mut witness_values = vec![F::ZERO; witness_pos.len() - 1]; + for ((step_idx, signal_id), idx) in witness_pos.iter() { + if *signal_id == 0 { + continue; + } + witness_values[idx - 1] = *witnesses[*step_idx].get(signal_id).unwrap(); + } + let mut public_values = vec![F::ZERO; public_pos.len()]; + for (pos, idx) in public_pos.iter() { + public_values[idx - witness_pos.len()] = *inputs.get(pos).unwrap(); + } + self.assignments = witness_values.clone(); + self.public_inputs = public_values.clone(); + } + + pub fn write_with_values(&mut self, inputs: &[F], witnesses: &[F]) { + assert_eq!(inputs.len(), self.l); + assert_eq!(witnesses.len(), self.n - self.l - 1); + + self.public_inputs = inputs.to_owned().clone(); + self.assignments = witnesses.to_owned().clone(); + } +} + +#[cfg(test)] +mod tests { + use std::vec; + + use super::*; + use halo2_proofs::halo2curves::bn256::Fr; + + #[test] + fn test_ccs() { + let n = 7; + let m = 4; + let t = 8; + let q = 5; + let l = 3; + + let mut ccs_circuit: CCSCircuit = CCSCircuit::new(n, m, t, q, l, 0); + let m0 = vec![ + (0, 1, Fr::ONE), + (1, 2, Fr::ONE), + (2, 3, Fr::ONE), + (3, 6, Fr::ONE), + ]; + let m1 = vec![ + (0, 1, Fr::ONE), + (1, 2, Fr::ONE), + (2, 4, Fr::ONE), + (3, 6, Fr::ONE), + ]; + let m2 = vec![ + (0, 1, Fr::ONE), + (1, 2, Fr::ONE), + (2, 5, Fr::ONE), + (3, 6, Fr::ONE), + ]; + let m3 = vec![(0, 0, Fr::ONE), (1, 0, Fr::ONE)]; + let m4 = vec![(2, 0, Fr::from(2))]; + let m5 = vec![(2, 0, Fr::from(2))]; + let m6 = vec![ + (0, 0, Fr::ONE.neg()), + (1, 0, Fr::ONE.neg()), + (2, 0, Fr::ONE.neg()), + ]; + let m7 = vec![(0, 0, Fr::ZERO)]; + let matrics = vec![m0, m1, m2, m3, m4, m5, m6, m7]; + + let selectors = vec![ + vec![(3, Fr::ONE), (0, Fr::ONE), (1, Fr::ONE)], + vec![(4, Fr::ONE), (0, Fr::ONE)], + vec![(5, Fr::ONE), (1, Fr::ONE)], + vec![(6, Fr::ONE), (2, Fr::ONE)], + vec![(7, Fr::ONE)], + ]; + let constants: Vec = vec![Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE]; + ccs_circuit.write(&matrics, &selectors, &constants); + + let mut z = Z::new(n, l); + z.write_with_values( + &[Fr::ZERO, Fr::ONE, Fr::from(2)], + &[Fr::from(3), Fr::from(10), Fr::from(43)], + ); + + let result = ccs_circuit.is_satisfied(&z); + + println!("result = {}", result); + } +} diff --git a/src/ccs/backend/mod.rs b/src/ccs/backend/mod.rs new file mode 100644 index 00000000..ce82ffd9 --- /dev/null +++ b/src/ccs/backend/mod.rs @@ -0,0 +1 @@ +pub mod ccs; diff --git a/src/ccs/circuit.rs b/src/ccs/circuit.rs deleted file mode 100644 index b3980b5d..00000000 --- a/src/ccs/circuit.rs +++ /dev/null @@ -1,290 +0,0 @@ -use halo2_proofs::arithmetic::Field; -use std::hash::Hash; - -pub fn hadamard_product + Hash>(vec1: &Vec, vec2: &Vec) -> Vec { - assert_eq!(vec1.len(), vec2.len()); - vec1.iter() - .zip(vec2.iter()) - .map(|(&v1, &v2)| v1 * v2) - .collect() -} - -pub fn vec_add + Hash>(vec: &Vec>) -> Vec { - assert!(vec.len() > 1); - vec.iter().fold(vec![F::ZERO; vec[0].len()], |acc, vec| { - acc.iter().zip(vec.iter()).map(|(&a, &v)| a + v).collect() - }) -} - -// input vector z = [1, x, w] -pub struct Z> { - n: usize, - l: usize, - values: Vec, -} - -impl + Hash> Z { - pub fn new(n: usize, l: usize) -> Self { - assert!(n > l); - Self { - n, - l, - values: Vec::new(), - } - } - - pub fn write(&mut self, inputs: &Vec, witnesses: &Vec) { - assert_eq!(inputs.len(), self.l); - assert_eq!(witnesses.len(), self.n - self.l - 1); - - // todo: order - let mut witnesses = witnesses.clone(); - let mut inputs = inputs.clone(); - let mut values = vec![F::ONE]; - values.append(&mut inputs); - values.append(&mut witnesses); - self.values = values; - } -} - -pub struct Matrix { - n: usize, - m: usize, - values: Vec>, -} - -impl + Hash> Matrix { - pub fn new(n: usize, m: usize) -> Self { - Self { - n, - m, - values: Vec::new(), - } - } - - pub fn write(&mut self, values: &[(usize, usize, F)]) { - let mut matrix: Vec> = (0..self.m) - .map(|_| (0..self.n).map(|_| F::ZERO).collect()) - .collect(); - for &(row, col, value) in values.iter() { - assert!(row < self.m); - assert!(col < self.n); - matrix[row][col] = value; - } - self.values = matrix.clone(); - } - - pub fn get(&self, row: usize, col: usize) -> F { - assert!(row < self.m); - assert!(col < self.n); - self.values[row][col] - } - - pub fn size(&self) -> (usize, usize) { - (self.m, self.n) - } -} - -impl + Hash> Matrix { - pub fn matrix_vector_product(&self, z: &Vec) -> Vec { - (0..self.values.len()) - .map(|i| self.hadamard_product_and_sum(i, z)) - .collect() - } - - fn hadamard_product_and_sum(&self, index: usize, z: &Vec) -> F { - assert!(index < self.values.len()); - assert_eq!(self.values[index].len(), z.len()); - hadamard_product(&self.values[index], z).iter().sum() - } -} - -pub struct CCSCircuit> { - n: usize, - m: usize, - nn: usize, - l: usize, - t: usize, - q: usize, - d: usize, - matrices: Vec>, - selectors: Vec>, - constants: Vec, -} - -impl + Hash> CCSCircuit { - pub fn new(n: usize, m: usize, t: usize, q: usize, l: usize) -> Self { - assert!(n > l); - - let matrices = (0..t).map(|_| Matrix::new(n, m)).collect(); - - let selectors = (0..q).map(|_| Vec::new()).collect(); - - let constants = (0..q).map(|_| F::ZERO).collect(); - - let nn = 0; - - Self { - n, - m, - l, - t, - q, - d: 0, - nn, - matrices, - selectors, - constants, - } - } - - pub fn read_nn(&self) -> usize { - self.nn - } - - pub fn public_num(&self) -> usize { - self.l - } - - pub fn witness_num(&self) -> usize { - self.n - self.l - 1 - } - - pub fn write( - &mut self, - matrices: &Vec>, - selectors: &Vec>, - constants: &Vec, - ) { - let mut degree = 0; - let mut nn = 0; - - assert_eq!(constants.len(), self.q); - self.constants = constants.clone(); - - assert_eq!(selectors.len(), self.q); - for selector in selectors.iter() { - for &s in selector { - assert!(s < self.t) - } - degree = degree.max(selector.len()) - } - self.selectors = selectors.clone(); - - assert_eq!(matrices.len(), self.t); - - self.matrices = matrices - .iter() - .map(|cells| { - for &cell in cells.iter() { - assert!(cell.0 < self.m); - assert!(cell.1 < self.n); - if cell.2 != F::ZERO { - nn += 1; - } - } - - let mut matrix = Matrix::new(self.n, self.m); - matrix.write(cells); - matrix - }) - .collect(); - - self.d = degree; - self.nn = nn; - } - - pub fn is_satisfied(&self, z: &Z) -> bool { - assert_eq!(self.selectors.len(), self.q); - assert_eq!(self.constants.len(), self.q); - - let prod_vec: Vec> = self - .constants - .iter() - .zip(self.selectors.iter()) - .map(|(&c, selector)| { - let hadamard_prod_vec: Vec> = selector - .iter() - .map(|&s| { - assert!(s < self.matrices.len()); - self.matrices[s].matrix_vector_product(&z.values) - }) - .collect(); - hadamard_prod_vec - .iter() - .fold(vec![c; self.m], |acc, vec| hadamard_product(&acc, vec)) - }) - .collect(); - let sum_vec = vec_add(&prod_vec); - - assert_eq!(sum_vec.len(), self.m); - - for &sum in sum_vec.iter() { - if sum != F::ZERO { - return false; - } - } - true - } -} - -#[cfg(test)] -mod tests { - use std::vec; - - use super::*; - use halo2_proofs::halo2curves::bn256::Fr; - - #[test] - fn test_ccs() { - let n = 7; - let m = 4; - let t = 8; - let q = 5; - let l = 3; - - let mut ccs_circuit: CCSCircuit = CCSCircuit::new(n, m, t, q, l); - let m0 = vec![ - (0, 1, Fr::ONE), - (1, 2, Fr::ONE), - (2, 3, Fr::ONE), - (3, 6, Fr::ONE), - ]; - let m1 = vec![ - (0, 1, Fr::ONE), - (1, 2, Fr::ONE), - (2, 4, Fr::ONE), - (3, 6, Fr::ONE), - ]; - let m2 = vec![ - (0, 1, Fr::ONE), - (1, 2, Fr::ONE), - (2, 5, Fr::ONE), - (3, 6, Fr::ONE), - ]; - let m3 = vec![(0, 0, Fr::ONE), (1, 0, Fr::ONE)]; - let m4 = vec![(2, 0, Fr::from(2))]; - let m5 = vec![(2, 0, Fr::from(2))]; - let m6 = vec![ - (0, 0, Fr::ONE.neg()), - (1, 0, Fr::ONE.neg()), - (2, 0, Fr::ONE.neg()), - ]; - let m7 = vec![(0, 0, Fr::ZERO)]; - let matrices = vec![m0, m1, m2, m3, m4, m5, m6, m7]; - - let selectors = vec![vec![3, 0, 1], vec![4, 0], vec![5, 1], vec![6, 2], vec![7]]; - let constants: Vec = vec![Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE]; - ccs_circuit.write(&matrices, &selectors, &constants); - - let mut z = Z::new(n, l); - z.write( - &vec![Fr::ZERO, Fr::ONE, Fr::from(2)], - &vec![Fr::from(3), Fr::from(10), Fr::from(43)], - ); - - let result = ccs_circuit.is_satisfied(&z); - - println!("result = {}", result); - } -} diff --git a/src/ccs/compiler/cell_manager.rs b/src/ccs/compiler/cell_manager.rs new file mode 100644 index 00000000..8f557ed7 --- /dev/null +++ b/src/ccs/compiler/cell_manager.rs @@ -0,0 +1,170 @@ +use super::CompilationUnit; +use std::{collections::HashMap, fmt::Debug}; + +use crate::util::UUID; +pub trait CellManager: Clone { + fn place(&self, unit: &mut CompilationUnit); +} + +#[derive(Debug, Default, Clone)] +pub struct SingleRowCellManager {} + +impl CellManager for SingleRowCellManager { + fn place(&self, unit: &mut CompilationUnit) { + let mut placement = Placement { + forward: HashMap::new(), + shared: HashMap::new(), + fixed: HashMap::new(), + steps: HashMap::new(), + base_height: 0, + }; + + let mut forward_signals: u32 = 0; + for signal in unit.forward_signals.iter() { + placement.forward.insert( + signal.uuid(), + SignalPlacement::new(signal.uuid(), signal.annotation(), forward_signals), + ); + forward_signals += 1; + } + + let mut shared_signals: u32 = 0; + for signal in unit.shared_signals.iter() { + placement.shared.insert( + signal.uuid(), + SignalPlacement::new( + signal.uuid(), + signal.annotation(), + forward_signals + shared_signals, + ), + ); + shared_signals += 1; + } + + let mut fixed_signals: u32 = 0; + for signal in unit.fixed_signals.iter() { + placement.fixed.insert( + signal.uuid(), + SignalPlacement::new( + signal.uuid(), + signal.annotation(), + forward_signals + shared_signals + fixed_signals, + ), + ); + fixed_signals += 1; + } + + placement.base_height = forward_signals + shared_signals + fixed_signals; + + for step in unit.step_types.values() { + let mut internal_signals: u32 = 0; + let mut step_placement = StepPlacement::new(0, HashMap::new()); + for signal in step.signals.iter() { + step_placement.signals.insert( + signal.uuid(), + SignalPlacement::new(signal.uuid(), signal.annotation(), internal_signals), + ); + internal_signals += 1; + } + step_placement.height = internal_signals; + placement.steps.insert(step.uuid(), step_placement); + } + unit.placement = placement; + } +} + +#[derive(Debug, Clone)] +pub struct SignalPlacement { + id: UUID, + annotation: String, + offset: u32, +} + +impl SignalPlacement { + pub fn new(id: UUID, annotation: String, offset: u32) -> Self { + Self { + id, + annotation, + offset, + } + } + + pub fn new_with_id(id: UUID, annotation: String) -> Self { + Self { + id, + annotation, + offset: 0, + } + } + + pub fn uuid(&self) -> UUID { + self.id + } + + pub fn annotation(&self) -> String { + self.annotation.to_string() + } + + pub fn offset(&self) -> u32 { + self.offset + } +} + +#[derive(Debug, Clone)] +pub struct StepPlacement { + height: u32, + signals: HashMap, +} + +impl StepPlacement { + pub fn new(height: u32, signals: HashMap) -> Self { + Self { height, signals } + } + + pub fn height(&self) -> u32 { + self.height + } + + pub fn signal(&self, uuid: UUID) -> SignalPlacement { + self.signals.get(&uuid).unwrap().clone() + } + + pub fn signals(&self) -> HashMap { + self.signals.clone() + } +} + +#[derive(Debug, Clone, Default)] +pub struct Placement { + pub forward: HashMap, + pub shared: HashMap, + pub fixed: HashMap, + pub steps: HashMap, + pub base_height: u32, +} + +impl Placement { + pub fn step_height(&self, step_uuid: UUID) -> u32 { + self.steps.get(&step_uuid).expect("step not found").height() + } + + pub fn step(&self, step_uuid: UUID) -> StepPlacement { + self.steps.get(&step_uuid).unwrap().clone() + } + + pub fn forward(&self, forward_uuid: UUID) -> SignalPlacement { + self.forward.get(&forward_uuid).unwrap().clone() + } + + pub fn shared(&self, shared_uuid: UUID) -> SignalPlacement { + self.shared.get(&shared_uuid).unwrap().clone() + } + + pub fn fixed(&self, fixed_uuid: UUID) -> SignalPlacement { + self.fixed.get(&fixed_uuid).unwrap().clone() + } + + pub fn internal(&self, step_uuid: UUID, internal_uuid: UUID) -> SignalPlacement { + self.steps.get(&step_uuid).unwrap().signal(internal_uuid) + } +} diff --git a/src/ccs/compiler/mod.rs b/src/ccs/compiler/mod.rs new file mode 100644 index 00000000..115e87f6 --- /dev/null +++ b/src/ccs/compiler/mod.rs @@ -0,0 +1,232 @@ +pub mod cell_manager; +pub mod step_selector; +pub(crate) mod unit; + +use std::{hash::Hash, rc::Rc}; +use unit::CompilationUnit; + +use crate::{ + ccs::ir::{assignments::AssignmentGenerator, circuit::Circuit, Poly, PolyExpr}, + field::Field, + poly::Expr, + sbpir::{query::Queriable, ExposeOffset, StepType, PIR, SBPIR as astCircuit}, + wit_gen::{AutoTraceGenerator, TraceGenerator}, +}; + +use cell_manager::CellManager; +use step_selector::StepSelectorBuilder; + +use self::cell_manager::SignalPlacement; + +#[derive(Clone)] +pub struct CompilerConfig { + cell_manager: CM, + step_selector_builder: SSB, +} + +pub fn config( + cell_manager: CM, + step_selector_builder: SSB, +) -> CompilerConfig { + CompilerConfig { + cell_manager, + step_selector_builder, + } +} + +pub fn compile( + config: CompilerConfig, + ast: &astCircuit, +) -> (Circuit, Option>) { + let mut unit = CompilationUnit::from(ast); + + config.cell_manager.place(&mut unit); + + compile_exposed(ast, &mut unit); + + compile_constraints(ast, &mut unit); + + config.step_selector_builder.build::(&mut unit); + + let assignment = ast.trace.as_ref().map(|v| { + AssignmentGenerator::new( + unit.uuid, + unit.placement.clone(), + unit.selector.clone(), + unit.matrix_values.clone(), + TraceGenerator::new(Rc::clone(v), ast.num_steps), + AutoTraceGenerator::from(ast), + ) + }); + + (unit.into(), assignment) +} + +fn compile_exposed(ast: &astCircuit, unit: &mut CompilationUnit) { + for (queriable, offset) in &ast.exposed { + let exposed = match queriable { + Queriable::Forward(forward_signal, _) => { + let step = match offset { + ExposeOffset::First => 0, + ExposeOffset::Last => unit.num_steps - 1, + ExposeOffset::Step(step) => *step, + }; + ( + step, + SignalPlacement::new( + forward_signal.uuid(), + forward_signal.annotation(), + unit.placement.forward(forward_signal.uuid()).offset(), + ), + ) + } + Queriable::Shared(shared_signal, _) => { + let step = match offset { + ExposeOffset::First => 0, + ExposeOffset::Last => unit.num_steps - 1, + ExposeOffset::Step(step) => *step, + }; + ( + step, + SignalPlacement::new( + shared_signal.uuid(), + shared_signal.annotation(), + unit.placement.forward.len() as u32 + + unit.placement.forward(shared_signal.uuid()).offset(), + ), + ) + } + _ => panic!("Queriable was not Forward or Shared"), + }; + unit.exposed.push(exposed); + } +} + +fn compile_constraints( + _: &astCircuit, + unit: &mut CompilationUnit, +) { + for step in unit.step_types.clone().values() { + let step_annotation = unit + .annotations + .get(&step.uuid()) + .unwrap_or(&"??".to_string()) + .to_owned(); + + let mut polys = Vec::new(); + for constr in step.constraints.iter() { + let poly = transform_expr(unit, step, &constr.expr.clone()); + polys.push(Poly { + expr: poly, + step_uuid: step.uuid(), + annotation: format!( + "{}::{} => {:?}", + step_annotation.clone(), + constr.annotation.clone(), + constr.expr + ), + }) + } + + for constr in step.transition_constraints.iter() { + let poly = transform_expr(unit, step, &constr.expr.clone()); + polys.push(Poly { + expr: poly, + step_uuid: step.uuid(), + annotation: format!( + "{}::{} => {:?}", + step_annotation.clone(), + constr.annotation.clone(), + constr.expr + ), + }) + } + + unit.polys.insert(step.uuid(), polys); + } +} + +fn transform_expr( + unit: &CompilationUnit, + step: &StepType, + source: &PIR, +) -> PolyExpr { + match source.clone() { + Expr::Const(c) => PolyExpr::Const(c), + Expr::Query(q) => place_queriable(unit, step, q), + Expr::Sum(v) => PolyExpr::Sum( + v.into_iter() + .map(|e| transform_expr(unit, step, &e)) + .collect(), + ), + Expr::Mul(v) => PolyExpr::Mul( + v.into_iter() + .map(|e| transform_expr(unit, step, &e)) + .collect(), + ), + Expr::Neg(v) => PolyExpr::Neg(Box::new(transform_expr(unit, step, &v))), + Expr::Pow(v, exp) => PolyExpr::Pow(Box::new(transform_expr(unit, step, &v)), exp), + Expr::Halo2Expr(_) => panic!("halo2 expr not done"), + Expr::MI(_) => panic!("mi elimination not done"), + } +} + +fn place_queriable( + unit: &CompilationUnit, + step: &StepType, + q: Queriable, +) -> PolyExpr { + match q { + // (UUID, UUID, String, u32) + Queriable::Forward(forward, next) => { + let annotation = if let Some(annotation) = unit.annotations.get(&forward.uuid()) { + if next { + format!("forward next({})", annotation) + } else { + format!("forward {}", annotation) + } + } else { + "forward".to_string() + }; + PolyExpr::Query((forward.uuid(), step.uuid(), annotation, next)) + } + Queriable::Shared(shared, rot) => { + let annotation = if let Some(annotation) = unit.annotations.get(&shared.uuid()) { + if rot == 0 { + format!("shared({})", annotation) + } else { + format!("shared_rot_{}({})", rot, annotation) + } + } else { + "forward".to_string() + }; + PolyExpr::Query((shared.uuid(), step.uuid(), annotation, false)) + } + Queriable::Fixed(fixed, rot) => { + let annotation = if let Some(annotation) = unit.annotations.get(&fixed.uuid()) { + if rot == 0 { + format!("fixed({})", annotation) + } else { + format!("fixed_rot_{}({})", rot, annotation) + } + } else { + "fixed".to_string() + }; + PolyExpr::Query((fixed.uuid(), step.uuid(), annotation, false)) + } + Queriable::Internal(signal) => { + let annotation = if let Some(annotation) = unit.annotations.get(&signal.uuid()) { + format!("internal {}", annotation) + } else { + "internal".to_string() + }; + + PolyExpr::Query((signal.uuid(), step.uuid(), annotation, false)) + } + + Queriable::StepTypeNext(_) => panic!("jarrl"), + Queriable::Halo2AdviceQuery(..) => panic!("jarrl"), + Queriable::Halo2FixedQuery(..) => panic!("jarrl"), + Queriable::_unaccessible(_) => panic!("jarrl"), + } +} diff --git a/src/ccs/compiler/step_selector.rs b/src/ccs/compiler/step_selector.rs new file mode 100644 index 00000000..418a8aa8 --- /dev/null +++ b/src/ccs/compiler/step_selector.rs @@ -0,0 +1,91 @@ +use crate::{ccs::ir::assignments::Coeffs, field::Field, util::UUID}; + +use super::{CompilationUnit, PolyExpr}; +use std::collections::HashMap; + +pub type SelectorAssignment = (PolyExpr, F); + +#[derive(Debug, Clone)] +pub struct StepSelector { + pub matrix_selectors: Vec>, + pub step_selectors: HashMap>>, +} + +impl Default for StepSelector { + fn default() -> Self { + Self { + step_selectors: Default::default(), + matrix_selectors: Default::default(), + } + } +} + +pub trait StepSelectorBuilder: Clone { + fn build(&self, unit: &mut CompilationUnit); +} + +#[derive(Debug, Default, Clone)] +pub struct SimpleStepSelectorBuilder {} + +impl StepSelectorBuilder for SimpleStepSelectorBuilder { + fn build(&self, unit: &mut CompilationUnit) { + let mut matrix_values = HashMap::new(); + for (step_id, polys) in unit.polys.iter() { + // one step + let values = polys + .iter() + .map(|poly| { + // one poly + poly.expr.poly_to_matrix(true) + }) + .collect(); + matrix_values.insert(*step_id, values); + } + let mut matrix_selectors: Vec> = Vec::new(); + let mut step_selectors: HashMap>> = HashMap::new(); + construct_selector(&matrix_values, &mut matrix_selectors, &mut step_selectors); + + unit.selector = StepSelector { + step_selectors, + matrix_selectors, + }; + unit.matrix_values = matrix_values; + } +} + +fn construct_selector( + values: &HashMap>, + matrix_selectors: &mut Vec>, + step_selectors: &mut HashMap>>, +) { + let mut total = matrix_selectors.iter().map(|v| v.len()).sum(); + + for (idx, polys_values) in values.iter() { + let mut step_selector = Vec::new(); // each selector has one + for poly_values in polys_values.iter() { + let mut constr_selector = Vec::new(); + let mut used = vec![false; matrix_selectors.len()]; + for chunk_values in poly_values.iter() { + let size = chunk_values.len(); + let mut pos = matrix_selectors.len(); + for (i, matrix_selector) in matrix_selectors.iter().enumerate() { + if matrix_selector.len() == size && !used[i] { + pos = i; + used[pos] = true; + constr_selector.push(pos); + break; + } + } + if pos == matrix_selectors.len() { + matrix_selectors.push((total..size + total).map(|i| (i, F::ONE)).collect()); + total += size; + used.push(true); + constr_selector.push(pos); + } + } + step_selector.push(constr_selector); + } + + step_selectors.insert(*idx, step_selector); + } +} diff --git a/src/ccs/compiler/unit.rs b/src/ccs/compiler/unit.rs new file mode 100644 index 00000000..c5baab6a --- /dev/null +++ b/src/ccs/compiler/unit.rs @@ -0,0 +1,116 @@ +use crate::{ + ccs::{ + compiler::SignalPlacement, + ir::{assignments::Coeffs, circuit::Circuit, Poly}, + }, + field::Field, + sbpir::{FixedSignal, ForwardSignal, SharedSignal, StepType, SBPIR as astCircuit}, + util::{uuid, UUID}, +}; + +use std::{collections::HashMap, hash::Hash, rc::Rc}; + +use super::{cell_manager::Placement, step_selector::StepSelector}; + +#[derive(Debug, Clone)] +pub struct CompilationUnit { + pub ast_id: UUID, + pub uuid: UUID, + pub annotations: HashMap, + + pub forward_signals: Vec, + pub shared_signals: Vec, + pub fixed_signals: Vec, + + pub num_steps: usize, + pub step_types: HashMap>>, + + pub placement: Placement, + + pub exposed: Vec<(usize, SignalPlacement)>, + + pub polys: HashMap>>, + + pub selector: StepSelector, + pub matrix_values: HashMap>, +} + +impl Default for CompilationUnit { + fn default() -> Self { + Self { + ast_id: Default::default(), + uuid: uuid(), + step_types: Default::default(), + + forward_signals: Default::default(), + shared_signals: Default::default(), + fixed_signals: Default::default(), + + annotations: Default::default(), + exposed: Default::default(), + num_steps: Default::default(), + selector: Default::default(), + polys: Default::default(), + + placement: Default::default(), + matrix_values: Default::default(), + } + } +} + +impl From<&astCircuit> for CompilationUnit { + fn from(ast: &astCircuit) -> Self { + Self { + ast_id: ast.id, + uuid: uuid(), + annotations: { + let mut acc = ast.annotations.clone(); + for step in ast.step_types.values() { + acc.extend(step.annotations.clone()); + } + + acc + }, + step_types: ast.step_types.clone(), + forward_signals: ast.forward_signals.clone(), + shared_signals: ast.shared_signals.clone(), + fixed_signals: ast.fixed_signals.clone(), + num_steps: ast.num_steps, + + ..Default::default() + } + } +} + +impl From> for Circuit { + fn from(unit: CompilationUnit) -> Self { + let mut circuit = Circuit::new(unit.ast_id); + + let exposed: Vec<(usize, UUID)> = unit + .exposed + .iter() + .map(|(step, exposed)| (*step, exposed.uuid())) + .collect(); + let mut witnesses = HashMap::new(); + + for (step_uuid, _) in unit.matrix_values.iter() { + let mut values: Vec = unit.placement.forward.keys().copied().collect(); + values.append(&mut unit.placement.shared.keys().copied().collect()); + values.append(&mut unit.placement.fixed.keys().copied().collect()); + values.append( + &mut unit + .placement + .step(*step_uuid) + .signals() + .keys() + .copied() + .collect(), + ); + witnesses.insert(*step_uuid, values); + } + + circuit.write(&unit.matrix_values, &unit.selector, &exposed, &witnesses); + + circuit + } +} diff --git a/src/ccs/ir/assignments.rs b/src/ccs/ir/assignments.rs new file mode 100644 index 00000000..4e9761ca --- /dev/null +++ b/src/ccs/ir/assignments.rs @@ -0,0 +1,261 @@ +use std::{ + collections::HashMap, + hash::Hash, + ops::{Deref, DerefMut}, +}; + +use crate::{ + ccs::compiler::{cell_manager::Placement, step_selector::StepSelector}, + field::Field, + sbpir::query::Queriable, + util::UUID, + wit_gen::{AutoTraceGenerator, StepInstance, TraceGenerator, TraceWitness}, +}; + +pub type Coeffs = Vec>>>; + +pub type MatrixsCoeffs = Vec>, usize)>>; + +#[derive(Debug, Clone)] +pub struct StepsID(pub Vec); + +impl Default for StepsID { + fn default() -> Self { + Self::new() + } +} + +impl StepsID { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn read(&self, index: usize) -> UUID { + assert!(index < self.len()); + self.0[index] + } +} + +#[derive(Debug, Clone)] +pub struct Assignments(pub Vec>); + +impl Default for Assignments { + fn default() -> Self { + Self(Vec::new()) + } +} + +impl Deref for Assignments { + type Target = Vec>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Assignments { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Assignments { + pub fn new(values: Vec>) -> Self { + Self(values) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn append(&mut self, ass: &mut Vec>) { + self.0.append(ass) + } + + pub fn read(&self) -> Vec> { + self.0.clone() + } + + pub fn get(&self, step_idx: usize, signal_id: UUID) -> F { + *self.0[step_idx].get(&signal_id).unwrap() + } + + pub fn write(&mut self, step_idx: usize, signal_id: UUID, value: F) { + self.0[step_idx].insert(signal_id, value); + } +} + +pub struct AssignmentGenerator { + placement: Placement, + selector: StepSelector, + matrix_values: HashMap>, + trace_gen: TraceGenerator, + auto_trace_gen: AutoTraceGenerator, + + ir_id: UUID, +} + +impl Clone for AssignmentGenerator { + fn clone(&self) -> Self { + Self { + ir_id: self.ir_id, + placement: self.placement.clone(), + selector: self.selector.clone(), + matrix_values: self.matrix_values.clone(), + trace_gen: self.trace_gen.clone(), + auto_trace_gen: self.auto_trace_gen.clone(), + } + } +} + +impl Default for AssignmentGenerator { + fn default() -> Self { + Self { + ir_id: Default::default(), + placement: Default::default(), + selector: Default::default(), + matrix_values: Default::default(), + trace_gen: Default::default(), + auto_trace_gen: Default::default(), + } + } +} + +impl AssignmentGenerator { + pub fn new( + ir_id: UUID, + placement: Placement, + selector: StepSelector, + matrix_values: HashMap>, + trace_gen: TraceGenerator, + auto_trace_gen: AutoTraceGenerator, + ) -> Self { + Self { + ir_id, + placement, + selector, + matrix_values, + trace_gen, + auto_trace_gen, + } + } + + pub fn trace_witness(&self, args: TraceArgs) -> TraceWitness { + self.trace_gen.generate(args) + } + + pub fn generate(&self, args: TraceArgs) -> (Assignments, StepsID) { + let witness = self.trace_gen.generate(args); + + self.generate_with_witness(witness) + } + + pub fn generate_with_witness(&self, witness: TraceWitness) -> (Assignments, StepsID) { + let witness = self.auto_trace_gen.generate(witness); + + let values: Vec> = witness + .step_instances + .iter() + .map(|step_instance| { + let mut values = HashMap::new(); + for (q, v) in step_instance.assignments.iter() { + values.insert(q.uuid(), *v); + } + values + }) + .collect(); + let mut assignments: Assignments = Assignments::new(values); + + let mut steps_id: StepsID = StepsID::new(); + for (idx, step_instance) in witness.step_instances.iter().enumerate() { + self.assign_step( + idx, + step_instance, + &mut assignments, + witness.step_instances.len(), + ); + steps_id.0.push(step_instance.step_type_uuid); + } + + (assignments, steps_id) + } + + pub fn assign_step( + &self, + idx: usize, + step_instance: &StepInstance, + assignments: &mut Assignments, + step_num: usize, + ) { + let step_uuid = step_instance.step_type_uuid; + let height = self.placement.base_height + self.placement.step_height(step_uuid); + + for (lhs, &rhs) in step_instance.assignments.iter() { + let offset = self.find_placement(step_uuid, lhs, height); + if offset < height { + assignments.write(idx, lhs.uuid(), rhs); + } else if idx < step_num - 1 { + assignments.write(idx + 1, lhs.uuid(), rhs); + } + } + } + + fn find_placement(&self, step_uuid: UUID, query: &Queriable, height: u32) -> u32 { + match query { + Queriable::Internal(signal) => { + self.placement.internal(step_uuid, signal.uuid()).offset() + } + + Queriable::Forward(forward, next) => { + if *next { + self.placement.forward(forward.uuid()).offset() + height + } else { + self.placement.forward(forward.uuid()).offset() + } + } + Queriable::Shared(shared, _) => self.placement.shared(shared.uuid()).offset(), + + Queriable::Fixed(fixed, _) => self.placement.fixed(fixed.uuid()).offset(), + + _ => panic!("invalid advice assignment on queriable {:?}", query), + } + } + + pub fn uuid(&self) -> UUID { + self.ir_id + } +} + +pub struct PublicInputs(pub Vec); + +impl Default for PublicInputs { + fn default() -> Self { + Self(Vec::new()) + } +} + +impl PublicInputs { + pub fn new(values: Vec) -> Self { + Self(values) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} diff --git a/src/ccs/ir/circuit.rs b/src/ccs/ir/circuit.rs new file mode 100644 index 00000000..21dba737 --- /dev/null +++ b/src/ccs/ir/circuit.rs @@ -0,0 +1,93 @@ +use std::{collections::HashMap, hash::Hash}; + +use super::assignments::*; +use crate::{ccs::compiler::step_selector::StepSelector, field::Field, util::UUID}; + +#[derive(Debug)] +pub struct Circuit { + pub ast_id: UUID, + + pub matrics: HashMap>, + pub selectors: Vec>, + pub constants: Vec, + pub exposed: Vec<(usize, UUID)>, + pub witness: HashMap>, + + pub d: usize, + pub q: usize, + pub t: usize, +} + +impl + Hash> Circuit { + pub fn new(ast_id: UUID) -> Self { + let matrics = HashMap::new(); + let selectors = Vec::new(); + let constants = Vec::new(); + let exposed = vec![]; + let witness = HashMap::new(); + + Self { + t: 0, + q: 0, + d: 0, + matrics, + selectors, + constants, + exposed, + witness, + ast_id, + } + } + + pub fn write( + &mut self, + matrix_values: &HashMap>, + selectors: &StepSelector, + exposed: &[(usize, UUID)], + witness: &HashMap>, + ) { + let mut t = 0; + let mut d = 0; + + self.q = selectors.matrix_selectors.len(); + self.constants = vec![F::ONE; self.q]; + + for selectors in selectors.matrix_selectors.iter() { + d = d.max(selectors.len()); + for (selector, _) in selectors.iter() { + t = t.max(*selector); + } + } + self.selectors = selectors.matrix_selectors.clone(); + + let mut matrics = HashMap::new(); + for (uuid, values) in matrix_values.iter() { + let selectors = selectors.step_selectors.get(uuid).unwrap(); + let v = values + .iter() + .zip(selectors.iter()) + .map(|(values, selectors)| { + values + .iter() + .zip(selectors.iter()) + .map(|(values, selectors)| (values.clone(), *selectors)) + .collect() + }) + .collect(); + + matrics.insert(*uuid, v); + } + self.matrics = matrics; + self.exposed = exposed.to_owned(); + self.witness = witness.clone(); + self.selectors = selectors.matrix_selectors.clone(); + } + + pub fn instance(&self, assignments: &Assignments) -> HashMap<(usize, UUID), F> { + let mut exposes = HashMap::new(); + for (step_idx, id) in self.exposed.iter() { + exposes.insert((*step_idx, *id), assignments.get(*step_idx, *id)); + } + exposes + } +} diff --git a/src/ccs/ir/mod.rs b/src/ccs/ir/mod.rs new file mode 100644 index 00000000..5a7fc561 --- /dev/null +++ b/src/ccs/ir/mod.rs @@ -0,0 +1,83 @@ +use std::fmt::Debug; + +pub mod assignments; +pub mod circuit; + +use crate::{field::Field, poly::Expr, util::UUID}; + +#[derive(Clone)] +pub struct Poly { + pub step_uuid: UUID, + pub annotation: String, + pub expr: PolyExpr, +} + +impl Debug for Poly { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} => {:?}", self.annotation, self.expr) + } +} + +// (signal_uuid, step_uuid, annotation, pos) +pub type PolyExpr = Expr; + +impl PolyExpr { + pub fn poly_to_matrix(&self, flag: bool) -> Vec>> { + let matrics = match self { + PolyExpr::Const(v) => vec![vec![vec![(*v, 0, false)]]], + PolyExpr::Query((id, _, _, q)) => vec![vec![vec![(F::ONE, *id, *q)]]], + PolyExpr::Neg(v) => { + let mut matrics = v.poly_to_matrix(flag); + for matrixs in matrics.iter_mut() { + matrixs.push(vec![(F::ONE.neg(), 0, false)]); + } + matrics + } + PolyExpr::Sum(v) => { + if flag { + let mut matrics = Vec::new(); + for e in v.iter() { + matrics.append(&mut e.poly_to_matrix(false)); + } + matrics + } else { + let values = v + .iter() + .map(|e| match *e { + PolyExpr::Const(v) => (v, 0, false), + PolyExpr::Query((id, _, _, q)) => (F::ONE, id, q), + PolyExpr::Neg(_) => (F::ONE.neg(), 0, false), + _ => panic!("invalid poly expr"), + }) + .collect(); + vec![vec![values]] + } + } + PolyExpr::Mul(v) => { + let mut matrics = Vec::new(); + for e in v.iter() { + let matrix = e.poly_to_matrix(false); + if matrix.len() != 1 { + panic!("invalid poly expr"); + } + for m in matrix[0].iter() { + matrics.push(m.clone()); + } + } + vec![matrics] + } + PolyExpr::Pow(v, exp) => { + let matrics = v.poly_to_matrix(flag); + if matrics.len() != 1 { + panic!("invalid poly expr"); + } + (0..*exp) + .map(|_| matrics.clone()) + .collect::>() + .concat() + } + _ => panic!("invalid poly expr"), + }; + matrics + } +} diff --git a/src/ccs/mod.rs b/src/ccs/mod.rs index db89e612..aab5b126 100644 --- a/src/ccs/mod.rs +++ b/src/ccs/mod.rs @@ -1 +1,3 @@ -pub mod circuit; +pub mod backend; +pub mod compiler; +pub mod ir; diff --git a/src/plonkish/backend/ccs.rs b/src/plonkish/backend/ccs.rs deleted file mode 100644 index 9af2881a..00000000 --- a/src/plonkish/backend/ccs.rs +++ /dev/null @@ -1,56 +0,0 @@ - - -use std::hash::Hash; - -use halo2_proofs::{arithmetic::Field, plonk::Assignment}; - -use crate::{ - plonkish::ir::{Circuit, assignments::Assignments}, - util::UUID, - ccs::circuit::CCSCircuit, -}; - - -pub struct ChiquitoCCS> { - pub debug: bool, - circuit: Circuit, - ir_id: UUID, - // ccs: -} - -impl + Hash> ChiquitoCCS { - pub fn new(circuit: Circuit) -> ChiquitoCCS { - let ir_id = circuit.ast_id; - Self { - debug: true, - circuit, - ir_id, - } - } - - pub fn configure(&mut self) { - - } -} - - -pub struct ChiquitoCCSCircuit> { - compiled: ChiquitoCCS, - witness: Option>, -} - -impl + Hash> ChiquitoCCSCircuit { - pub fn new(compiled: ChiquitoCCS, witness: Option>) -> Self { - Self { compiled, witness } - } - - pub fn instance(&self) { - - } -} - - -#[allow(non_snake_case)] -pub fn chiquito2CCS + Hash>(circuit: Circuit) -> ChiquitoCCS { - ChiquitoCCS::new(circuit) -} \ No newline at end of file diff --git a/src/plonkish/backend/mod.rs b/src/plonkish/backend/mod.rs index 5c01f61d..8d078b29 100644 --- a/src/plonkish/backend/mod.rs +++ b/src/plonkish/backend/mod.rs @@ -1,4 +1,3 @@ pub mod halo2; pub mod hyperplonk; pub mod plaf; -pub mod ccs; \ No newline at end of file From c01d0e13b8299a2bac4d059c4c20d5953e8020bb Mon Sep 17 00:00:00 2001 From: 10to4 Date: Mon, 17 Jun 2024 22:38:47 +0800 Subject: [PATCH 05/24] ccs: tidy up --- src/ccs/backend/ccs.rs | 223 ++++++++++++++++-------------- src/ccs/compiler/step_selector.rs | 2 +- src/ccs/compiler/unit.rs | 6 - src/ccs/ir/assignments.rs | 1 + 4 files changed, 123 insertions(+), 109 deletions(-) diff --git a/src/ccs/backend/ccs.rs b/src/ccs/backend/ccs.rs index 41f25509..5d6d4def 100644 --- a/src/ccs/backend/ccs.rs +++ b/src/ccs/backend/ccs.rs @@ -60,6 +60,114 @@ impl + Hash> ChiquitoCCSCircuit { } HashMap::new() } + + fn export_matrix_vec( + &self, + n: usize, + num: usize, + witness_pos: &PositionMap, + ) -> (Vec>, usize) { + let mut m = self.num_poly(); + let mut matrix_vec = vec![Matrix::new(n, m); num]; + let mut row = 0; + if let Some(steps_id) = self.steps_id.as_ref() { + for (idx, step_id) in steps_id.0.iter().enumerate() { + // step + let matrics = self.compiled.circuit.matrics.get(step_id).unwrap(); + + for matrics in matrics.iter() { + // poly + if idx == steps_id.0.len() - 1 { + let mut skip = false; + for (matrics, _) in matrics.iter() { + if skip { + break; + } + for (_, _, next) in matrics.concat().iter() { + if *next { + skip = true; + break; + } + } + } + if skip { + m -= 1; + continue; + } + } + + for (matrix_chunks, index) in matrics.iter() { + // chunk + assert!(*index <= self.compiled.circuit.selectors.len()); + let selectors = self.compiled.circuit.selectors[*index].clone(); + assert_eq!(matrix_chunks.len(), selectors.len()); + + for (items, (selector, _)) in matrix_chunks.iter().zip(selectors.iter()) { + // one row in on matrix + let mut values: Vec<(usize, usize, F)> = Vec::new(); + for (v, id, next) in items.iter() { + let idx = if *next { idx + 1 } else { idx }; + let col = if *id == 0 { + witness_pos.get(&(0, 0)) + } else { + witness_pos.get(&(idx, *id)) + }; + match col { + None => continue, + Some(col) => values.push((row, *col, *v)), + } + } + matrix_vec[*selector].write(&values); + } + } + row += 1; + } + } + }; + + for matrix in matrix_vec.iter_mut() { + if row < matrix.m { + matrix.remove_rows(row); + } + } + (matrix_vec, m) + } + + fn num_poly(&self) -> usize { + self.steps_id + .as_ref() + .map(|steps_idx| { + steps_idx + .0 + .iter() + .map(|idx| self.compiled.circuit.matrics.get(idx).unwrap().len()) + .sum() + }) + .unwrap() + } + + fn coeffs_offsets(&self) -> (PositionMap, PositionMap) { + let mut witness_pos = HashMap::new(); + let mut offset = 0; + witness_pos.insert((0, 0), offset); + offset += 1; + if let Some(steps_id) = self.steps_id.as_ref() { + for (idx, step_idx) in steps_id.0.iter().enumerate() { + let witnesses = self.compiled.circuit.witness.get(step_idx).unwrap(); + for id in witnesses.iter() { + witness_pos.insert((idx, *id), offset); + offset += 1; + } + } + } + let mut public_pos = HashMap::new(); + for (idx, id) in self.compiled.circuit.exposed.iter() { + public_pos.insert((*idx, *id), offset); + offset += 1; + } + // todo: remove public values + (witness_pos, public_pos) + } } impl + Hash> ChiquitoCCSCircuit { @@ -95,9 +203,7 @@ impl + Hash> CCSCircuit { assert!(n > l); let matrics = Vec::new(); - let selectors = (0..q).map(|_| Vec::new()).collect(); - let constants = (0..q).map(|_| F::ZERO).collect(); Self { @@ -114,17 +220,6 @@ impl + Hash> CCSCircuit { } pub fn from_circuit(circuit: &ChiquitoCCSCircuit) -> (Self, PositionMap, PositionMap) { - let mut m: usize = circuit - .steps_id - .as_ref() - .map(|steps_idx| { - steps_idx - .0 - .iter() - .map(|idx| circuit.compiled.circuit.matrics.get(idx).unwrap().len()) - .sum() - }) - .unwrap(); let selectors = circuit.compiled.circuit.selectors.clone(); let constants = (0..circuit.compiled.circuit.q).map(|_| F::ONE).collect(); @@ -135,90 +230,10 @@ impl + Hash> CCSCircuit { } } - let mut witness_pos = HashMap::new(); - let mut offset = 0; - witness_pos.insert((0, 0), offset); // 1 - offset += 1; - if let Some(steps_id) = circuit.steps_id.as_ref() { - for (idx, step_idx) in steps_id.0.iter().enumerate() { - let witnesses = circuit.compiled.circuit.witness.get(step_idx).unwrap(); - for id in witnesses.iter() { - witness_pos.insert((idx, *id), offset); - offset += 1; - } - } - } - let mut public_pos = HashMap::new(); - for (idx, id) in circuit.compiled.circuit.exposed.iter() { - public_pos.insert((*idx, *id), offset); - offset += 1; - } - // todo: remove public values - + let (witness_pos, public_pos) = circuit.coeffs_offsets(); let n = witness_pos.len() + public_pos.len(); - let mut matrix_vec = vec![Matrix::new(n, m); matrix_num]; - - let mut row = 0; - if let Some(steps_id) = circuit.steps_id.as_ref() { - for (idx, step_id) in steps_id.0.iter().enumerate() { - // step - let matrics = circuit.compiled.circuit.matrics.get(step_id).unwrap(); - - for matrics in matrics.iter() { - // poly - if idx == steps_id.0.len() - 1 { - let mut skip = false; - for (matrics, _) in matrics.iter() { - if skip { - break; - } - for (_, _, next) in matrics.concat().iter() { - if *next { - skip = true; - break; - } - } - } - if skip { - m -= 1; - continue; - } - } - - for (matrix_chunks, index) in matrics.iter() { - // chunk - assert!(*index <= selectors.len()); - let selectors = selectors[*index].clone(); - assert_eq!(matrix_chunks.len(), selectors.len()); - - for (items, (selector, _)) in matrix_chunks.iter().zip(selectors.iter()) { - // one row in on matrix - let mut values: Vec<(usize, usize, F)> = Vec::new(); - for (v, id, next) in items.iter() { - let idx = if *next { idx + 1 } else { idx }; - let col = if *id == 0 { - witness_pos.get(&(0, 0)) - } else { - witness_pos.get(&(idx, *id)) - }; - match col { - None => continue, - Some(col) => values.push((row, *col, *v)), - } - } - matrix_vec[*selector].write(&values); - } - } - row += 1; - } - } - }; - for matrix in matrix_vec.iter_mut() { - if row < matrix.m { - matrix.remove_rows(row); - } - } + let (matrix_vec, m) = circuit.export_matrix_vec(n, matrix_num, &witness_pos); ( Self { @@ -251,12 +266,18 @@ impl + Hash> CCSCircuit { selectors: &[Vec<(usize, F)>], constants: &[F], ) { - let mut degree = 0; - let mut nn = 0; + self.write_constants(constants); + self.write_selectors_and_degree(selectors); + self.write_matrics(matrics); + } + pub fn write_constants(&mut self, constants: &[F]) { assert_eq!(constants.len(), self.q); self.constants = constants.to_owned().clone(); + } + pub fn write_selectors_and_degree(&mut self, selectors: &[Vec<(usize, F)>]) { + let mut degree = 0; assert_eq!(selectors.len(), self.q); for selector in selectors.iter() { for &(s, _) in selector { @@ -265,7 +286,10 @@ impl + Hash> CCSCircuit { degree = degree.max(selector.len()) } self.selectors = selectors.to_owned().clone(); + self.d = degree; + } + fn write_matrics(&mut self, matrics: &[Vec<(usize, usize, F)>]) { assert_eq!(matrics.len(), self.t); self.matrics = matrics @@ -274,9 +298,6 @@ impl + Hash> CCSCircuit { for &cell in cells.iter() { assert!(cell.0 < self.m); assert!(cell.1 < self.n); - if cell.2 != F::ZERO { - nn += 1; - } } let mut matrix = Matrix::new(self.n, self.m); @@ -284,8 +305,6 @@ impl + Hash> CCSCircuit { matrix }) .collect(); - - self.d = degree; } pub fn is_satisfied(&self, z: &Z) -> bool { diff --git a/src/ccs/compiler/step_selector.rs b/src/ccs/compiler/step_selector.rs index 418a8aa8..c285d263 100644 --- a/src/ccs/compiler/step_selector.rs +++ b/src/ccs/compiler/step_selector.rs @@ -61,7 +61,7 @@ fn construct_selector( let mut total = matrix_selectors.iter().map(|v| v.len()).sum(); for (idx, polys_values) in values.iter() { - let mut step_selector = Vec::new(); // each selector has one + let mut step_selector = Vec::new(); for poly_values in polys_values.iter() { let mut constr_selector = Vec::new(); let mut used = vec![false; matrix_selectors.len()]; diff --git a/src/ccs/compiler/unit.rs b/src/ccs/compiler/unit.rs index c5baab6a..21d20658 100644 --- a/src/ccs/compiler/unit.rs +++ b/src/ccs/compiler/unit.rs @@ -26,11 +26,8 @@ pub struct CompilationUnit { pub step_types: HashMap>>, pub placement: Placement, - pub exposed: Vec<(usize, SignalPlacement)>, - pub polys: HashMap>>, - pub selector: StepSelector, pub matrix_values: HashMap>, } @@ -41,17 +38,14 @@ impl Default for CompilationUnit { ast_id: Default::default(), uuid: uuid(), step_types: Default::default(), - forward_signals: Default::default(), shared_signals: Default::default(), fixed_signals: Default::default(), - annotations: Default::default(), exposed: Default::default(), num_steps: Default::default(), selector: Default::default(), polys: Default::default(), - placement: Default::default(), matrix_values: Default::default(), } diff --git a/src/ccs/ir/assignments.rs b/src/ccs/ir/assignments.rs index 4e9761ca..17cf746d 100644 --- a/src/ccs/ir/assignments.rs +++ b/src/ccs/ir/assignments.rs @@ -225,6 +225,7 @@ impl AssignmentGenerator { self.placement.forward(forward.uuid()).offset() } } + Queriable::Shared(shared, _) => self.placement.shared(shared.uuid()).offset(), Queriable::Fixed(fixed, _) => self.placement.fixed(fixed.uuid()).offset(), From ba71c66a91a4ef4e0bb9a75314fb902d48edde7d Mon Sep 17 00:00:00 2001 From: 10to4 Date: Thu, 20 Jun 2024 23:53:22 +0800 Subject: [PATCH 06/24] ccs: clean code --- examples/fibonacci_ccs.rs | 6 +- src/ccs/backend/ccs.rs | 231 ++++++++++++++++-------------- src/ccs/compiler/cell_manager.rs | 31 ++-- src/ccs/compiler/mod.rs | 123 ++++++++-------- src/ccs/compiler/step_selector.rs | 98 ++++++------- src/ccs/compiler/unit.rs | 25 ++-- src/ccs/ir/assignments.rs | 151 +++++++++---------- src/ccs/ir/circuit.rs | 89 +++++++----- src/ccs/ir/mod.rs | 40 ++++-- 9 files changed, 400 insertions(+), 394 deletions(-) diff --git a/examples/fibonacci_ccs.rs b/examples/fibonacci_ccs.rs index 129a2bf0..635b79a8 100644 --- a/examples/fibonacci_ccs.rs +++ b/examples/fibonacci_ccs.rs @@ -123,8 +123,7 @@ fn main() { let (chiquito, wit_gen, _) = fibo_circuit_ccs::(); let compiled = chiquito2CCS(chiquito); - let (witness, steps) = wit_gen.map(|g| g.generate(())).unzip(); - let circuit = ChiquitoCCSCircuit::new(compiled, witness, steps); + let circuit = ChiquitoCCSCircuit::new(compiled, wit_gen.map(|g| g.generate(()))); let (ccs, z) = circuit.configure(); let result = ccs.is_satisfied(&z); @@ -132,8 +131,7 @@ fn main() { let (chiquito, wit_gen, _) = fibo_circuit_ccs::(); let compiled = chiquito2CCS(chiquito); - let (witness, steps) = wit_gen.map(|g| g.generate(())).unzip(); - let circuit = ChiquitoCCSCircuit::new(compiled, witness, steps); + let circuit = ChiquitoCCSCircuit::new(compiled, wit_gen.map(|g| g.generate(()))); let (circuit, z) = circuit.configure(); let ccs = CCS { diff --git a/src/ccs/backend/ccs.rs b/src/ccs/backend/ccs.rs index 5d6d4def..962f508c 100644 --- a/src/ccs/backend/ccs.rs +++ b/src/ccs/backend/ccs.rs @@ -2,14 +2,16 @@ use std::{collections::HashMap, hash::Hash}; use crate::{ ccs::ir::{ - assignments::{Assignments, StepsID}, - circuit::Circuit, + assignments::Assignments, + circuit::{Circuit, SelectorsOffsetAndCoeff}, + CoeffsForProds, }, field::Field, util::UUID, }; -pub type PositionMap = HashMap<(usize, u128), usize>; +// ((step_idx, step_UUID), assignment_offset) +pub type AssignmentOffsets = HashMap<(usize, u128), usize>; #[allow(non_snake_case)] pub fn chiquito2CCS + Hash>(circuit: Circuit) -> ChiquitoCCS { @@ -17,7 +19,6 @@ pub fn chiquito2CCS + Hash>(circuit: Circuit) -> Chiquit } pub struct ChiquitoCCS> { - pub debug: bool, pub circuit: Circuit, pub ir_id: UUID, } @@ -25,31 +26,18 @@ pub struct ChiquitoCCS> { impl + Hash> ChiquitoCCS { pub fn new(circuit: Circuit) -> ChiquitoCCS { let ir_id = circuit.ast_id; - Self { - debug: true, - circuit, - ir_id, - } + Self { circuit, ir_id } } } pub struct ChiquitoCCSCircuit> { pub compiled: ChiquitoCCS, - pub steps_id: Option, pub witness: Option>, } impl + Hash> ChiquitoCCSCircuit { - pub fn new( - compiled: ChiquitoCCS, - witness: Option>, - steps_id: Option, - ) -> Self { - Self { - compiled, - witness, - steps_id, - } + pub fn new(compiled: ChiquitoCCS, witness: Option>) -> Self { + Self { compiled, witness } } pub fn instance(&self) -> HashMap<(usize, UUID), F> { @@ -65,56 +53,46 @@ impl + Hash> ChiquitoCCSCircuit { &self, n: usize, num: usize, - witness_pos: &PositionMap, + assign_pos: &AssignmentOffsets, ) -> (Vec>, usize) { - let mut m = self.num_poly(); - let mut matrix_vec = vec![Matrix::new(n, m); num]; + let mut matrix_vec = vec![Matrix::new(n, self.num_poly()); num]; let mut row = 0; - if let Some(steps_id) = self.steps_id.as_ref() { - for (idx, step_id) in steps_id.0.iter().enumerate() { - // step - let matrics = self.compiled.circuit.matrics.get(step_id).unwrap(); - for matrics in matrics.iter() { + if let Some(steps_id) = self.witness.as_ref() { + let step_num = steps_id.len(); + for (step_idx, (step_id, _)) in steps_id.iter().enumerate() { + for coeffs_one_poly in self + .compiled + .circuit + .matrix_coeffs_and_offsets + .get(step_id) + .unwrap() + .iter() + { // poly - if idx == steps_id.0.len() - 1 { - let mut skip = false; - for (matrics, _) in matrics.iter() { - if skip { - break; - } - for (_, _, next) in matrics.concat().iter() { - if *next { - skip = true; - break; - } - } - } - if skip { - m -= 1; - continue; - } + if is_last_step_with_next_signal(coeffs_one_poly, step_num, step_idx) { + continue; } - for (matrix_chunks, index) in matrics.iter() { + for (coeffs_chunks, index) in coeffs_one_poly.iter() { // chunk assert!(*index <= self.compiled.circuit.selectors.len()); let selectors = self.compiled.circuit.selectors[*index].clone(); - assert_eq!(matrix_chunks.len(), selectors.len()); + assert_eq!(coeffs_chunks.len(), selectors.len()); - for (items, (selector, _)) in matrix_chunks.iter().zip(selectors.iter()) { + for (coeffs, (selector, _)) in coeffs_chunks.iter().zip(selectors.iter()) { // one row in on matrix let mut values: Vec<(usize, usize, F)> = Vec::new(); - for (v, id, next) in items.iter() { - let idx = if *next { idx + 1 } else { idx }; - let col = if *id == 0 { - witness_pos.get(&(0, 0)) + for (value, signal_id, next) in coeffs.iter() { + let col = if *signal_id == 0 { + assign_pos.get(&(0, 0)) } else { - witness_pos.get(&(idx, *id)) + let idx = if *next { step_idx + 1 } else { step_idx }; + assign_pos.get(&(idx, *signal_id)) }; match col { None => continue, - Some(col) => values.push((row, *col, *v)), + Some(col) => values.push((row, *col, *value)), } } matrix_vec[*selector].write(&values); @@ -126,60 +104,92 @@ impl + Hash> ChiquitoCCSCircuit { }; for matrix in matrix_vec.iter_mut() { - if row < matrix.m { - matrix.remove_rows(row); - } + matrix.reduce_rows(row); } - (matrix_vec, m) + (matrix_vec, row) } fn num_poly(&self) -> usize { - self.steps_id + self.witness .as_ref() .map(|steps_idx| { steps_idx - .0 .iter() - .map(|idx| self.compiled.circuit.matrics.get(idx).unwrap().len()) + .map(|(idx, _)| { + self.compiled + .circuit + .matrix_coeffs_and_offsets + .get(idx) + .unwrap() + .len() + }) .sum() }) .unwrap() } - fn coeffs_offsets(&self) -> (PositionMap, PositionMap) { + fn coeffs_offsets(&self) -> (AssignmentOffsets, usize, usize) { + let mut public_pos = HashMap::new(); + for (offset, (step_idx, signal_uuid)) in self.compiled.circuit.exposed.iter().enumerate() { + public_pos.insert((*step_idx, *signal_uuid), offset); + } + let mut witness_pos = HashMap::new(); let mut offset = 0; witness_pos.insert((0, 0), offset); offset += 1; - if let Some(steps_id) = self.steps_id.as_ref() { - for (idx, step_idx) in steps_id.0.iter().enumerate() { - let witnesses = self.compiled.circuit.witness.get(step_idx).unwrap(); - for id in witnesses.iter() { - witness_pos.insert((idx, *id), offset); - offset += 1; + if let Some(assignments) = self.witness.as_ref() { + for (step_idx, (step_id, _)) in assignments.iter().enumerate() { + let signal_uuids = self.compiled.circuit.witness.get(step_id).unwrap(); + for id in signal_uuids.iter() { + let exist = public_pos.get(&(step_idx, *id)); + if exist.is_none() { + witness_pos.insert((step_idx, *id), offset); + offset += 1; + } } } } - let mut public_pos = HashMap::new(); - for (idx, id) in self.compiled.circuit.exposed.iter() { - public_pos.insert((*idx, *id), offset); + + for ((step_idx, id), v) in public_pos.iter() { + witness_pos.insert((*step_idx, *id), v + offset); offset += 1; } - // todo: remove public values - (witness_pos, public_pos) + (witness_pos, offset, public_pos.len()) } } +fn is_last_step_with_next_signal( + coeffs_one_poly: &[(CoeffsForProds, usize)], + step_num: usize, + step_idx: usize, +) -> bool { + let mut skip = false; + if step_idx == step_num - 1 { + for (coeffs_for_prods, _) in coeffs_one_poly.iter() { + if skip { + break; + } + for (_, _, next) in coeffs_for_prods.concat().iter() { + if *next { + skip = true; + break; + } + } + } + } + skip +} + impl + Hash> ChiquitoCCSCircuit { pub fn configure(&self) -> (CCSCircuit, Z) { - let (ccs, witness_pos, public_pos) = CCSCircuit::from_circuit(self); + let (ccs, assign_pos) = CCSCircuit::from_circuit(self); let mut z: Z = Z::new(ccs.n, ccs.l); z.write( &self.instance(), self.witness.as_deref().unwrap(), - &witness_pos, - &public_pos, + &assign_pos, ); (ccs, z) } @@ -219,27 +229,20 @@ impl + Hash> CCSCircuit { } } - pub fn from_circuit(circuit: &ChiquitoCCSCircuit) -> (Self, PositionMap, PositionMap) { + pub fn from_circuit(circuit: &ChiquitoCCSCircuit) -> (Self, AssignmentOffsets) { let selectors = circuit.compiled.circuit.selectors.clone(); - let constants = (0..circuit.compiled.circuit.q).map(|_| F::ONE).collect(); - - let mut matrix_num = 0; - for selector in selectors.iter() { - for (idx, _) in selector.iter() { - matrix_num = matrix_num.max(*idx + 1); - } - } + let constants = circuit.compiled.circuit.constants.clone(); - let (witness_pos, public_pos) = circuit.coeffs_offsets(); - let n = witness_pos.len() + public_pos.len(); + let (assign_pos, n, l) = circuit.coeffs_offsets(); - let (matrix_vec, m) = circuit.export_matrix_vec(n, matrix_num, &witness_pos); + let matrix_num = calc_matrix_num(&selectors); + let (matrix_vec, m) = circuit.export_matrix_vec(n, matrix_num, &assign_pos); ( Self { n, m, - l: public_pos.len(), + l, t: circuit.compiled.circuit.t, q: circuit.compiled.circuit.q, d: circuit.compiled.circuit.d, @@ -247,8 +250,7 @@ impl + Hash> CCSCircuit { selectors, constants, }, - witness_pos, - public_pos, + assign_pos, ) } @@ -364,6 +366,16 @@ pub fn vec_add + Hash>(vec: &[Vec]) -> Vec { }) } +fn calc_matrix_num(selectors: &SelectorsOffsetAndCoeff) -> usize { + let mut matrix_num = 0; + for selector in selectors.iter() { + for (idx, _) in selector.iter() { + matrix_num = matrix_num.max(*idx + 1); + } + } + matrix_num +} + #[derive(Debug, Clone)] pub struct Matrix { n: usize, @@ -394,10 +406,11 @@ impl Matrix { self.values[row][col] } - pub fn remove_rows(&mut self, m: usize) { - assert!(m <= self.m); - self.values = self.values[0..m].to_owned(); - self.m = m; + pub fn reduce_rows(&mut self, m: usize) { + if m < self.m { + self.values = self.values[0..m].to_owned(); + self.m = m; + } } pub fn size(&self) -> (usize, usize) { @@ -445,25 +458,27 @@ impl + Hash> Z { pub fn write( &mut self, inputs: &HashMap<(usize, UUID), F>, - witnesses: &[HashMap], - witness_pos: &PositionMap, - public_pos: &PositionMap, + witnesses: &[(UUID, HashMap)], + assign_pos: &AssignmentOffsets, ) { assert_eq!(inputs.len(), self.l); - - let mut witness_values = vec![F::ZERO; witness_pos.len() - 1]; - for ((step_idx, signal_id), idx) in witness_pos.iter() { + assert_eq!(assign_pos.len(), self.n); + let witness_len = self.n - self.l - 1; + let mut witness_values = vec![F::ZERO; witness_len]; + let mut public_values = vec![F::ZERO; self.l]; + for ((step_idx, signal_id), idx) in assign_pos.iter() { if *signal_id == 0 { continue; } - witness_values[idx - 1] = *witnesses[*step_idx].get(signal_id).unwrap(); - } - let mut public_values = vec![F::ZERO; public_pos.len()]; - for (pos, idx) in public_pos.iter() { - public_values[idx - witness_pos.len()] = *inputs.get(pos).unwrap(); + if *idx < self.n - self.l { + witness_values[*idx - 1] = *witnesses[*step_idx].1.get(signal_id).unwrap(); + } else { + public_values[*idx - witness_len - 1] = + *inputs.get(&(*step_idx, *signal_id)).unwrap(); + } } - self.assignments = witness_values.clone(); - self.public_inputs = public_values.clone(); + self.assignments = witness_values; + self.public_inputs = public_values; } pub fn write_with_values(&mut self, inputs: &[F], witnesses: &[F]) { diff --git a/src/ccs/compiler/cell_manager.rs b/src/ccs/compiler/cell_manager.rs index 8f557ed7..a86d82d7 100644 --- a/src/ccs/compiler/cell_manager.rs +++ b/src/ccs/compiler/cell_manager.rs @@ -23,7 +23,8 @@ impl CellManager for SingleRowCellManager { for signal in unit.forward_signals.iter() { placement.forward.insert( signal.uuid(), - SignalPlacement::new(signal.uuid(), signal.annotation(), forward_signals), + SignalPlacement::new(signal.uuid(), signal.annotation()), + // SignalPlacement::new(signal.uuid(), signal.annotation(), forward_signals), ); forward_signals += 1; } @@ -35,7 +36,7 @@ impl CellManager for SingleRowCellManager { SignalPlacement::new( signal.uuid(), signal.annotation(), - forward_signals + shared_signals, + // forward_signals + shared_signals, ), ); shared_signals += 1; @@ -48,7 +49,7 @@ impl CellManager for SingleRowCellManager { SignalPlacement::new( signal.uuid(), signal.annotation(), - forward_signals + shared_signals + fixed_signals, + // forward_signals + shared_signals + fixed_signals, ), ); fixed_signals += 1; @@ -62,7 +63,8 @@ impl CellManager for SingleRowCellManager { for signal in step.signals.iter() { step_placement.signals.insert( signal.uuid(), - SignalPlacement::new(signal.uuid(), signal.annotation(), internal_signals), + // SignalPlacement::new(signal.uuid(), signal.annotation(), internal_signals), + SignalPlacement::new(signal.uuid(), signal.annotation()), ); internal_signals += 1; } @@ -77,24 +79,11 @@ impl CellManager for SingleRowCellManager { pub struct SignalPlacement { id: UUID, annotation: String, - offset: u32, } impl SignalPlacement { - pub fn new(id: UUID, annotation: String, offset: u32) -> Self { - Self { - id, - annotation, - offset, - } - } - - pub fn new_with_id(id: UUID, annotation: String) -> Self { - Self { - id, - annotation, - offset: 0, - } + pub fn new(id: UUID, annotation: String) -> Self { + Self { id, annotation } } pub fn uuid(&self) -> UUID { @@ -104,10 +93,6 @@ impl SignalPlacement { pub fn annotation(&self) -> String { self.annotation.to_string() } - - pub fn offset(&self) -> u32 { - self.offset - } } #[derive(Debug, Clone)] diff --git a/src/ccs/compiler/mod.rs b/src/ccs/compiler/mod.rs index 115e87f6..2269a6bc 100644 --- a/src/ccs/compiler/mod.rs +++ b/src/ccs/compiler/mod.rs @@ -2,14 +2,15 @@ pub mod cell_manager; pub mod step_selector; pub(crate) mod unit; -use std::{hash::Hash, rc::Rc}; +use std::{collections::HashMap, hash::Hash, rc::Rc}; use unit::CompilationUnit; use crate::{ - ccs::ir::{assignments::AssignmentGenerator, circuit::Circuit, Poly, PolyExpr}, + ccs::ir::{assignments::AssignmentGenerator, circuit::Circuit, CoeffsForSteps, Poly, PolyExpr}, field::Field, poly::Expr, - sbpir::{query::Queriable, ExposeOffset, StepType, PIR, SBPIR as astCircuit}, + sbpir::{query::Queriable, ExposeOffset, PIR, SBPIR as astCircuit}, + util::{uuid, UUID}, wit_gen::{AutoTraceGenerator, TraceGenerator}, }; @@ -46,6 +47,8 @@ pub fn compile(&mut unit); let assignment = ast.trace.as_ref().map(|v| { @@ -53,7 +56,7 @@ pub fn compile(ast: &astCircuit, unit: &mut CompilationUnit) { for (queriable, offset) in &ast.exposed { - let exposed = match queriable { - Queriable::Forward(forward_signal, _) => { - let step = match offset { - ExposeOffset::First => 0, - ExposeOffset::Last => unit.num_steps - 1, - ExposeOffset::Step(step) => *step, - }; - ( - step, - SignalPlacement::new( - forward_signal.uuid(), - forward_signal.annotation(), - unit.placement.forward(forward_signal.uuid()).offset(), - ), - ) - } - Queriable::Shared(shared_signal, _) => { - let step = match offset { - ExposeOffset::First => 0, - ExposeOffset::Last => unit.num_steps - 1, - ExposeOffset::Step(step) => *step, - }; - ( - step, - SignalPlacement::new( - shared_signal.uuid(), - shared_signal.annotation(), - unit.placement.forward.len() as u32 - + unit.placement.forward(shared_signal.uuid()).offset(), - ), - ) - } + let step_idx = match offset { + ExposeOffset::First => 0, + ExposeOffset::Last => unit.num_steps - 1, + ExposeOffset::Step(step) => *step, + }; + let signal = match queriable { + Queriable::Forward(forward_signal, _) => SignalPlacement::new( + forward_signal.uuid(), + "exposed:".to_owned() + &forward_signal.annotation(), + ), + Queriable::Shared(shared_signal, _) => SignalPlacement::new( + shared_signal.uuid(), + "exposed:".to_owned() + &shared_signal.annotation(), + ), _ => panic!("Queriable was not Forward or Shared"), }; - unit.exposed.push(exposed); + unit.exposed.push((step_idx, signal)); } } fn compile_constraints( - _: &astCircuit, + ast: &astCircuit, unit: &mut CompilationUnit, ) { - for step in unit.step_types.clone().values() { + for step in ast.step_types.clone().values() { let step_annotation = unit .annotations .get(&step.uuid()) @@ -115,9 +100,9 @@ fn compile_constraints( let mut polys = Vec::new(); for constr in step.constraints.iter() { - let poly = transform_expr(unit, step, &constr.expr.clone()); + let poly = transform_expr(step.uuid(), &unit.annotations, &constr.expr.clone()); polys.push(Poly { - expr: poly, + uuid: uuid(), step_uuid: step.uuid(), annotation: format!( "{}::{} => {:?}", @@ -125,13 +110,14 @@ fn compile_constraints( constr.annotation.clone(), constr.expr ), + expr: poly, }) } for constr in step.transition_constraints.iter() { - let poly = transform_expr(unit, step, &constr.expr.clone()); + let poly = transform_expr(step.uuid(), &unit.annotations, &constr.expr.clone()); polys.push(Poly { - expr: poly, + uuid: uuid(), step_uuid: step.uuid(), annotation: format!( "{}::{} => {:?}", @@ -139,6 +125,7 @@ fn compile_constraints( constr.annotation.clone(), constr.expr ), + expr: poly, }) } @@ -147,39 +134,39 @@ fn compile_constraints( } fn transform_expr( - unit: &CompilationUnit, - step: &StepType, + step_id: UUID, + annotations: &HashMap, source: &PIR, ) -> PolyExpr { match source.clone() { Expr::Const(c) => PolyExpr::Const(c), - Expr::Query(q) => place_queriable(unit, step, q), + Expr::Query(q) => place_queriable(step_id, annotations, q), Expr::Sum(v) => PolyExpr::Sum( v.into_iter() - .map(|e| transform_expr(unit, step, &e)) + .map(|e| transform_expr(step_id, annotations, &e)) .collect(), ), Expr::Mul(v) => PolyExpr::Mul( v.into_iter() - .map(|e| transform_expr(unit, step, &e)) + .map(|e| transform_expr(step_id, annotations, &e)) .collect(), ), - Expr::Neg(v) => PolyExpr::Neg(Box::new(transform_expr(unit, step, &v))), - Expr::Pow(v, exp) => PolyExpr::Pow(Box::new(transform_expr(unit, step, &v)), exp), + Expr::Neg(v) => PolyExpr::Neg(Box::new(transform_expr(step_id, annotations, &v))), + Expr::Pow(v, exp) => PolyExpr::Pow(Box::new(transform_expr(step_id, annotations, &v)), exp), Expr::Halo2Expr(_) => panic!("halo2 expr not done"), Expr::MI(_) => panic!("mi elimination not done"), } } fn place_queriable( - unit: &CompilationUnit, - step: &StepType, + step_id: UUID, + annotations: &HashMap, q: Queriable, ) -> PolyExpr { match q { // (UUID, UUID, String, u32) Queriable::Forward(forward, next) => { - let annotation = if let Some(annotation) = unit.annotations.get(&forward.uuid()) { + let annotation = if let Some(annotation) = annotations.get(&forward.uuid()) { if next { format!("forward next({})", annotation) } else { @@ -188,10 +175,10 @@ fn place_queriable( } else { "forward".to_string() }; - PolyExpr::Query((forward.uuid(), step.uuid(), annotation, next)) + PolyExpr::Query((forward.uuid(), step_id, annotation, next)) } Queriable::Shared(shared, rot) => { - let annotation = if let Some(annotation) = unit.annotations.get(&shared.uuid()) { + let annotation = if let Some(annotation) = annotations.get(&shared.uuid()) { if rot == 0 { format!("shared({})", annotation) } else { @@ -200,10 +187,10 @@ fn place_queriable( } else { "forward".to_string() }; - PolyExpr::Query((shared.uuid(), step.uuid(), annotation, false)) + PolyExpr::Query((shared.uuid(), step_id, annotation, false)) } Queriable::Fixed(fixed, rot) => { - let annotation = if let Some(annotation) = unit.annotations.get(&fixed.uuid()) { + let annotation = if let Some(annotation) = annotations.get(&fixed.uuid()) { if rot == 0 { format!("fixed({})", annotation) } else { @@ -212,16 +199,16 @@ fn place_queriable( } else { "fixed".to_string() }; - PolyExpr::Query((fixed.uuid(), step.uuid(), annotation, false)) + PolyExpr::Query((fixed.uuid(), step_id, annotation, false)) } Queriable::Internal(signal) => { - let annotation = if let Some(annotation) = unit.annotations.get(&signal.uuid()) { + let annotation = if let Some(annotation) = annotations.get(&signal.uuid()) { format!("internal {}", annotation) } else { "internal".to_string() }; - PolyExpr::Query((signal.uuid(), step.uuid(), annotation, false)) + PolyExpr::Query((signal.uuid(), step_id, annotation, false)) } Queriable::StepTypeNext(_) => panic!("jarrl"), @@ -230,3 +217,19 @@ fn place_queriable( Queriable::_unaccessible(_) => panic!("jarrl"), } } + +fn compile_matrix_coeffs(unit: &mut CompilationUnit) { + let mut coeffs: CoeffsForSteps = HashMap::new(); + for (step_id, polys) in unit.polys.iter() { + // one step + let coeffs_one_step = polys + .iter() + .map(|poly| { + // one poly + poly.expr.poly_to_coeffs(true) + }) + .collect(); + coeffs.insert(*step_id, coeffs_one_step); + } + unit.matrix_coeffs = coeffs; +} diff --git a/src/ccs/compiler/step_selector.rs b/src/ccs/compiler/step_selector.rs index c285d263..1eef9859 100644 --- a/src/ccs/compiler/step_selector.rs +++ b/src/ccs/compiler/step_selector.rs @@ -1,14 +1,22 @@ -use crate::{ccs::ir::assignments::Coeffs, field::Field, util::UUID}; +use crate::{field::Field, util::UUID}; use super::{CompilationUnit, PolyExpr}; use std::collections::HashMap; pub type SelectorAssignment = (PolyExpr, F); +pub type SelectorForMatrix = (usize, F); +pub type SelectorForProd = Vec>; +pub type SelectorsForALLMatrix = Vec>; + +pub type SelectorsForPoly = Vec; +pub type SelectorsForOneStep = Vec; +pub type SelectorsForALLSteps = HashMap; + #[derive(Debug, Clone)] pub struct StepSelector { - pub matrix_selectors: Vec>, - pub step_selectors: HashMap>>, + pub matrix_selectors: SelectorsForALLMatrix, + pub step_selectors: SelectorsForALLSteps, } impl Default for StepSelector { @@ -29,63 +37,49 @@ pub struct SimpleStepSelectorBuilder {} impl StepSelectorBuilder for SimpleStepSelectorBuilder { fn build(&self, unit: &mut CompilationUnit) { - let mut matrix_values = HashMap::new(); - for (step_id, polys) in unit.polys.iter() { - // one step - let values = polys + let mut matrix_selectors: SelectorsForALLMatrix = Vec::new(); + let mut step_selectors: SelectorsForALLSteps = HashMap::new(); + + let mut matrix_num = 0; + + for (step_id, coeffs_one_step) in unit.matrix_coeffs.iter() { + let selectors_one_step = coeffs_one_step .iter() - .map(|poly| { - // one poly - poly.expr.poly_to_matrix(true) + .map(|coeffs_one_poly| { + let mut used = vec![false; matrix_selectors.len()]; + coeffs_one_poly + .iter() + .map(|coeffs_prods| { + let size = coeffs_prods.len(); + let mut offset = matrix_selectors.len(); + for (i, prods_selector) in matrix_selectors.iter().enumerate() { + if prods_selector.len() == size && !used[i] { + used[i] = true; + offset = i; + break; + } + } + if offset == matrix_selectors.len() { + matrix_selectors.push( + (matrix_num..size + matrix_num) + .map(|i| (i, F::ONE)) + .collect(), + ); + matrix_num += size; + used.push(true); + } + offset + }) + .collect() }) .collect(); - matrix_values.insert(*step_id, values); + + step_selectors.insert(*step_id, selectors_one_step); } - let mut matrix_selectors: Vec> = Vec::new(); - let mut step_selectors: HashMap>> = HashMap::new(); - construct_selector(&matrix_values, &mut matrix_selectors, &mut step_selectors); unit.selector = StepSelector { step_selectors, matrix_selectors, }; - unit.matrix_values = matrix_values; - } -} - -fn construct_selector( - values: &HashMap>, - matrix_selectors: &mut Vec>, - step_selectors: &mut HashMap>>, -) { - let mut total = matrix_selectors.iter().map(|v| v.len()).sum(); - - for (idx, polys_values) in values.iter() { - let mut step_selector = Vec::new(); - for poly_values in polys_values.iter() { - let mut constr_selector = Vec::new(); - let mut used = vec![false; matrix_selectors.len()]; - for chunk_values in poly_values.iter() { - let size = chunk_values.len(); - let mut pos = matrix_selectors.len(); - for (i, matrix_selector) in matrix_selectors.iter().enumerate() { - if matrix_selector.len() == size && !used[i] { - pos = i; - used[pos] = true; - constr_selector.push(pos); - break; - } - } - if pos == matrix_selectors.len() { - matrix_selectors.push((total..size + total).map(|i| (i, F::ONE)).collect()); - total += size; - used.push(true); - constr_selector.push(pos); - } - } - step_selector.push(constr_selector); - } - - step_selectors.insert(*idx, step_selector); } } diff --git a/src/ccs/compiler/unit.rs b/src/ccs/compiler/unit.rs index 21d20658..9f8ea32b 100644 --- a/src/ccs/compiler/unit.rs +++ b/src/ccs/compiler/unit.rs @@ -1,7 +1,7 @@ use crate::{ ccs::{ compiler::SignalPlacement, - ir::{assignments::Coeffs, circuit::Circuit, Poly}, + ir::{circuit::Circuit, CoeffsForSteps, Poly}, }, field::Field, sbpir::{FixedSignal, ForwardSignal, SharedSignal, StepType, SBPIR as astCircuit}, @@ -18,6 +18,7 @@ pub struct CompilationUnit { pub uuid: UUID, pub annotations: HashMap, + pub placement: Placement, pub forward_signals: Vec, pub shared_signals: Vec, pub fixed_signals: Vec, @@ -25,11 +26,11 @@ pub struct CompilationUnit { pub num_steps: usize, pub step_types: HashMap>>, - pub placement: Placement, pub exposed: Vec<(usize, SignalPlacement)>, + pub polys: HashMap>>, pub selector: StepSelector, - pub matrix_values: HashMap>, + pub matrix_coeffs: CoeffsForSteps, } impl Default for CompilationUnit { @@ -47,7 +48,7 @@ impl Default for CompilationUnit { selector: Default::default(), polys: Default::default(), placement: Default::default(), - matrix_values: Default::default(), + matrix_coeffs: Default::default(), } } } @@ -85,13 +86,13 @@ impl From> for Circuit { .iter() .map(|(step, exposed)| (*step, exposed.uuid())) .collect(); - let mut witnesses = HashMap::new(); - for (step_uuid, _) in unit.matrix_values.iter() { - let mut values: Vec = unit.placement.forward.keys().copied().collect(); - values.append(&mut unit.placement.shared.keys().copied().collect()); - values.append(&mut unit.placement.fixed.keys().copied().collect()); - values.append( + let mut witnesses = HashMap::new(); + for (step_uuid, _) in unit.matrix_coeffs.iter() { + let mut signal_uuids: Vec = unit.placement.forward.keys().copied().collect(); + signal_uuids.append(&mut unit.placement.shared.keys().copied().collect()); + signal_uuids.append(&mut unit.placement.fixed.keys().copied().collect()); + signal_uuids.append( &mut unit .placement .step(*step_uuid) @@ -100,10 +101,10 @@ impl From> for Circuit { .copied() .collect(), ); - witnesses.insert(*step_uuid, values); + witnesses.insert(*step_uuid, signal_uuids); } - circuit.write(&unit.matrix_values, &unit.selector, &exposed, &witnesses); + circuit.write(&unit.matrix_coeffs, &unit.selector, &exposed, &witnesses); circuit } diff --git a/src/ccs/ir/assignments.rs b/src/ccs/ir/assignments.rs index 17cf746d..78b9b5bc 100644 --- a/src/ccs/ir/assignments.rs +++ b/src/ccs/ir/assignments.rs @@ -14,38 +14,43 @@ use crate::{ pub type Coeffs = Vec>>>; -pub type MatrixsCoeffs = Vec>, usize)>>; +// #[derive(Debug, Clone)] +// pub struct StepsID(pub Vec); -#[derive(Debug, Clone)] -pub struct StepsID(pub Vec); +// impl Default for StepsID { +// fn default() -> Self { +// Self::new() +// } +// } -impl Default for StepsID { - fn default() -> Self { - Self::new() - } -} +// impl StepsID { +// pub fn new() -> Self { +// Self(Vec::new()) +// } -impl StepsID { - pub fn new() -> Self { - Self(Vec::new()) - } +// pub fn new_with_witness(witness: &TraceWitness) -> Self { - pub fn len(&self) -> usize { - self.0.len() - } +// let uuids = witness.step_instances.iter().map(|step| step.step_type_uuid).collect(); +// Self(uuids) - pub fn is_empty(&self) -> bool { - self.len() == 0 - } +// } - pub fn read(&self, index: usize) -> UUID { - assert!(index < self.len()); - self.0[index] - } -} +// pub fn len(&self) -> usize { +// self.0.len() +// } + +// pub fn is_empty(&self) -> bool { +// self.len() == 0 +// } + +// pub fn read(&self, index: usize) -> UUID { +// assert!(index < self.len()); +// self.0[index] +// } +// } #[derive(Debug, Clone)] -pub struct Assignments(pub Vec>); +pub struct Assignments(pub Vec<(UUID, HashMap)>); impl Default for Assignments { fn default() -> Self { @@ -54,7 +59,7 @@ impl Default for Assignments { } impl Deref for Assignments { - type Target = Vec>; + type Target = Vec<(UUID, HashMap)>; fn deref(&self) -> &Self::Target { &self.0 @@ -68,10 +73,27 @@ impl DerefMut for Assignments { } impl Assignments { - pub fn new(values: Vec>) -> Self { + pub fn new(values: Vec<(UUID, HashMap)>) -> Self { Self(values) } + pub fn new_with_witness(witness: &TraceWitness) -> Self { + let values = witness + .step_instances + .iter() + .map(|step_instance| { + let step_id = step_instance.step_type_uuid; + let mut values = HashMap::new(); + for (q, v) in step_instance.assignments.iter() { + values.insert(q.uuid(), *v); + } + (step_id, values) + }) + .collect(); + + Self::new(values) + } + pub fn len(&self) -> usize { self.0.len() } @@ -80,20 +102,20 @@ impl Assignments { self.len() == 0 } - pub fn append(&mut self, ass: &mut Vec>) { + pub fn append(&mut self, ass: &mut Vec<(UUID, HashMap)>) { self.0.append(ass) } - pub fn read(&self) -> Vec> { + pub fn read(&self) -> Vec<(UUID, HashMap)> { self.0.clone() } pub fn get(&self, step_idx: usize, signal_id: UUID) -> F { - *self.0[step_idx].get(&signal_id).unwrap() + *self.0[step_idx].1.get(&signal_id).unwrap() } pub fn write(&mut self, step_idx: usize, signal_id: UUID, value: F) { - self.0[step_idx].insert(signal_id, value); + self.0[step_idx].1.insert(signal_id, value); } } @@ -156,40 +178,22 @@ impl AssignmentGenerator { self.trace_gen.generate(args) } - pub fn generate(&self, args: TraceArgs) -> (Assignments, StepsID) { + pub fn generate(&self, args: TraceArgs) -> Assignments { let witness = self.trace_gen.generate(args); self.generate_with_witness(witness) } - pub fn generate_with_witness(&self, witness: TraceWitness) -> (Assignments, StepsID) { + pub fn generate_with_witness(&self, witness: TraceWitness) -> Assignments { let witness = self.auto_trace_gen.generate(witness); - let values: Vec> = witness - .step_instances - .iter() - .map(|step_instance| { - let mut values = HashMap::new(); - for (q, v) in step_instance.assignments.iter() { - values.insert(q.uuid(), *v); - } - values - }) - .collect(); - let mut assignments: Assignments = Assignments::new(values); + let mut assignments = Assignments::new_with_witness(&witness); - let mut steps_id: StepsID = StepsID::new(); for (idx, step_instance) in witness.step_instances.iter().enumerate() { - self.assign_step( - idx, - step_instance, - &mut assignments, - witness.step_instances.len(), - ); - steps_id.0.push(step_instance.step_type_uuid); + self.assign_step(idx, step_instance, &mut assignments); } - (assignments, steps_id) + assignments } pub fn assign_step( @@ -197,48 +201,29 @@ impl AssignmentGenerator { idx: usize, step_instance: &StepInstance, assignments: &mut Assignments, - step_num: usize, ) { - let step_uuid = step_instance.step_type_uuid; - let height = self.placement.base_height + self.placement.step_height(step_uuid); - for (lhs, &rhs) in step_instance.assignments.iter() { - let offset = self.find_placement(step_uuid, lhs, height); - if offset < height { - assignments.write(idx, lhs.uuid(), rhs); - } else if idx < step_num - 1 { + let next = is_next(lhs); + if next { assignments.write(idx + 1, lhs.uuid(), rhs); + } else { + assignments.write(idx, lhs.uuid(), rhs); } } } - fn find_placement(&self, step_uuid: UUID, query: &Queriable, height: u32) -> u32 { - match query { - Queriable::Internal(signal) => { - self.placement.internal(step_uuid, signal.uuid()).offset() - } - - Queriable::Forward(forward, next) => { - if *next { - self.placement.forward(forward.uuid()).offset() + height - } else { - self.placement.forward(forward.uuid()).offset() - } - } - - Queriable::Shared(shared, _) => self.placement.shared(shared.uuid()).offset(), - - Queriable::Fixed(fixed, _) => self.placement.fixed(fixed.uuid()).offset(), - - _ => panic!("invalid advice assignment on queriable {:?}", query), - } - } - pub fn uuid(&self) -> UUID { self.ir_id } } +fn is_next(query: &Queriable) -> bool { + match query { + Queriable::Forward(_, next) => *next, + _ => false, + } +} + pub struct PublicInputs(pub Vec); impl Default for PublicInputs { diff --git a/src/ccs/ir/circuit.rs b/src/ccs/ir/circuit.rs index 21dba737..13e301bd 100644 --- a/src/ccs/ir/circuit.rs +++ b/src/ccs/ir/circuit.rs @@ -1,14 +1,20 @@ use std::{collections::HashMap, hash::Hash}; -use super::assignments::*; -use crate::{ccs::compiler::step_selector::StepSelector, field::Field, util::UUID}; +use super::{assignments::*, CoeffsForProds}; +use crate::{ + ccs::compiler::step_selector::{SelectorsForALLMatrix, SelectorsForALLSteps, StepSelector}, + field::Field, + util::UUID, +}; +pub type MatrixCoeffsAndOffset = Vec, usize)>>; +pub type SelectorsOffsetAndCoeff = Vec>; #[derive(Debug)] pub struct Circuit { pub ast_id: UUID, - pub matrics: HashMap>, - pub selectors: Vec>, + pub matrix_coeffs_and_offsets: HashMap>, + pub selectors: SelectorsOffsetAndCoeff, pub constants: Vec, pub exposed: Vec<(usize, UUID)>, pub witness: HashMap>, @@ -20,67 +26,76 @@ pub struct Circuit { impl + Hash> Circuit { pub fn new(ast_id: UUID) -> Self { - let matrics = HashMap::new(); - let selectors = Vec::new(); - let constants = Vec::new(); - let exposed = vec![]; - let witness = HashMap::new(); - Self { t: 0, q: 0, d: 0, - matrics, - selectors, - constants, - exposed, - witness, + matrix_coeffs_and_offsets: HashMap::new(), + selectors: Vec::new(), + constants: Vec::new(), + exposed: Vec::new(), + witness: HashMap::new(), ast_id, } } pub fn write( &mut self, - matrix_values: &HashMap>, + matrix_coeffs: &HashMap>, selectors: &StepSelector, exposed: &[(usize, UUID)], witness: &HashMap>, ) { - let mut t = 0; - let mut d = 0; + self.calcu_bounds(&selectors.matrix_selectors); - self.q = selectors.matrix_selectors.len(); self.constants = vec![F::ONE; self.q]; + self.selectors = selectors.matrix_selectors.clone(); + self.exposed = exposed.to_owned(); + self.witness = witness.clone(); + + self.construct_matrix_coeffs_and_offsets(matrix_coeffs, &selectors.step_selectors); + } + + fn calcu_bounds(&mut self, matrix_selectors: &SelectorsForALLMatrix) { + let mut t = 0; + let mut d = 0; + self.q = matrix_selectors.len(); - for selectors in selectors.matrix_selectors.iter() { + for selectors in matrix_selectors.iter() { d = d.max(selectors.len()); - for (selector, _) in selectors.iter() { - t = t.max(*selector); + for (idx, _) in selectors.iter() { + t = t.max(*idx); } } - self.selectors = selectors.matrix_selectors.clone(); + self.t = t; + self.d = d; + } - let mut matrics = HashMap::new(); - for (uuid, values) in matrix_values.iter() { - let selectors = selectors.step_selectors.get(uuid).unwrap(); - let v = values + fn construct_matrix_coeffs_and_offsets( + &mut self, + matrix_coeffs: &HashMap>, + step_selectors: &SelectorsForALLSteps, + ) { + let mut matrix_coeffs_and_offsets = HashMap::new(); + for (step_id, coeffs_one_step) in matrix_coeffs.iter() { + let selectors_one_step = step_selectors.get(step_id).unwrap(); + let v = coeffs_one_step .iter() - .zip(selectors.iter()) - .map(|(values, selectors)| { - values + .zip(selectors_one_step.iter()) + .map(|(coeffs_one_poly, selectors_one_poly)| { + coeffs_one_poly .iter() - .zip(selectors.iter()) - .map(|(values, selectors)| (values.clone(), *selectors)) + .zip(selectors_one_poly.iter()) + .map(|(coeffs_one_chunk, selectors_one_chunk)| { + (coeffs_one_chunk.clone(), *selectors_one_chunk) + }) .collect() }) .collect(); - matrics.insert(*uuid, v); + matrix_coeffs_and_offsets.insert(*step_id, v); } - self.matrics = matrics; - self.exposed = exposed.to_owned(); - self.witness = witness.clone(); - self.selectors = selectors.matrix_selectors.clone(); + self.matrix_coeffs_and_offsets = matrix_coeffs_and_offsets; } pub fn instance(&self, assignments: &Assignments) -> HashMap<(usize, UUID), F> { diff --git a/src/ccs/ir/mod.rs b/src/ccs/ir/mod.rs index 5a7fc561..6b24c89b 100644 --- a/src/ccs/ir/mod.rs +++ b/src/ccs/ir/mod.rs @@ -1,4 +1,4 @@ -use std::fmt::Debug; +use std::{collections::HashMap, fmt::Debug}; pub mod assignments; pub mod circuit; @@ -7,6 +7,7 @@ use crate::{field::Field, poly::Expr, util::UUID}; #[derive(Clone)] pub struct Poly { + pub uuid: UUID, pub step_uuid: UUID, pub annotation: String, pub expr: PolyExpr, @@ -21,27 +22,36 @@ impl Debug for Poly { // (signal_uuid, step_uuid, annotation, pos) pub type PolyExpr = Expr; +pub type Coeff = (F, UUID, bool); +pub type Coeffs = Vec>; +pub type CoeffsForProds = Vec>; +pub type CoeffsOnePoly = Vec>; +pub type CoeffsOneStep = Vec>; +pub type CoeffsForSteps = HashMap>; + +pub type MatrixsCoeffs = Vec, usize)>>; + impl PolyExpr { - pub fn poly_to_matrix(&self, flag: bool) -> Vec>> { + pub fn poly_to_coeffs(&self, flag: bool) -> CoeffsOnePoly { let matrics = match self { PolyExpr::Const(v) => vec![vec![vec![(*v, 0, false)]]], PolyExpr::Query((id, _, _, q)) => vec![vec![vec![(F::ONE, *id, *q)]]], PolyExpr::Neg(v) => { - let mut matrics = v.poly_to_matrix(flag); - for matrixs in matrics.iter_mut() { - matrixs.push(vec![(F::ONE.neg(), 0, false)]); + let mut coeffs_for_one_poly = v.poly_to_coeffs(flag); + for coeffs_for_prods in coeffs_for_one_poly.iter_mut() { + coeffs_for_prods.push(vec![(F::ONE.neg(), 0, false)]); } - matrics + coeffs_for_one_poly } PolyExpr::Sum(v) => { if flag { - let mut matrics = Vec::new(); + let mut coeffs_for_one_poly: Vec> = Vec::new(); for e in v.iter() { - matrics.append(&mut e.poly_to_matrix(false)); + coeffs_for_one_poly.append(&mut e.poly_to_coeffs(false)); } - matrics + coeffs_for_one_poly } else { - let values = v + let coeffs = v .iter() .map(|e| match *e { PolyExpr::Const(v) => (v, 0, false), @@ -50,13 +60,13 @@ impl PolyExpr { _ => panic!("invalid poly expr"), }) .collect(); - vec![vec![values]] + vec![vec![coeffs]] } } PolyExpr::Mul(v) => { let mut matrics = Vec::new(); for e in v.iter() { - let matrix = e.poly_to_matrix(false); + let matrix = e.poly_to_coeffs(false); if matrix.len() != 1 { panic!("invalid poly expr"); } @@ -67,12 +77,12 @@ impl PolyExpr { vec![matrics] } PolyExpr::Pow(v, exp) => { - let matrics = v.poly_to_matrix(flag); - if matrics.len() != 1 { + let coeffs = v.poly_to_coeffs(flag); + if coeffs.len() != 1 { panic!("invalid poly expr"); } (0..*exp) - .map(|_| matrics.clone()) + .map(|_| coeffs.clone()) .collect::>() .concat() } From 1d5151b424ef95bc64489b1d188fc459ab715a97 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Fri, 21 Jun 2024 21:42:47 +0800 Subject: [PATCH 07/24] ccs: refactor sonobe integration and clean code --- examples/fibonacci_ccs.rs | 92 ++++++++------------------------ src/ccs/backend/ccs.rs | 15 ++---- src/ccs/backend/mod.rs | 1 + src/ccs/backend/sonobe.rs | 54 +++++++++++++++++++ src/ccs/compiler/cell_manager.rs | 14 +---- 5 files changed, 82 insertions(+), 94 deletions(-) create mode 100644 src/ccs/backend/sonobe.rs diff --git a/examples/fibonacci_ccs.rs b/examples/fibonacci_ccs.rs index 635b79a8..4992174a 100644 --- a/examples/fibonacci_ccs.rs +++ b/examples/fibonacci_ccs.rs @@ -116,9 +116,9 @@ fn fibo_circuit_ccs + Hash>() -> FiboReturn { // integrated into a Halo2 circuit, which is done by the boilerplate code below. use ark_bn254::Fr as FFr; -use ark_std::log2; -use folding_schemes::{ccs::CCS, utils::vec::dense_matrix_to_sparse}; +use ark_ff::BigInt; use halo2_proofs::halo2curves::{bn256::Fr, ff::PrimeField}; + fn main() { let (chiquito, wit_gen, _) = fibo_circuit_ccs::(); let compiled = chiquito2CCS(chiquito); @@ -134,76 +134,26 @@ fn main() { let circuit = ChiquitoCCSCircuit::new(compiled, wit_gen.map(|g| g.generate(()))); let (circuit, z) = circuit.configure(); - let ccs = CCS { - m: circuit.m, - n: circuit.n, - l: circuit.l, - t: circuit.t, - q: circuit.q, - d: circuit.d, - s: log2(circuit.m) as usize, - s_prime: log2(circuit.n) as usize, - M: circuit - .matrics - .iter() - .map(|matrix| { - let values = matrix - .values() - .iter() - .map(|m| { - m.iter() - .map(|r| { - // todo() - if Fr::ONE.neg().eq(r) { - FFr::from(-1) - } else { - let mut array = [0u8; 8]; - array.copy_from_slice(&r.to_repr().as_ref()[0..8]); - FFr::from(u64::from_le_bytes(array)) - } - }) - .collect() - }) - .collect(); - dense_matrix_to_sparse(values) - }) - .collect(), - S: circuit - .selectors - .iter() - .map(|selectors| selectors.iter().map(|(idx, _)| *idx).collect()) - .collect(), - c: (0..circuit.q).map(|_| FFr::from(1)).collect(), - }; - - let assignments = z - .assignments - .iter() - .map(|r| { - if Fr::ONE.neg().eq(r) { - FFr::from(-1) - } else { - let mut array = [0u8; 8]; - array.copy_from_slice(&r.to_repr().as_ref()[0..8]); - FFr::from(u64::from_le_bytes(array)) - } - }) - .collect(); - let public_inputs = z - .public_inputs - .iter() - .map(|r| { - if Fr::ONE.neg().eq(r) { - FFr::from(-1) - } else { - let mut array = [0u8; 8]; - array.copy_from_slice(&r.to_repr().as_ref()[0..8]); - FFr::from(u64::from_le_bytes(array)) - } - }) - .collect(); - let inputs = [vec![FFr::from(1)], assignments, public_inputs].concat(); + let ccs = circuit.convert_to_sonobe_circuit(fr_convert); + let inputs = z.convert_to_sonobe_inputs(fr_convert); let result = ccs.check_relation(&inputs); println!("sonobe fibonacci = {:?}", result); } + +fn fr_convert(r: &Fr) -> FFr { + let mut array1 = [0u8; 8]; + array1.copy_from_slice(&r.to_repr().as_ref()[0..8]); + let mut array2 = [0u8; 8]; + array2.copy_from_slice(&r.to_repr().as_ref()[8..16]); + let mut array3 = [0u8; 8]; + array3.copy_from_slice(&r.to_repr().as_ref()[16..24]); + let mut array4 = [0u8; 8]; + array4.copy_from_slice(&r.to_repr().as_ref()[24..32]); + FFr::from(BigInt::new([ + u64::from_le_bytes(array1), + u64::from_le_bytes(array2), + u64::from_le_bytes(array3), + u64::from_le_bytes(array4), + ])) +} diff --git a/src/ccs/backend/ccs.rs b/src/ccs/backend/ccs.rs index 962f508c..f41b917d 100644 --- a/src/ccs/backend/ccs.rs +++ b/src/ccs/backend/ccs.rs @@ -69,13 +69,11 @@ impl + Hash> ChiquitoCCSCircuit { .unwrap() .iter() { - // poly if is_last_step_with_next_signal(coeffs_one_poly, step_num, step_idx) { continue; } for (coeffs_chunks, index) in coeffs_one_poly.iter() { - // chunk assert!(*index <= self.compiled.circuit.selectors.len()); let selectors = self.compiled.circuit.selectors[*index].clone(); assert_eq!(coeffs_chunks.len(), selectors.len()); @@ -128,7 +126,7 @@ impl + Hash> ChiquitoCCSCircuit { .unwrap() } - fn coeffs_offsets(&self) -> (AssignmentOffsets, usize, usize) { + fn assignments_coeff_offset(&self) -> (AssignmentOffsets, usize, usize) { let mut public_pos = HashMap::new(); for (offset, (step_idx, signal_uuid)) in self.compiled.circuit.exposed.iter().enumerate() { public_pos.insert((*step_idx, *signal_uuid), offset); @@ -164,21 +162,16 @@ fn is_last_step_with_next_signal( step_num: usize, step_idx: usize, ) -> bool { - let mut skip = false; if step_idx == step_num - 1 { for (coeffs_for_prods, _) in coeffs_one_poly.iter() { - if skip { - break; - } for (_, _, next) in coeffs_for_prods.concat().iter() { if *next { - skip = true; - break; + return true; } } } } - skip + false } impl + Hash> ChiquitoCCSCircuit { @@ -233,7 +226,7 @@ impl + Hash> CCSCircuit { let selectors = circuit.compiled.circuit.selectors.clone(); let constants = circuit.compiled.circuit.constants.clone(); - let (assign_pos, n, l) = circuit.coeffs_offsets(); + let (assign_pos, n, l) = circuit.assignments_coeff_offset(); let matrix_num = calc_matrix_num(&selectors); let (matrix_vec, m) = circuit.export_matrix_vec(n, matrix_num, &assign_pos); diff --git a/src/ccs/backend/mod.rs b/src/ccs/backend/mod.rs index ce82ffd9..43dc6b0e 100644 --- a/src/ccs/backend/mod.rs +++ b/src/ccs/backend/mod.rs @@ -1 +1,2 @@ pub mod ccs; +pub mod sonobe; diff --git a/src/ccs/backend/sonobe.rs b/src/ccs/backend/sonobe.rs new file mode 100644 index 00000000..995b8d76 --- /dev/null +++ b/src/ccs/backend/sonobe.rs @@ -0,0 +1,54 @@ +// use ark_bn254::Fr as FFr; +use ark_ff::PrimeField; +use ark_std::log2; +use folding_schemes::{ccs::CCS, utils::vec::dense_matrix_to_sparse}; + +use super::ccs::{CCSCircuit, Z}; +use crate::field::Field; + +impl CCSCircuit { + pub fn convert_to_sonobe_circuit(&self, f: fn(&F) -> Fr) -> CCS { + let matrics = self + .matrics + .iter() + .map(|matrix| { + let values = matrix + .values() + .iter() + .map(|values| values.iter().map(f).collect()) + .collect(); + dense_matrix_to_sparse(values) + }) + .collect(); + let selectors = self + .selectors + .iter() + .map(|selectors| selectors.iter().map(|(idx, _)| *idx).collect()) + .collect(); + CCS { + m: self.m, + n: self.n, + l: self.l, + t: self.t, + q: self.q, + d: self.d, + s: log2(self.m) as usize, + s_prime: log2(self.n) as usize, + M: matrics, + S: selectors, + c: (0..self.q).map(|_| Fr::from(1u64)).collect(), + } + } +} + +impl Z { + pub fn convert_to_sonobe_inputs(&self, f: fn(&F) -> Fr) -> Vec { + let values = [ + vec![F::from(1u64)], + self.assignments.clone(), + self.public_inputs.clone(), + ] + .concat(); + values.iter().map(f).collect() + } +} diff --git a/src/ccs/compiler/cell_manager.rs b/src/ccs/compiler/cell_manager.rs index a86d82d7..7d297251 100644 --- a/src/ccs/compiler/cell_manager.rs +++ b/src/ccs/compiler/cell_manager.rs @@ -24,7 +24,6 @@ impl CellManager for SingleRowCellManager { placement.forward.insert( signal.uuid(), SignalPlacement::new(signal.uuid(), signal.annotation()), - // SignalPlacement::new(signal.uuid(), signal.annotation(), forward_signals), ); forward_signals += 1; } @@ -33,11 +32,7 @@ impl CellManager for SingleRowCellManager { for signal in unit.shared_signals.iter() { placement.shared.insert( signal.uuid(), - SignalPlacement::new( - signal.uuid(), - signal.annotation(), - // forward_signals + shared_signals, - ), + SignalPlacement::new(signal.uuid(), signal.annotation()), ); shared_signals += 1; } @@ -46,11 +41,7 @@ impl CellManager for SingleRowCellManager { for signal in unit.fixed_signals.iter() { placement.fixed.insert( signal.uuid(), - SignalPlacement::new( - signal.uuid(), - signal.annotation(), - // forward_signals + shared_signals + fixed_signals, - ), + SignalPlacement::new(signal.uuid(), signal.annotation()), ); fixed_signals += 1; } @@ -63,7 +54,6 @@ impl CellManager for SingleRowCellManager { for signal in step.signals.iter() { step_placement.signals.insert( signal.uuid(), - // SignalPlacement::new(signal.uuid(), signal.annotation(), internal_signals), SignalPlacement::new(signal.uuid(), signal.annotation()), ); internal_signals += 1; From c41caf91211c4b33cf377212c9f05522f69acef1 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Mon, 24 Jun 2024 17:59:33 +0800 Subject: [PATCH 08/24] ccs: add factorization for polynomials --- src/ccs/compiler/mod.rs | 3 +- src/ccs/ir/assignments.rs | 35 --------- src/ccs/ir/mod.rs | 155 +++++++++++++++++++++++++++++--------- 3 files changed, 121 insertions(+), 72 deletions(-) diff --git a/src/ccs/compiler/mod.rs b/src/ccs/compiler/mod.rs index 2269a6bc..983538e2 100644 --- a/src/ccs/compiler/mod.rs +++ b/src/ccs/compiler/mod.rs @@ -226,7 +226,8 @@ fn compile_matrix_coeffs(unit: &mut CompilationUnit) { .iter() .map(|poly| { // one poly - poly.expr.poly_to_coeffs(true) + let poly = poly.expr.factor_expr(); + poly.poly_to_coeffs() }) .collect(); coeffs.insert(*step_id, coeffs_one_step); diff --git a/src/ccs/ir/assignments.rs b/src/ccs/ir/assignments.rs index 78b9b5bc..1b0882d6 100644 --- a/src/ccs/ir/assignments.rs +++ b/src/ccs/ir/assignments.rs @@ -14,41 +14,6 @@ use crate::{ pub type Coeffs = Vec>>>; -// #[derive(Debug, Clone)] -// pub struct StepsID(pub Vec); - -// impl Default for StepsID { -// fn default() -> Self { -// Self::new() -// } -// } - -// impl StepsID { -// pub fn new() -> Self { -// Self(Vec::new()) -// } - -// pub fn new_with_witness(witness: &TraceWitness) -> Self { - -// let uuids = witness.step_instances.iter().map(|step| step.step_type_uuid).collect(); -// Self(uuids) - -// } - -// pub fn len(&self) -> usize { -// self.0.len() -// } - -// pub fn is_empty(&self) -> bool { -// self.len() == 0 -// } - -// pub fn read(&self, index: usize) -> UUID { -// assert!(index < self.len()); -// self.0[index] -// } -// } - #[derive(Debug, Clone)] pub struct Assignments(pub Vec<(UUID, HashMap)>); diff --git a/src/ccs/ir/mod.rs b/src/ccs/ir/mod.rs index 6b24c89b..708131be 100644 --- a/src/ccs/ir/mod.rs +++ b/src/ccs/ir/mod.rs @@ -1,10 +1,9 @@ use std::{collections::HashMap, fmt::Debug}; +use crate::{field::Field, poly::Expr, util::UUID}; pub mod assignments; pub mod circuit; -use crate::{field::Field, poly::Expr, util::UUID}; - #[derive(Clone)] pub struct Poly { pub uuid: UUID, @@ -29,46 +28,60 @@ pub type CoeffsOnePoly = Vec>; pub type CoeffsOneStep = Vec>; pub type CoeffsForSteps = HashMap>; -pub type MatrixsCoeffs = Vec, usize)>>; - impl PolyExpr { - pub fn poly_to_coeffs(&self, flag: bool) -> CoeffsOnePoly { + pub fn poly_to_coeffs(&self) -> CoeffsOnePoly { let matrics = match self { PolyExpr::Const(v) => vec![vec![vec![(*v, 0, false)]]], PolyExpr::Query((id, _, _, q)) => vec![vec![vec![(F::ONE, *id, *q)]]], - PolyExpr::Neg(v) => { - let mut coeffs_for_one_poly = v.poly_to_coeffs(flag); - for coeffs_for_prods in coeffs_for_one_poly.iter_mut() { - coeffs_for_prods.push(vec![(F::ONE.neg(), 0, false)]); - } - coeffs_for_one_poly - } PolyExpr::Sum(v) => { - if flag { + let mut flag = true; + for v in v.iter() { + flag = match v { + Expr::Const(_) => flag, + Expr::Query(_) => flag, + _ => false, + }; + if !flag { + break; + } + } + if !flag { let mut coeffs_for_one_poly: Vec> = Vec::new(); for e in v.iter() { - coeffs_for_one_poly.append(&mut e.poly_to_coeffs(false)); + coeffs_for_one_poly.append(&mut e.poly_to_coeffs()); } coeffs_for_one_poly } else { - let coeffs = v - .iter() - .map(|e| match *e { - PolyExpr::Const(v) => (v, 0, false), - PolyExpr::Query((id, _, _, q)) => (F::ONE, id, q), - PolyExpr::Neg(_) => (F::ONE.neg(), 0, false), - _ => panic!("invalid poly expr"), - }) - .collect(); + let mut coeffs: HashMap<(UUID, bool), F> = HashMap::new(); + for e in v.iter() { + if let Expr::Const(v) = e { + match coeffs.get(&(0, false)) { + None => coeffs.insert((0, false), *v), + Some(&value) => { + let v = value + *v; + coeffs.insert((0, false), v) + } + }; + } else if let Expr::Query((id, _, _, q)) = e { + match coeffs.get(&(*id, *q)) { + None => coeffs.insert((*id, *q), F::ONE), + Some(&value) => { + let v = value + F::ONE; + coeffs.insert((*id, *q), v) + } + }; + } + } + let coeffs = coeffs.iter().map(|((id, q), v)| (*v, *id, *q)).collect(); vec![vec![coeffs]] } } PolyExpr::Mul(v) => { let mut matrics = Vec::new(); for e in v.iter() { - let matrix = e.poly_to_coeffs(false); + let matrix = e.poly_to_coeffs(); if matrix.len() != 1 { - panic!("invalid poly expr"); + panic!("invalid poly expr at poly_to_coeffs with PolyExpr::Mul"); } for m in matrix[0].iter() { matrics.push(m.clone()); @@ -76,18 +89,88 @@ impl PolyExpr { } vec![matrics] } - PolyExpr::Pow(v, exp) => { - let coeffs = v.poly_to_coeffs(flag); - if coeffs.len() != 1 { - panic!("invalid poly expr"); - } - (0..*exp) - .map(|_| coeffs.clone()) - .collect::>() - .concat() - } - _ => panic!("invalid poly expr"), + _ => panic!("invalid poly expr at poly_to_coeffs"), }; matrics } + + pub fn factor_expr(&self) -> PolyExpr { + let poly = self.pre_factor(); + poly.factor() + } + + fn pre_factor(&self) -> PolyExpr { + match self { + Expr::Const(_) => self.clone(), + Expr::Query(_) => self.clone(), + Expr::Sum(v) => PolyExpr::Sum(v.iter().map(|e| e.pre_factor()).collect()), + Expr::Neg(v) => PolyExpr::Mul(vec![v.pre_factor(), Expr::Const(F::ONE.neg())]), + Expr::Pow(v, exp) => { + let v = v.pre_factor(); + PolyExpr::Mul(vec![v; *exp as usize]) + } + Expr::Mul(v) => PolyExpr::Mul(v.iter().map(|e| e.pre_factor()).collect()), + Expr::Halo2Expr(_) => panic!("halo2 expr not done"), + Expr::MI(_) => panic!("mi elimination not done"), + } + } + + fn factor(&self) -> PolyExpr { + match self { + Expr::Const(_) => self.clone(), + Expr::Query(_) => self.clone(), + Expr::Sum(v) => PolyExpr::Sum(v.iter().map(|e| e.factor()).collect()), + Expr::Mul(v) => { + let v: Vec> = v.iter().map(|e| e.factor()).collect(); + factor_mul(v) + } + _ => panic!("invalid expr"), + } + } } + +fn factor_mul(polys: Vec>) -> PolyExpr { + let mut sum_polys = Vec::new(); + let mut no_sum_polys = Vec::new(); + for v in polys.iter() { + if let Expr::Sum(_) = v { + sum_polys.push(v.clone()); + } else { + no_sum_polys.push(v.clone()); + } + } + let mut new_polys = PolyExpr::Mul(no_sum_polys); + + for poly in sum_polys.iter() { + if let PolyExpr::Sum(poly_vec) = poly { + new_polys = match new_polys { + Expr::Mul(v) => { + let polys: Vec> = poly_vec + .iter() + .map(|e| { + let mut v = v.clone(); + v.push(e.clone()); + Expr::Mul(v) + }) + .collect(); + PolyExpr::Sum(polys) + } + Expr::Sum(v_vec) => { + let polys: Vec>> = v_vec + .iter() + .map(|v| { + poly_vec + .iter() + .map(|e| Expr::Mul(vec![v.clone(), e.clone()])) + .collect() + }) + .collect(); + PolyExpr::Sum(polys.concat()) + } + _ => panic!("invalid expr"), + }; + } + } + + new_polys +} \ No newline at end of file From 511473f63daa9276870116c823b9fb92a92096d2 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Mon, 24 Jun 2024 18:13:09 +0800 Subject: [PATCH 09/24] ccs: minor --- src/ccs/backend/ccs.rs | 2 +- src/ccs/backend/sonobe.rs | 6 ++---- src/ccs/compiler/mod.rs | 6 +----- src/ccs/ir/mod.rs | 4 ++-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/ccs/backend/ccs.rs b/src/ccs/backend/ccs.rs index f41b917d..bb670f5f 100644 --- a/src/ccs/backend/ccs.rs +++ b/src/ccs/backend/ccs.rs @@ -79,7 +79,7 @@ impl + Hash> ChiquitoCCSCircuit { assert_eq!(coeffs_chunks.len(), selectors.len()); for (coeffs, (selector, _)) in coeffs_chunks.iter().zip(selectors.iter()) { - // one row in on matrix + // one row in a matrix let mut values: Vec<(usize, usize, F)> = Vec::new(); for (value, signal_id, next) in coeffs.iter() { let col = if *signal_id == 0 { diff --git a/src/ccs/backend/sonobe.rs b/src/ccs/backend/sonobe.rs index 995b8d76..dba9fe15 100644 --- a/src/ccs/backend/sonobe.rs +++ b/src/ccs/backend/sonobe.rs @@ -1,4 +1,3 @@ -// use ark_bn254::Fr as FFr; use ark_ff::PrimeField; use ark_std::log2; use folding_schemes::{ccs::CCS, utils::vec::dense_matrix_to_sparse}; @@ -43,12 +42,11 @@ impl CCSCircuit { impl Z { pub fn convert_to_sonobe_inputs(&self, f: fn(&F) -> Fr) -> Vec { - let values = [ + [ vec![F::from(1u64)], self.assignments.clone(), self.public_inputs.clone(), ] - .concat(); - values.iter().map(f).collect() + .concat().iter().map(f).collect() } } diff --git a/src/ccs/compiler/mod.rs b/src/ccs/compiler/mod.rs index 983538e2..2c78d84f 100644 --- a/src/ccs/compiler/mod.rs +++ b/src/ccs/compiler/mod.rs @@ -224,11 +224,7 @@ fn compile_matrix_coeffs(unit: &mut CompilationUnit) { // one step let coeffs_one_step = polys .iter() - .map(|poly| { - // one poly - let poly = poly.expr.factor_expr(); - poly.poly_to_coeffs() - }) + .map(|poly| poly.expr.factor_expr().poly_to_coeffs()) .collect(); coeffs.insert(*step_id, coeffs_one_step); } diff --git a/src/ccs/ir/mod.rs b/src/ccs/ir/mod.rs index 708131be..c544372f 100644 --- a/src/ccs/ir/mod.rs +++ b/src/ccs/ir/mod.rs @@ -81,7 +81,7 @@ impl PolyExpr { for e in v.iter() { let matrix = e.poly_to_coeffs(); if matrix.len() != 1 { - panic!("invalid poly expr at poly_to_coeffs with PolyExpr::Mul"); + panic!("[poly_to_coeffs]invalid poly expr with PolyExpr::Mul"); } for m in matrix[0].iter() { matrics.push(m.clone()); @@ -89,7 +89,7 @@ impl PolyExpr { } vec![matrics] } - _ => panic!("invalid poly expr at poly_to_coeffs"), + _ => panic!("[poly_to_coeffs]invalid poly expr"), }; matrics } From b53527d239fc123800c9d7ab43b582e898042dc1 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Tue, 2 Jul 2024 19:05:48 +0800 Subject: [PATCH 10/24] ccs: improve fr_convert function --- examples/fibonacci_ccs.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/examples/fibonacci_ccs.rs b/examples/fibonacci_ccs.rs index 4992174a..9e9e8a6b 100644 --- a/examples/fibonacci_ccs.rs +++ b/examples/fibonacci_ccs.rs @@ -142,18 +142,12 @@ fn main() { } fn fr_convert(r: &Fr) -> FFr { - let mut array1 = [0u8; 8]; - array1.copy_from_slice(&r.to_repr().as_ref()[0..8]); - let mut array2 = [0u8; 8]; - array2.copy_from_slice(&r.to_repr().as_ref()[8..16]); - let mut array3 = [0u8; 8]; - array3.copy_from_slice(&r.to_repr().as_ref()[16..24]); - let mut array4 = [0u8; 8]; - array4.copy_from_slice(&r.to_repr().as_ref()[24..32]); - FFr::from(BigInt::new([ - u64::from_le_bytes(array1), - u64::from_le_bytes(array2), - u64::from_le_bytes(array3), - u64::from_le_bytes(array4), - ])) + + let converted = (0..3).map(|i|{ + let mut values = [0u8; 8]; + values.copy_from_slice(&r.to_repr().as_ref()[i * 8..i * 8 + 8]); + u64::from_le_bytes(values) + }).collect::>().try_into().unwrap(); + + FFr::from(BigInt::new(converted)) } From 2c576576f2d70f1e9190c80b1a88ac4cf88f9389 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Mon, 8 Jul 2024 14:41:22 +0800 Subject: [PATCH 11/24] ccs: add sonobe hypernova examples & bugfix --- Cargo.toml | 3 +- examples/fibonacci_ccs.rs | 95 +++++++++++++++++++++++++++++++++------ src/ccs/ir/circuit.rs | 2 +- src/ccs/ir/mod.rs | 2 +- 4 files changed, 84 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 57abd130..3127de30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,7 @@ regex = "1" ark-ff = "^0.4.0" ark-std = "^0.4.0" ark-bn254 = "0.4.0" -folding-schemes = { git = "https://github.com/privacy-scaling-explorations/sonobe.git", commit = "ed1faee6bfdf9eb484a0b5bc6196896cc250abed"} - +folding-schemes = { git = "https://github.com/privacy-scaling-explorations/sonobe.git", commit = "fd942bda713082239738c97c02a1e8ad02ecdfd5"} [dev-dependencies] rand_chacha = "0.3" diff --git a/examples/fibonacci_ccs.rs b/examples/fibonacci_ccs.rs index 9e9e8a6b..9c9cb81b 100644 --- a/examples/fibonacci_ccs.rs +++ b/examples/fibonacci_ccs.rs @@ -1,4 +1,7 @@ use std::hash::Hash; +use ark_bn254::{Fr as FFr, G1Projective as Projective}; +use ark_ff::BigInt; +use halo2_proofs::halo2curves::{bn256::Fr, ff::PrimeField}; use chiquito::{ ccs::{ @@ -10,6 +13,7 @@ use chiquito::{ step_selector::SimpleStepSelectorBuilder, }, ir::{assignments::AssignmentGenerator, circuit::Circuit}, + }, field::Field, frontend::dsl::circuit, @@ -17,6 +21,16 @@ use chiquito::{ sbpir::SBPIR, }; +use folding_schemes::{ + ccs::CCS, + commitment::{pedersen::Pedersen, CommitmentScheme}, + folding::hypernova::nimfs::NIMFS, + transcript::{ + poseidon::{poseidon_canonical_config, PoseidonTranscript}, + Transcript, + }, +}; + // the main circuit function: returns the compiled IR of a Chiquito circuit // Generic types F, (), (u64, 64) stand for: // 1. type that implements a field trait @@ -112,13 +126,6 @@ fn fibo_circuit_ccs + Hash>() -> FiboReturn { (compiled.0, compiled.1, fibo) } -// After compiling Chiquito AST to an IR, it is further parsed by a Chiquito Halo2 backend and -// integrated into a Halo2 circuit, which is done by the boilerplate code below. - -use ark_bn254::Fr as FFr; -use ark_ff::BigInt; -use halo2_proofs::halo2curves::{bn256::Fr, ff::PrimeField}; - fn main() { let (chiquito, wit_gen, _) = fibo_circuit_ccs::(); let compiled = chiquito2CCS(chiquito); @@ -139,15 +146,75 @@ fn main() { let result = ccs.check_relation(&inputs); println!("sonobe fibonacci = {:?}", result); + + run_hypernova(ccs, inputs); } -fn fr_convert(r: &Fr) -> FFr { +fn run_hypernova(ccs: CCS, z: Vec) { + use ark_ff::PrimeField; + + let mut rng = ark_std::test_rng(); + let n: usize = 256; + // setup params + let (params, _) = Pedersen::::setup(&mut rng, n).unwrap(); + + let (running_instance, running_wit) = ccs.to_lcccs(&mut rng, ¶ms, &z).unwrap(); + let res = running_instance.check_relation(¶ms, &ccs, &running_wit); + println!("sonobe fibonacci lcccs check_relation = {:?}", res); + + let (new_instance, new_wit) = ccs.to_cccs(&mut rng, ¶ms, &z).unwrap(); + let res = new_instance.check_relation(¶ms, &ccs, &new_wit); + println!("sonobe fibonacci cccs check_relation = {:?}", res); + + // Prover's transcript + let poseidon_config = poseidon_canonical_config::(); + let mut transcript_p: PoseidonTranscript = + PoseidonTranscript::::new(&poseidon_config); + transcript_p.absorb(&FFr::from_le_bytes_mod_order(b"init init")); + + let (proof, folded_lcccs, folded_witness) = + NIMFS::>::prove( + &mut transcript_p, + &ccs, + &[running_instance.clone()], + &[new_instance.clone()], + &[running_wit], + &[new_wit], + ) + .unwrap(); + + // Verifier's transcript + let mut transcript_v: PoseidonTranscript = + PoseidonTranscript::::new(&poseidon_config); + transcript_v.absorb(&FFr::from_le_bytes_mod_order(b"init init")); + + let folded_lcccs_v = NIMFS::>::verify( + &mut transcript_v, + &ccs, + &[running_instance.clone()], + &[new_instance.clone()], + proof, + ) + .unwrap(); + println!("sonobe hypernova verify...ok"); + + assert_eq!(folded_lcccs, folded_lcccs_v); + let res = folded_lcccs.check_relation(¶ms, &ccs, &folded_witness); + println!("sonobe fibonacci folded lcccs check_relation = {:?}", res); + +} - let converted = (0..3).map(|i|{ - let mut values = [0u8; 8]; - values.copy_from_slice(&r.to_repr().as_ref()[i * 8..i * 8 + 8]); - u64::from_le_bytes(values) - }).collect::>().try_into().unwrap(); + +fn fr_convert(r: &Fr) -> FFr { + let converted = (0..4) + .map(|i| { + let mut values = [0u8; 8]; + values.copy_from_slice(&r.to_repr().as_ref()[i * 8..i * 8 + 8]); + u64::from_le_bytes(values) + }) + .collect::>() + .try_into() + .unwrap(); FFr::from(BigInt::new(converted)) -} +} \ No newline at end of file diff --git a/src/ccs/ir/circuit.rs b/src/ccs/ir/circuit.rs index 13e301bd..19b7e443 100644 --- a/src/ccs/ir/circuit.rs +++ b/src/ccs/ir/circuit.rs @@ -64,7 +64,7 @@ impl + Hash> Circuit { for selectors in matrix_selectors.iter() { d = d.max(selectors.len()); for (idx, _) in selectors.iter() { - t = t.max(*idx); + t = t.max(*idx + 1); } } self.t = t; diff --git a/src/ccs/ir/mod.rs b/src/ccs/ir/mod.rs index c544372f..a6f4584b 100644 --- a/src/ccs/ir/mod.rs +++ b/src/ccs/ir/mod.rs @@ -173,4 +173,4 @@ fn factor_mul(polys: Vec>) -> PolyExpr { } new_polys -} \ No newline at end of file +} From 0fd2f9c487af96c8ac48c0863ba8aefe41b61a65 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Mon, 8 Jul 2024 14:46:58 +0800 Subject: [PATCH 12/24] ccs: minor --- examples/fibonacci_ccs.rs | 9 +++------ src/ccs/backend/sonobe.rs | 5 ++++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/fibonacci_ccs.rs b/examples/fibonacci_ccs.rs index 9c9cb81b..4030f616 100644 --- a/examples/fibonacci_ccs.rs +++ b/examples/fibonacci_ccs.rs @@ -1,7 +1,7 @@ -use std::hash::Hash; use ark_bn254::{Fr as FFr, G1Projective as Projective}; use ark_ff::BigInt; use halo2_proofs::halo2curves::{bn256::Fr, ff::PrimeField}; +use std::hash::Hash; use chiquito::{ ccs::{ @@ -13,7 +13,6 @@ use chiquito::{ step_selector::SimpleStepSelectorBuilder, }, ir::{assignments::AssignmentGenerator, circuit::Circuit}, - }, field::Field, frontend::dsl::circuit, @@ -154,7 +153,7 @@ fn run_hypernova(ccs: CCS, z: Vec) { use ark_ff::PrimeField; let mut rng = ark_std::test_rng(); - let n: usize = 256; + let n: usize = 64; // setup params let (params, _) = Pedersen::::setup(&mut rng, n).unwrap(); @@ -201,10 +200,8 @@ fn run_hypernova(ccs: CCS, z: Vec) { assert_eq!(folded_lcccs, folded_lcccs_v); let res = folded_lcccs.check_relation(¶ms, &ccs, &folded_witness); println!("sonobe fibonacci folded lcccs check_relation = {:?}", res); - } - fn fr_convert(r: &Fr) -> FFr { let converted = (0..4) .map(|i| { @@ -217,4 +214,4 @@ fn fr_convert(r: &Fr) -> FFr { .unwrap(); FFr::from(BigInt::new(converted)) -} \ No newline at end of file +} diff --git a/src/ccs/backend/sonobe.rs b/src/ccs/backend/sonobe.rs index dba9fe15..52cbf4db 100644 --- a/src/ccs/backend/sonobe.rs +++ b/src/ccs/backend/sonobe.rs @@ -47,6 +47,9 @@ impl Z { self.assignments.clone(), self.public_inputs.clone(), ] - .concat().iter().map(f).collect() + .concat() + .iter() + .map(f) + .collect() } } From ea2496e86c0e561be87b57bcdd509a8f03a56473 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Wed, 17 Jul 2024 17:21:20 +0800 Subject: [PATCH 13/24] ccs: fix --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3127de30..8e9a4bdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ regex = "1" ark-ff = "^0.4.0" ark-std = "^0.4.0" ark-bn254 = "0.4.0" -folding-schemes = { git = "https://github.com/privacy-scaling-explorations/sonobe.git", commit = "fd942bda713082239738c97c02a1e8ad02ecdfd5"} +folding-schemes = { git = "https://github.com/privacy-scaling-explorations/sonobe.git", rev = "bda8ad6ce16b2bfdaf93347e7e54d0c7800f16ab"} [dev-dependencies] rand_chacha = "0.3" From 18a7d5acffd988f85aba5b334593125084d694b2 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Thu, 22 Aug 2024 14:58:18 +0800 Subject: [PATCH 14/24] ccs: move ccs circuit to ir --- examples/fibonacci_ccs.rs | 23 +- src/ccs/backend/ccs.rs | 551 -------------------------------------- src/ccs/backend/mod.rs | 1 - src/ccs/backend/sonobe.rs | 8 +- src/ccs/ir/circuit.rs | 436 ++++++++++++++++++++++++++++-- 5 files changed, 434 insertions(+), 585 deletions(-) delete mode 100644 src/ccs/backend/ccs.rs diff --git a/examples/fibonacci_ccs.rs b/examples/fibonacci_ccs.rs index 4030f616..a2ef8171 100644 --- a/examples/fibonacci_ccs.rs +++ b/examples/fibonacci_ccs.rs @@ -5,7 +5,6 @@ use std::hash::Hash; use chiquito::{ ccs::{ - backend::ccs::{chiquito2CCS, ChiquitoCCSCircuit}, compiler::{ cell_manager::SingleRowCellManager, compile, // input for constructing the compiler @@ -126,21 +125,21 @@ fn fibo_circuit_ccs + Hash>() -> FiboReturn { } fn main() { - let (chiquito, wit_gen, _) = fibo_circuit_ccs::(); - let compiled = chiquito2CCS(chiquito); + let (mut chiquito, wit_gen, _) = fibo_circuit_ccs::(); + let witness = wit_gen.map(|g| g.generate(())); - let circuit = ChiquitoCCSCircuit::new(compiled, wit_gen.map(|g| g.generate(()))); - - let (ccs, z) = circuit.configure(); - let result = ccs.is_satisfied(&z); + let z = chiquito.generate(witness); + let compiled = chiquito.ccs; + let result = compiled.is_satisfied(&z); println!("fibonacci {:#?}", result); - let (chiquito, wit_gen, _) = fibo_circuit_ccs::(); - let compiled = chiquito2CCS(chiquito); - let circuit = ChiquitoCCSCircuit::new(compiled, wit_gen.map(|g| g.generate(()))); - let (circuit, z) = circuit.configure(); + let (mut chiquito, wit_gen, _) = fibo_circuit_ccs::(); + let witness = wit_gen.map(|g| g.generate(())); + + let z = chiquito.generate(witness); + let compiled = chiquito.ccs; - let ccs = circuit.convert_to_sonobe_circuit(fr_convert); + let ccs = compiled.convert_to_sonobe_circuit(fr_convert); let inputs = z.convert_to_sonobe_inputs(fr_convert); let result = ccs.check_relation(&inputs); diff --git a/src/ccs/backend/ccs.rs b/src/ccs/backend/ccs.rs deleted file mode 100644 index bb670f5f..00000000 --- a/src/ccs/backend/ccs.rs +++ /dev/null @@ -1,551 +0,0 @@ -use std::{collections::HashMap, hash::Hash}; - -use crate::{ - ccs::ir::{ - assignments::Assignments, - circuit::{Circuit, SelectorsOffsetAndCoeff}, - CoeffsForProds, - }, - field::Field, - util::UUID, -}; - -// ((step_idx, step_UUID), assignment_offset) -pub type AssignmentOffsets = HashMap<(usize, u128), usize>; - -#[allow(non_snake_case)] -pub fn chiquito2CCS + Hash>(circuit: Circuit) -> ChiquitoCCS { - ChiquitoCCS::new(circuit) -} - -pub struct ChiquitoCCS> { - pub circuit: Circuit, - pub ir_id: UUID, -} - -impl + Hash> ChiquitoCCS { - pub fn new(circuit: Circuit) -> ChiquitoCCS { - let ir_id = circuit.ast_id; - Self { circuit, ir_id } - } -} - -pub struct ChiquitoCCSCircuit> { - pub compiled: ChiquitoCCS, - pub witness: Option>, -} - -impl + Hash> ChiquitoCCSCircuit { - pub fn new(compiled: ChiquitoCCS, witness: Option>) -> Self { - Self { compiled, witness } - } - - pub fn instance(&self) -> HashMap<(usize, UUID), F> { - if !self.compiled.circuit.exposed.is_empty() { - if let Some(witness) = &self.witness { - return self.compiled.circuit.instance(witness); - } - } - HashMap::new() - } - - fn export_matrix_vec( - &self, - n: usize, - num: usize, - assign_pos: &AssignmentOffsets, - ) -> (Vec>, usize) { - let mut matrix_vec = vec![Matrix::new(n, self.num_poly()); num]; - let mut row = 0; - - if let Some(steps_id) = self.witness.as_ref() { - let step_num = steps_id.len(); - for (step_idx, (step_id, _)) in steps_id.iter().enumerate() { - for coeffs_one_poly in self - .compiled - .circuit - .matrix_coeffs_and_offsets - .get(step_id) - .unwrap() - .iter() - { - if is_last_step_with_next_signal(coeffs_one_poly, step_num, step_idx) { - continue; - } - - for (coeffs_chunks, index) in coeffs_one_poly.iter() { - assert!(*index <= self.compiled.circuit.selectors.len()); - let selectors = self.compiled.circuit.selectors[*index].clone(); - assert_eq!(coeffs_chunks.len(), selectors.len()); - - for (coeffs, (selector, _)) in coeffs_chunks.iter().zip(selectors.iter()) { - // one row in a matrix - let mut values: Vec<(usize, usize, F)> = Vec::new(); - for (value, signal_id, next) in coeffs.iter() { - let col = if *signal_id == 0 { - assign_pos.get(&(0, 0)) - } else { - let idx = if *next { step_idx + 1 } else { step_idx }; - assign_pos.get(&(idx, *signal_id)) - }; - match col { - None => continue, - Some(col) => values.push((row, *col, *value)), - } - } - matrix_vec[*selector].write(&values); - } - } - row += 1; - } - } - }; - - for matrix in matrix_vec.iter_mut() { - matrix.reduce_rows(row); - } - (matrix_vec, row) - } - - fn num_poly(&self) -> usize { - self.witness - .as_ref() - .map(|steps_idx| { - steps_idx - .iter() - .map(|(idx, _)| { - self.compiled - .circuit - .matrix_coeffs_and_offsets - .get(idx) - .unwrap() - .len() - }) - .sum() - }) - .unwrap() - } - - fn assignments_coeff_offset(&self) -> (AssignmentOffsets, usize, usize) { - let mut public_pos = HashMap::new(); - for (offset, (step_idx, signal_uuid)) in self.compiled.circuit.exposed.iter().enumerate() { - public_pos.insert((*step_idx, *signal_uuid), offset); - } - - let mut witness_pos = HashMap::new(); - let mut offset = 0; - witness_pos.insert((0, 0), offset); - offset += 1; - if let Some(assignments) = self.witness.as_ref() { - for (step_idx, (step_id, _)) in assignments.iter().enumerate() { - let signal_uuids = self.compiled.circuit.witness.get(step_id).unwrap(); - for id in signal_uuids.iter() { - let exist = public_pos.get(&(step_idx, *id)); - if exist.is_none() { - witness_pos.insert((step_idx, *id), offset); - offset += 1; - } - } - } - } - - for ((step_idx, id), v) in public_pos.iter() { - witness_pos.insert((*step_idx, *id), v + offset); - offset += 1; - } - (witness_pos, offset, public_pos.len()) - } -} - -fn is_last_step_with_next_signal( - coeffs_one_poly: &[(CoeffsForProds, usize)], - step_num: usize, - step_idx: usize, -) -> bool { - if step_idx == step_num - 1 { - for (coeffs_for_prods, _) in coeffs_one_poly.iter() { - for (_, _, next) in coeffs_for_prods.concat().iter() { - if *next { - return true; - } - } - } - } - false -} - -impl + Hash> ChiquitoCCSCircuit { - pub fn configure(&self) -> (CCSCircuit, Z) { - let (ccs, assign_pos) = CCSCircuit::from_circuit(self); - let mut z: Z = Z::new(ccs.n, ccs.l); - - z.write( - &self.instance(), - self.witness.as_deref().unwrap(), - &assign_pos, - ); - (ccs, z) - } -} - -#[derive(Default)] -pub struct CCSCircuit { - pub n: usize, - pub m: usize, - pub l: usize, - pub t: usize, - pub q: usize, - pub d: usize, - pub matrics: Vec>, - pub selectors: Vec>, - pub constants: Vec, -} - -impl + Hash> CCSCircuit { - pub fn new(n: usize, m: usize, t: usize, q: usize, l: usize, d: usize) -> Self { - assert!(n > l); - - let matrics = Vec::new(); - let selectors = (0..q).map(|_| Vec::new()).collect(); - let constants = (0..q).map(|_| F::ZERO).collect(); - - Self { - n, - m, - l, - t, - q, - d, - matrics, - selectors, - constants, - } - } - - pub fn from_circuit(circuit: &ChiquitoCCSCircuit) -> (Self, AssignmentOffsets) { - let selectors = circuit.compiled.circuit.selectors.clone(); - let constants = circuit.compiled.circuit.constants.clone(); - - let (assign_pos, n, l) = circuit.assignments_coeff_offset(); - - let matrix_num = calc_matrix_num(&selectors); - let (matrix_vec, m) = circuit.export_matrix_vec(n, matrix_num, &assign_pos); - - ( - Self { - n, - m, - l, - t: circuit.compiled.circuit.t, - q: circuit.compiled.circuit.q, - d: circuit.compiled.circuit.d, - matrics: matrix_vec, - selectors, - constants, - }, - assign_pos, - ) - } - - pub fn public_num(&self) -> usize { - self.l - } - - pub fn witness_num(&self) -> usize { - self.n - self.l - 1 - } - - pub fn write( - &mut self, - matrics: &[Vec<(usize, usize, F)>], - selectors: &[Vec<(usize, F)>], - constants: &[F], - ) { - self.write_constants(constants); - self.write_selectors_and_degree(selectors); - self.write_matrics(matrics); - } - - pub fn write_constants(&mut self, constants: &[F]) { - assert_eq!(constants.len(), self.q); - self.constants = constants.to_owned().clone(); - } - - pub fn write_selectors_and_degree(&mut self, selectors: &[Vec<(usize, F)>]) { - let mut degree = 0; - assert_eq!(selectors.len(), self.q); - for selector in selectors.iter() { - for &(s, _) in selector { - assert!(s < self.t) - } - degree = degree.max(selector.len()) - } - self.selectors = selectors.to_owned().clone(); - self.d = degree; - } - - fn write_matrics(&mut self, matrics: &[Vec<(usize, usize, F)>]) { - assert_eq!(matrics.len(), self.t); - - self.matrics = matrics - .iter() - .map(|cells| { - for &cell in cells.iter() { - assert!(cell.0 < self.m); - assert!(cell.1 < self.n); - } - - let mut matrix = Matrix::new(self.n, self.m); - matrix.write(cells); - matrix - }) - .collect(); - } - - pub fn is_satisfied(&self, z: &Z) -> bool { - assert_eq!(self.selectors.len(), self.q); - assert_eq!(self.constants.len(), self.q); - - let mut witnesses = z.assignments.clone(); - let mut inputs = z.public_inputs.clone(); - - let mut values = vec![F::ONE]; - values.append(&mut inputs); - values.append(&mut witnesses); - - let prod_vec: Vec> = self - .constants - .iter() - .zip(self.selectors.iter()) - .map(|(&c, selector)| { - let hadamard_prod_vec: Vec> = selector - .iter() - .map(|&(s, _)| { - assert!(s < self.matrics.len()); - self.matrics[s].matrix_vector_product(&values) - }) - .collect(); - hadamard_prod_vec - .iter() - .fold(vec![c; self.m], |acc, vec| hadamard_product(&acc, vec)) - }) - .collect(); - let sum_vec = vec_add(&prod_vec); - - assert_eq!(sum_vec.len(), self.m); - - for &sum in sum_vec.iter() { - if sum != F::ZERO { - return false; - } - } - - true - } -} - -pub fn hadamard_product + Hash>(vec1: &[F], vec2: &[F]) -> Vec { - assert_eq!(vec1.len(), vec2.len()); - vec1.iter() - .zip(vec2.iter()) - .map(|(&v1, &v2)| v1 * v2) - .collect() -} - -pub fn vec_add + Hash>(vec: &[Vec]) -> Vec { - assert!(vec.len() > 1); - vec.iter().fold(vec![F::ZERO; vec[0].len()], |acc, vec| { - acc.iter().zip(vec.iter()).map(|(&a, &v)| a + v).collect() - }) -} - -fn calc_matrix_num(selectors: &SelectorsOffsetAndCoeff) -> usize { - let mut matrix_num = 0; - for selector in selectors.iter() { - for (idx, _) in selector.iter() { - matrix_num = matrix_num.max(*idx + 1); - } - } - matrix_num -} - -#[derive(Debug, Clone)] -pub struct Matrix { - n: usize, - m: usize, - values: Vec>, -} - -impl Matrix { - pub fn new(n: usize, m: usize) -> Self { - Self { - n, - m, - values: vec![vec![F::ZERO; n]; m], - } - } - - pub fn write(&mut self, values: &[(usize, usize, F)]) { - for &(row, col, value) in values.iter() { - assert!(row < self.m); - assert!(col < self.n); - self.values[row][col] = value; - } - } - - pub fn get(&self, row: usize, col: usize) -> F { - assert!(row < self.m); - assert!(col < self.n); - self.values[row][col] - } - - pub fn reduce_rows(&mut self, m: usize) { - if m < self.m { - self.values = self.values[0..m].to_owned(); - self.m = m; - } - } - - pub fn size(&self) -> (usize, usize) { - (self.m, self.n) - } - - pub fn values(&self) -> Vec> { - self.values.clone() - } -} - -impl + Hash> Matrix { - pub fn matrix_vector_product(&self, z: &[F]) -> Vec { - (0..self.values.len()) - .map(|i| self.hadamard_product_and_sum(i, z)) - .collect() - } - - fn hadamard_product_and_sum(&self, index: usize, z: &[F]) -> F { - assert!(index < self.values.len()); - assert_eq!(self.values[index].len(), z.len()); - hadamard_product(&self.values[index], z).iter().sum() - } -} - -// input vector z = [1, x, w] -pub struct Z> { - pub n: usize, - pub l: usize, - pub assignments: Vec, - pub public_inputs: Vec, -} - -impl + Hash> Z { - pub fn new(n: usize, l: usize) -> Self { - assert!(n > l); - Self { - n, - l, - assignments: Default::default(), - public_inputs: Default::default(), - } - } - - pub fn write( - &mut self, - inputs: &HashMap<(usize, UUID), F>, - witnesses: &[(UUID, HashMap)], - assign_pos: &AssignmentOffsets, - ) { - assert_eq!(inputs.len(), self.l); - assert_eq!(assign_pos.len(), self.n); - let witness_len = self.n - self.l - 1; - let mut witness_values = vec![F::ZERO; witness_len]; - let mut public_values = vec![F::ZERO; self.l]; - for ((step_idx, signal_id), idx) in assign_pos.iter() { - if *signal_id == 0 { - continue; - } - if *idx < self.n - self.l { - witness_values[*idx - 1] = *witnesses[*step_idx].1.get(signal_id).unwrap(); - } else { - public_values[*idx - witness_len - 1] = - *inputs.get(&(*step_idx, *signal_id)).unwrap(); - } - } - self.assignments = witness_values; - self.public_inputs = public_values; - } - - pub fn write_with_values(&mut self, inputs: &[F], witnesses: &[F]) { - assert_eq!(inputs.len(), self.l); - assert_eq!(witnesses.len(), self.n - self.l - 1); - - self.public_inputs = inputs.to_owned().clone(); - self.assignments = witnesses.to_owned().clone(); - } -} - -#[cfg(test)] -mod tests { - use std::vec; - - use super::*; - use halo2_proofs::halo2curves::bn256::Fr; - - #[test] - fn test_ccs() { - let n = 7; - let m = 4; - let t = 8; - let q = 5; - let l = 3; - - let mut ccs_circuit: CCSCircuit = CCSCircuit::new(n, m, t, q, l, 0); - let m0 = vec![ - (0, 1, Fr::ONE), - (1, 2, Fr::ONE), - (2, 3, Fr::ONE), - (3, 6, Fr::ONE), - ]; - let m1 = vec![ - (0, 1, Fr::ONE), - (1, 2, Fr::ONE), - (2, 4, Fr::ONE), - (3, 6, Fr::ONE), - ]; - let m2 = vec![ - (0, 1, Fr::ONE), - (1, 2, Fr::ONE), - (2, 5, Fr::ONE), - (3, 6, Fr::ONE), - ]; - let m3 = vec![(0, 0, Fr::ONE), (1, 0, Fr::ONE)]; - let m4 = vec![(2, 0, Fr::from(2))]; - let m5 = vec![(2, 0, Fr::from(2))]; - let m6 = vec![ - (0, 0, Fr::ONE.neg()), - (1, 0, Fr::ONE.neg()), - (2, 0, Fr::ONE.neg()), - ]; - let m7 = vec![(0, 0, Fr::ZERO)]; - let matrics = vec![m0, m1, m2, m3, m4, m5, m6, m7]; - - let selectors = vec![ - vec![(3, Fr::ONE), (0, Fr::ONE), (1, Fr::ONE)], - vec![(4, Fr::ONE), (0, Fr::ONE)], - vec![(5, Fr::ONE), (1, Fr::ONE)], - vec![(6, Fr::ONE), (2, Fr::ONE)], - vec![(7, Fr::ONE)], - ]; - let constants: Vec = vec![Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE]; - ccs_circuit.write(&matrics, &selectors, &constants); - - let mut z = Z::new(n, l); - z.write_with_values( - &[Fr::ZERO, Fr::ONE, Fr::from(2)], - &[Fr::from(3), Fr::from(10), Fr::from(43)], - ); - - let result = ccs_circuit.is_satisfied(&z); - - println!("result = {}", result); - } -} diff --git a/src/ccs/backend/mod.rs b/src/ccs/backend/mod.rs index 43dc6b0e..39cf66c2 100644 --- a/src/ccs/backend/mod.rs +++ b/src/ccs/backend/mod.rs @@ -1,2 +1 @@ -pub mod ccs; pub mod sonobe; diff --git a/src/ccs/backend/sonobe.rs b/src/ccs/backend/sonobe.rs index 52cbf4db..0e790b6c 100644 --- a/src/ccs/backend/sonobe.rs +++ b/src/ccs/backend/sonobe.rs @@ -2,13 +2,15 @@ use ark_ff::PrimeField; use ark_std::log2; use folding_schemes::{ccs::CCS, utils::vec::dense_matrix_to_sparse}; -use super::ccs::{CCSCircuit, Z}; -use crate::field::Field; +use crate::{ + ccs::ir::circuit::{CCSCircuit, Z}, + field::Field, +}; impl CCSCircuit { pub fn convert_to_sonobe_circuit(&self, f: fn(&F) -> Fr) -> CCS { let matrics = self - .matrics + .matrices .iter() .map(|matrix| { let values = matrix diff --git a/src/ccs/ir/circuit.rs b/src/ccs/ir/circuit.rs index 19b7e443..8d41d4db 100644 --- a/src/ccs/ir/circuit.rs +++ b/src/ccs/ir/circuit.rs @@ -9,32 +9,286 @@ use crate::{ pub type MatrixCoeffsAndOffset = Vec, usize)>>; pub type SelectorsOffsetAndCoeff = Vec>; + +// ((step_idx, step_UUID), assignment_offset) +pub type AssignmentOffsets = HashMap<(usize, u128), usize>; + +pub fn hadamard_product + Hash>(vec1: &[F], vec2: &[F]) -> Vec { + assert_eq!(vec1.len(), vec2.len()); + vec1.iter() + .zip(vec2.iter()) + .map(|(&v1, &v2)| v1 * v2) + .collect() +} + +pub fn vec_add + Hash>(vec: &[Vec]) -> Vec { + assert!(vec.len() > 1); + vec.iter().fold(vec![F::ZERO; vec[0].len()], |acc, vec| { + acc.iter().zip(vec.iter()).map(|(&a, &v)| a + v).collect() + }) +} + +// input vector z = [1, x, w] +pub struct Z> { + pub n: usize, + pub l: usize, + pub assignments: Vec, + pub public_inputs: Vec, +} + +impl + Hash> Z { + pub fn new(n: usize, l: usize) -> Self { + assert!(n > l); + Self { + n, + l, + assignments: Default::default(), + public_inputs: Default::default(), + } + } + + pub fn write( + &mut self, + inputs: &HashMap<(usize, UUID), F>, + witnesses: &[(UUID, HashMap)], + assign_pos: &AssignmentOffsets, + ) { + assert_eq!(inputs.len(), self.l); + assert_eq!(assign_pos.len(), self.n); + let witness_len = self.n - self.l - 1; + let mut witness_values = vec![F::ZERO; witness_len]; + let mut public_values = vec![F::ZERO; self.l]; + for ((step_idx, signal_id), idx) in assign_pos.iter() { + if *signal_id == 0 { + continue; + } + if *idx < self.n - self.l { + witness_values[*idx - 1] = *witnesses[*step_idx].1.get(signal_id).unwrap(); + } else { + public_values[*idx - witness_len - 1] = + *inputs.get(&(*step_idx, *signal_id)).unwrap(); + } + } + self.assignments = witness_values; + self.public_inputs = public_values; + } + + pub fn write_with_values(&mut self, inputs: &[F], witnesses: &[F]) { + assert_eq!(inputs.len(), self.l); + assert_eq!(witnesses.len(), self.n - self.l - 1); + + self.public_inputs = inputs.to_owned().clone(); + self.assignments = witnesses.to_owned().clone(); + } +} + +#[derive(Debug, Clone)] +pub struct Matrix { + n: usize, + m: usize, + values: Vec>, +} + +impl Matrix { + pub fn new(n: usize, m: usize) -> Self { + Self { + n, + m, + values: vec![vec![F::ZERO; n]; m], + } + } + + pub fn write(&mut self, values: &[(usize, usize, F)]) { + for &(row, col, value) in values.iter() { + assert!(row < self.m); + assert!(col < self.n); + self.values[row][col] = value; + } + } + + pub fn get(&self, row: usize, col: usize) -> F { + assert!(row < self.m); + assert!(col < self.n); + self.values[row][col] + } + + pub fn reduce_rows(&mut self, m: usize) { + if m < self.m { + self.values = self.values[0..m].to_owned(); + self.m = m; + } + } + + pub fn size(&self) -> (usize, usize) { + (self.m, self.n) + } + + pub fn values(&self) -> Vec> { + self.values.clone() + } +} + +impl + Hash> Matrix { + pub fn matrix_vector_product(&self, z: &[F]) -> Vec { + (0..self.values.len()) + .map(|i| self.hadamard_product_and_sum(i, z)) + .collect() + } + + fn hadamard_product_and_sum(&self, index: usize, z: &[F]) -> F { + assert!(index < self.values.len()); + assert_eq!(self.values[index].len(), z.len()); + hadamard_product(&self.values[index], z).iter().sum() + } +} + +#[derive(Debug, Default)] +pub struct CCSCircuit { + pub n: usize, + pub m: usize, + pub l: usize, + pub t: usize, + pub q: usize, + pub d: usize, + pub matrices: Vec>, + pub selectors: SelectorsOffsetAndCoeff, + pub constants: Vec, +} + +impl + Hash> CCSCircuit { + pub fn new() -> Self { + let matrices = Vec::new(); + let selectors = Vec::new(); + let constants = Vec::new(); + + Self { + n: 0, + m: 0, + l: 0, + t: 0, + q: 0, + d: 0, + matrices, + selectors, + constants, + } + } + + pub fn public_num(&self) -> usize { + self.l + } + + pub fn witness_num(&self) -> usize { + self.n - self.l - 1 + } + + pub fn write( + &mut self, + matrics: &[Vec<(usize, usize, F)>], + selectors: &[Vec<(usize, F)>], + constants: &[F], + ) { + self.write_constants(constants); + self.write_selectors_and_degree(selectors); + self.write_matrics(matrics); + } + + pub fn write_constants(&mut self, constants: &[F]) { + assert_eq!(constants.len(), self.q); + self.constants = constants.to_owned().clone(); + } + + pub fn write_selectors_and_degree(&mut self, selectors: &[Vec<(usize, F)>]) { + let mut degree = 0; + assert_eq!(selectors.len(), self.q); + for selector in selectors.iter() { + for &(s, _) in selector { + assert!(s < self.t) + } + degree = degree.max(selector.len()) + } + self.selectors = selectors.to_owned().clone(); + self.d = degree; + } + + fn write_matrics(&mut self, matrics: &[Vec<(usize, usize, F)>]) { + assert_eq!(matrics.len(), self.t); + + self.matrices = matrics + .iter() + .map(|cells| { + for &cell in cells.iter() { + assert!(cell.0 < self.m); + assert!(cell.1 < self.n); + } + + let mut matrix = Matrix::new(self.n, self.m); + matrix.write(cells); + matrix + }) + .collect(); + } + + pub fn is_satisfied(&self, z: &Z) -> bool { + assert_eq!(self.selectors.len(), self.q); + assert_eq!(self.constants.len(), self.q); + + let mut witnesses = z.assignments.clone(); + let mut inputs = z.public_inputs.clone(); + + let mut values = vec![F::ONE]; + values.append(&mut inputs); + values.append(&mut witnesses); + + let prod_vec: Vec> = self + .constants + .iter() + .zip(self.selectors.iter()) + .map(|(&c, selector)| { + let hadamard_prod_vec: Vec> = selector + .iter() + .map(|&(s, _)| { + assert!(s < self.matrices.len()); + self.matrices[s].matrix_vector_product(&values) + }) + .collect(); + hadamard_prod_vec + .iter() + .fold(vec![c; self.m], |acc, vec| hadamard_product(&acc, vec)) + }) + .collect(); + let sum_vec = vec_add(&prod_vec); + + assert_eq!(sum_vec.len(), self.m); + + for &sum in sum_vec.iter() { + if sum != F::ZERO { + return false; + } + } + + true + } +} + #[derive(Debug)] pub struct Circuit { pub ast_id: UUID, pub matrix_coeffs_and_offsets: HashMap>, - pub selectors: SelectorsOffsetAndCoeff, - pub constants: Vec, pub exposed: Vec<(usize, UUID)>, pub witness: HashMap>, - pub d: usize, - pub q: usize, - pub t: usize, + pub ccs: CCSCircuit, } impl + Hash> Circuit { pub fn new(ast_id: UUID) -> Self { Self { - t: 0, - q: 0, - d: 0, matrix_coeffs_and_offsets: HashMap::new(), - selectors: Vec::new(), - constants: Vec::new(), exposed: Vec::new(), witness: HashMap::new(), + ccs: CCSCircuit::new(), ast_id, } } @@ -48,8 +302,8 @@ impl + Hash> Circuit { ) { self.calcu_bounds(&selectors.matrix_selectors); - self.constants = vec![F::ONE; self.q]; - self.selectors = selectors.matrix_selectors.clone(); + self.ccs.constants = vec![F::ONE; self.ccs.q]; + self.ccs.selectors = selectors.matrix_selectors.clone(); self.exposed = exposed.to_owned(); self.witness = witness.clone(); @@ -59,7 +313,7 @@ impl + Hash> Circuit { fn calcu_bounds(&mut self, matrix_selectors: &SelectorsForALLMatrix) { let mut t = 0; let mut d = 0; - self.q = matrix_selectors.len(); + self.ccs.q = matrix_selectors.len(); for selectors in matrix_selectors.iter() { d = d.max(selectors.len()); @@ -67,8 +321,8 @@ impl + Hash> Circuit { t = t.max(*idx + 1); } } - self.t = t; - self.d = d; + self.ccs.t = t; + self.ccs.d = d; } fn construct_matrix_coeffs_and_offsets( @@ -98,11 +352,157 @@ impl + Hash> Circuit { self.matrix_coeffs_and_offsets = matrix_coeffs_and_offsets; } - pub fn instance(&self, assignments: &Assignments) -> HashMap<(usize, UUID), F> { + pub fn instance(&self, assignments: &Option>) -> HashMap<(usize, UUID), F> { let mut exposes = HashMap::new(); - for (step_idx, id) in self.exposed.iter() { - exposes.insert((*step_idx, *id), assignments.get(*step_idx, *id)); + if !self.exposed.is_empty() { + for (step_idx, id) in self.exposed.iter() { + if let Some(witness) = assignments { + exposes.insert((*step_idx, *id), witness.get(*step_idx, *id)); + } + } } exposes } + + pub fn generate(&mut self, assignments: Option>) -> Z { + let (assign_pos, n, l) = self.assignments_coeff_offset(&assignments); + let matrix_num = calc_matrix_num(&self.ccs.selectors); + + let (matrices, m) = self.export_matrix_vec(&assignments, n, matrix_num, &assign_pos); + + self.ccs.n = n; + self.ccs.l = l; + self.ccs.m = m; + self.ccs.matrices = matrices; + + let mut z: Z = Z::new(n, l); + z.write( + &self.instance(&assignments), + assignments.as_deref().unwrap(), + &assign_pos, + ); + + z + } + + fn assignments_coeff_offset( + &self, + witness: &Option>, + ) -> (AssignmentOffsets, usize, usize) { + let mut public_pos = HashMap::new(); + for (offset, (step_idx, signal_uuid)) in self.exposed.iter().enumerate() { + public_pos.insert((*step_idx, *signal_uuid), offset); + } + + let mut witness_pos = HashMap::new(); + let mut offset = 0; + witness_pos.insert((0, 0), offset); + offset += 1; + if let Some(assignments) = witness.as_ref() { + for (step_idx, (step_id, _)) in assignments.iter().enumerate() { + let signal_uuids = self.witness.get(step_id).unwrap(); + for id in signal_uuids.iter() { + let exist = public_pos.get(&(step_idx, *id)); + if exist.is_none() { + witness_pos.insert((step_idx, *id), offset); + offset += 1; + } + } + } + } + + for ((step_idx, id), v) in public_pos.iter() { + witness_pos.insert((*step_idx, *id), v + offset); + offset += 1; + } + (witness_pos, offset, public_pos.len()) + } + + fn export_matrix_vec( + &self, + witness: &Option>, + n: usize, + num: usize, + assign_pos: &AssignmentOffsets, + ) -> (Vec>, usize) { + let num_poly = witness + .as_ref() + .map(|steps_idx| { + steps_idx + .iter() + .map(|(idx, _)| self.matrix_coeffs_and_offsets.get(idx).unwrap().len()) + .sum() + }) + .unwrap(); + let mut matrices = vec![Matrix::new(n, num_poly); num]; + let mut row = 0; + + if let Some(steps_id) = witness.as_ref() { + let step_num = steps_id.len(); + for (step_idx, (step_id, _)) in steps_id.iter().enumerate() { + for coeffs_one_poly in self.matrix_coeffs_and_offsets.get(step_id).unwrap().iter() { + if is_last_step_with_next_signal(coeffs_one_poly, step_num, step_idx) { + continue; + } + + for (coeffs_chunks, index) in coeffs_one_poly.iter() { + assert!(*index <= self.ccs.selectors.len()); + let selectors = self.ccs.selectors[*index].clone(); + assert_eq!(coeffs_chunks.len(), selectors.len()); + + for (coeffs, (selector, _)) in coeffs_chunks.iter().zip(selectors.iter()) { + // one row in a matrix + let mut values: Vec<(usize, usize, F)> = Vec::new(); + for (value, signal_id, next) in coeffs.iter() { + let col = if *signal_id == 0 { + assign_pos.get(&(0, 0)) + } else { + let idx = if *next { step_idx + 1 } else { step_idx }; + assign_pos.get(&(idx, *signal_id)) + }; + match col { + None => continue, + Some(col) => values.push((row, *col, *value)), + } + } + matrices[*selector].write(&values); + } + } + row += 1; + } + } + }; + + for matrix in matrices.iter_mut() { + matrix.reduce_rows(row); + } + (matrices, row) + } +} + +fn calc_matrix_num(selectors: &SelectorsOffsetAndCoeff) -> usize { + let mut matrix_num = 0; + for selector in selectors.iter() { + for (idx, _) in selector.iter() { + matrix_num = matrix_num.max(*idx + 1); + } + } + matrix_num +} + +fn is_last_step_with_next_signal( + coeffs_one_poly: &[(CoeffsForProds, usize)], + step_num: usize, + step_idx: usize, +) -> bool { + if step_idx == step_num - 1 { + for (coeffs_for_prods, _) in coeffs_one_poly.iter() { + for (_, _, next) in coeffs_for_prods.concat().iter() { + if *next { + return true; + } + } + } + } + false } From 9fc3569db967973959df0c47e4b5402ba2828c51 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Thu, 22 Aug 2024 15:02:08 +0800 Subject: [PATCH 15/24] ccs: move ccs circuit to ir --- examples/fibonacci_ccs.rs | 7 ------- src/ccs/backend/sonobe.rs | 4 ++-- src/ccs/ir/circuit.rs | 10 +++++----- src/ccs/ir/mod.rs | 10 +++++----- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/examples/fibonacci_ccs.rs b/examples/fibonacci_ccs.rs index a2ef8171..c9e8e95d 100644 --- a/examples/fibonacci_ccs.rs +++ b/examples/fibonacci_ccs.rs @@ -38,13 +38,6 @@ use folding_schemes::{ type FiboReturn = (Circuit, Option>, SBPIR); fn fibo_circuit_ccs + Hash>() -> FiboReturn { - // PLONKish table for the Fibonacci circuit: - // | a | b | c | - // | 1 | 1 | 2 | - // | 1 | 2 | 3 | - // | 2 | 3 | 5 | - // | 3 | 5 | 8 | - // ... use chiquito::frontend::dsl::cb::*; // functions for constraint building diff --git a/src/ccs/backend/sonobe.rs b/src/ccs/backend/sonobe.rs index 0e790b6c..bbd250e4 100644 --- a/src/ccs/backend/sonobe.rs +++ b/src/ccs/backend/sonobe.rs @@ -9,7 +9,7 @@ use crate::{ impl CCSCircuit { pub fn convert_to_sonobe_circuit(&self, f: fn(&F) -> Fr) -> CCS { - let matrics = self + let matrices = self .matrices .iter() .map(|matrix| { @@ -35,7 +35,7 @@ impl CCSCircuit { d: self.d, s: log2(self.m) as usize, s_prime: log2(self.n) as usize, - M: matrics, + M: matrices, S: selectors, c: (0..self.q).map(|_| Fr::from(1u64)).collect(), } diff --git a/src/ccs/ir/circuit.rs b/src/ccs/ir/circuit.rs index 8d41d4db..248bf39b 100644 --- a/src/ccs/ir/circuit.rs +++ b/src/ccs/ir/circuit.rs @@ -184,13 +184,13 @@ impl + Hash> CCSCircuit { pub fn write( &mut self, - matrics: &[Vec<(usize, usize, F)>], + matrices: &[Vec<(usize, usize, F)>], selectors: &[Vec<(usize, F)>], constants: &[F], ) { self.write_constants(constants); self.write_selectors_and_degree(selectors); - self.write_matrics(matrics); + self.write_matrices(matrices); } pub fn write_constants(&mut self, constants: &[F]) { @@ -211,10 +211,10 @@ impl + Hash> CCSCircuit { self.d = degree; } - fn write_matrics(&mut self, matrics: &[Vec<(usize, usize, F)>]) { - assert_eq!(matrics.len(), self.t); + fn write_matrices(&mut self, matrices: &[Vec<(usize, usize, F)>]) { + assert_eq!(matrices.len(), self.t); - self.matrices = matrics + self.matrices = matrices .iter() .map(|cells| { for &cell in cells.iter() { diff --git a/src/ccs/ir/mod.rs b/src/ccs/ir/mod.rs index a6f4584b..7858a33c 100644 --- a/src/ccs/ir/mod.rs +++ b/src/ccs/ir/mod.rs @@ -30,7 +30,7 @@ pub type CoeffsForSteps = HashMap>; impl PolyExpr { pub fn poly_to_coeffs(&self) -> CoeffsOnePoly { - let matrics = match self { + let matrices = match self { PolyExpr::Const(v) => vec![vec![vec![(*v, 0, false)]]], PolyExpr::Query((id, _, _, q)) => vec![vec![vec![(F::ONE, *id, *q)]]], PolyExpr::Sum(v) => { @@ -77,21 +77,21 @@ impl PolyExpr { } } PolyExpr::Mul(v) => { - let mut matrics = Vec::new(); + let mut matrices = Vec::new(); for e in v.iter() { let matrix = e.poly_to_coeffs(); if matrix.len() != 1 { panic!("[poly_to_coeffs]invalid poly expr with PolyExpr::Mul"); } for m in matrix[0].iter() { - matrics.push(m.clone()); + matrices.push(m.clone()); } } - vec![matrics] + vec![matrices] } _ => panic!("[poly_to_coeffs]invalid poly expr"), }; - matrics + matrices } pub fn factor_expr(&self) -> PolyExpr { From 6e8171bf3397968846c3a9052b433f72cf813fd3 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Thu, 22 Aug 2024 17:27:28 +0800 Subject: [PATCH 16/24] ccs: backend/sonobe -> backend/sonobe_hypernova --- src/ccs/backend/mod.rs | 2 +- src/ccs/backend/{sonobe.rs => sonobe_hypernova.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/ccs/backend/{sonobe.rs => sonobe_hypernova.rs} (100%) diff --git a/src/ccs/backend/mod.rs b/src/ccs/backend/mod.rs index 39cf66c2..5705b12c 100644 --- a/src/ccs/backend/mod.rs +++ b/src/ccs/backend/mod.rs @@ -1 +1 @@ -pub mod sonobe; +pub mod sonobe_hypernova; diff --git a/src/ccs/backend/sonobe.rs b/src/ccs/backend/sonobe_hypernova.rs similarity index 100% rename from src/ccs/backend/sonobe.rs rename to src/ccs/backend/sonobe_hypernova.rs From fee7befeed2c9740c66e7c700cb158efe20ea7c3 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Thu, 22 Aug 2024 23:31:21 +0800 Subject: [PATCH 17/24] ccs: update test for ccscircuit --- examples/fibonacci_ccs.rs | 1 - src/ccs/ir/circuit.rs | 70 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/examples/fibonacci_ccs.rs b/examples/fibonacci_ccs.rs index c9e8e95d..6340afdb 100644 --- a/examples/fibonacci_ccs.rs +++ b/examples/fibonacci_ccs.rs @@ -38,7 +38,6 @@ use folding_schemes::{ type FiboReturn = (Circuit, Option>, SBPIR); fn fibo_circuit_ccs + Hash>() -> FiboReturn { - use chiquito::frontend::dsl::cb::*; // functions for constraint building let fibo = circuit::("fibonacci", |ctx| { diff --git a/src/ccs/ir/circuit.rs b/src/ccs/ir/circuit.rs index 248bf39b..90ae18ec 100644 --- a/src/ccs/ir/circuit.rs +++ b/src/ccs/ir/circuit.rs @@ -506,3 +506,73 @@ fn is_last_step_with_next_signal( } false } + +#[cfg(test)] +mod tests { + use std::vec; + + use super::*; + use halo2_proofs::halo2curves::bn256::Fr; + + #[test] + fn test_ccs() { + let n = 7; + let l = 3; + + let mut ccs_circuit: CCSCircuit = CCSCircuit::new(); + ccs_circuit.n = n; + ccs_circuit.m = 4; + ccs_circuit.t = 8; + ccs_circuit.q = 5; + ccs_circuit.l = 3; + + let m0 = vec![ + (0, 1, Fr::ONE), + (1, 2, Fr::ONE), + (2, 3, Fr::ONE), + (3, 6, Fr::ONE), + ]; + let m1 = vec![ + (0, 1, Fr::ONE), + (1, 2, Fr::ONE), + (2, 4, Fr::ONE), + (3, 6, Fr::ONE), + ]; + let m2 = vec![ + (0, 1, Fr::ONE), + (1, 2, Fr::ONE), + (2, 5, Fr::ONE), + (3, 6, Fr::ONE), + ]; + let m3 = vec![(0, 0, Fr::ONE), (1, 0, Fr::ONE)]; + let m4 = vec![(2, 0, Fr::from(2))]; + let m5 = vec![(2, 0, Fr::from(2))]; + let m6 = vec![ + (0, 0, Fr::ONE.neg()), + (1, 0, Fr::ONE.neg()), + (2, 0, Fr::ONE.neg()), + ]; + let m7 = vec![(0, 0, Fr::ZERO)]; + let matrics = vec![m0, m1, m2, m3, m4, m5, m6, m7]; + + let selectors = vec![ + vec![(3, Fr::ONE), (0, Fr::ONE), (1, Fr::ONE)], + vec![(4, Fr::ONE), (0, Fr::ONE)], + vec![(5, Fr::ONE), (1, Fr::ONE)], + vec![(6, Fr::ONE), (2, Fr::ONE)], + vec![(7, Fr::ONE)], + ]; + let constants: Vec = vec![Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE]; + ccs_circuit.write(&matrics, &selectors, &constants); + + let mut z = Z::new(n, l); + z.write_with_values( + &[Fr::ZERO, Fr::ONE, Fr::from(2)], + &[Fr::from(3), Fr::from(10), Fr::from(43)], + ); + + let result = ccs_circuit.is_satisfied(&z); + + println!("result = {}", result); + } +} From 5de45028204b7dbf3c4d6a1d42b9d7fb6acc0a03 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Tue, 27 Aug 2024 16:34:11 +0800 Subject: [PATCH 18/24] ccs: update rust-toolchain --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 32a59aa4..79e15fd4 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2024-02-13 +1.77.0 From 68580644152dfafe70783a969dd9a1c556b940ee Mon Sep 17 00:00:00 2001 From: 10to4 Date: Tue, 27 Aug 2024 18:42:53 +0800 Subject: [PATCH 19/24] ccs: update toolchain-> 1.78.0 & modify for clippy --- Cargo.toml | 5 +++-- examples/blake2f.rs | 14 +++++++------- examples/keccak.rs | 10 ++++++++-- rust-toolchain | 2 +- src/ccs/ir/circuit.rs | 17 +++++++++-------- src/plonkish/backend/halo2.rs | 4 ++-- src/wit_gen.rs | 32 ++++++++++++++++---------------- 7 files changed, 46 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e9a4bdf..cbd6be8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,8 +27,9 @@ num-bigint = { version = "0.4", features = ["rand"] } uuid = { version = "1.4.0", features = ["v1", "rng"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -hyperplonk_benchmark = { git = "https://github.com/qwang98/plonkish.git", branch = "main", package = "benchmark" } -plonkish_backend = { git = "https://github.com/qwang98/plonkish.git", branch = "main", package = "plonkish_backend" } +hyperplonk_benchmark = { git = "https://github.com/10to4/plonkish.git", branch = "main", package = "benchmark" } +plonkish_backend = { git = "https://github.com/10to4/plonkish.git", branch = "main", package = "plonkish_backend" } +# plonkish_backend = { path = "../../zkevm-circuit/plonkish/plonkish_backend" } regex = "1" ark-ff = "^0.4.0" diff --git a/examples/blake2f.rs b/examples/blake2f.rs index 1ce2db7f..8607684a 100644 --- a/examples/blake2f.rs +++ b/examples/blake2f.rs @@ -871,15 +871,15 @@ fn blake2f_circuit( let mut output_vec = v_mid1_vec.clone(); if i >= 8 { if i % 2 == 0 { - input_vec = v_mid2_vec.clone(); - output_vec = v_mid3_vec.clone(); + input_vec.clone_from(&v_mid2_vec); + output_vec.clone_from(&v_mid3_vec) } else { - input_vec = v_mid3_vec.clone(); - output_vec = v_mid4_vec.clone(); + input_vec.clone_from(&v_mid3_vec); + output_vec.clone_from(&v_mid4_vec) } } else if i % 2 == 1 { - input_vec = v_mid1_vec.clone(); - output_vec = v_mid2_vec.clone(); + input_vec.clone_from(&v_mid1_vec); + output_vec.clone_from(&v_mid2_vec); } let (mut a, mut b, mut c, mut d) = (i / 2, 4 + i / 2, 8 + i / 2, 12 + i / 2); @@ -1319,7 +1319,7 @@ fn blake2f_circuit( b_3bits_vec, }; ctx.add(&blake2f_g_setup_vec[r as usize], ginputs); - v_vec_values = v_mid4_vec_values.clone(); + v_vec_values.clone_from(&v_mid4_vec_values); } let output_vec_values: Vec = h_vec_values diff --git a/examples/keccak.rs b/examples/keccak.rs index bc06c93b..ca750374 100644 --- a/examples/keccak.rs +++ b/examples/keccak.rs @@ -1635,9 +1635,15 @@ fn keccak_circuit + Eq + Hash>( let mut tmp_pi_sum_split_xor_vec = setup_theta_sum_split_xor_vec.clone(); for i in 0..PART_SIZE as usize { for j in 0..PART_SIZE as usize { + // tmp_pi_sum_split_xor_vec + // [j * PART_SIZE as usize + ((2 * i + 3 * j) % PART_SIZE as usize)] + // = setup_theta_sum_split_xor_vec[i * + // PART_SIZE as usize + j].clone(); tmp_pi_sum_split_xor_vec - [j * PART_SIZE as usize + ((2 * i + 3 * j) % PART_SIZE as usize)] = - setup_theta_sum_split_xor_vec[i * PART_SIZE as usize + j].clone(); + [j * PART_SIZE as usize + ((2 * i + 3 * j) % PART_SIZE as usize)] + .clone_from( + &setup_theta_sum_split_xor_vec[i * PART_SIZE as usize + j], + ); } } diff --git a/rust-toolchain b/rust-toolchain index 79e15fd4..54227249 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.77.0 +1.78.0 diff --git a/src/ccs/ir/circuit.rs b/src/ccs/ir/circuit.rs index 90ae18ec..93bfed6e 100644 --- a/src/ccs/ir/circuit.rs +++ b/src/ccs/ir/circuit.rs @@ -77,8 +77,8 @@ impl + Hash> Z { assert_eq!(inputs.len(), self.l); assert_eq!(witnesses.len(), self.n - self.l - 1); - self.public_inputs = inputs.to_owned().clone(); - self.assignments = witnesses.to_owned().clone(); + self.public_inputs.clone_from(&inputs.to_owned()); + self.assignments.clone_from(&witnesses.to_owned()); } } @@ -114,7 +114,8 @@ impl Matrix { pub fn reduce_rows(&mut self, m: usize) { if m < self.m { - self.values = self.values[0..m].to_owned(); + // self.values = self.values[0..m].to_owned(); + self.values.truncate(m); self.m = m; } } @@ -195,7 +196,7 @@ impl + Hash> CCSCircuit { pub fn write_constants(&mut self, constants: &[F]) { assert_eq!(constants.len(), self.q); - self.constants = constants.to_owned().clone(); + self.constants.clone_from(&constants.to_owned()); } pub fn write_selectors_and_degree(&mut self, selectors: &[Vec<(usize, F)>]) { @@ -207,7 +208,7 @@ impl + Hash> CCSCircuit { } degree = degree.max(selector.len()) } - self.selectors = selectors.to_owned().clone(); + self.selectors.clone_from(&selectors.to_owned()); self.d = degree; } @@ -303,9 +304,9 @@ impl + Hash> Circuit { self.calcu_bounds(&selectors.matrix_selectors); self.ccs.constants = vec![F::ONE; self.ccs.q]; - self.ccs.selectors = selectors.matrix_selectors.clone(); - self.exposed = exposed.to_owned(); - self.witness = witness.clone(); + self.ccs.selectors.clone_from(&selectors.matrix_selectors); + exposed.clone_into(&mut self.exposed); + self.witness.clone_from(witness); self.construct_matrix_coeffs_and_offsets(matrix_coeffs, &selectors.step_selectors); } diff --git a/src/plonkish/backend/halo2.rs b/src/plonkish/backend/halo2.rs index d8dfd6d2..410bfe4f 100644 --- a/src/plonkish/backend/halo2.rs +++ b/src/plonkish/backend/halo2.rs @@ -473,8 +473,8 @@ impl + Hash> h2Circuit for ChiquitoHalo2SuperCircuit }); sub_circuits.iter_mut().for_each(|sub_circuit| { - sub_circuit.advice_columns = advice_columns.clone(); - sub_circuit.fixed_columns = fixed_columns.clone(); + sub_circuit.advice_columns.clone_from(&advice_columns); + sub_circuit.fixed_columns.clone_from(&fixed_columns); sub_circuit.configure_sub_circuit(meta) }); diff --git a/src/wit_gen.rs b/src/wit_gen.rs index 81b542f1..c2bd9958 100644 --- a/src/wit_gen.rs +++ b/src/wit_gen.rs @@ -189,22 +189,22 @@ pub(crate) fn calc_auto_signals 0 { - pending = pending - .clone() - .into_iter() - .filter(|s| { - if let Some(value) = auto_signals - .get(s) - .expect("auto definition not found") - .eval(assignments) - { - assignments.insert(s.clone(), value); - } - - assignments.get(s).is_none() - }) - .collect::>() - .clone(); + pending.clone_from( + &pending + .clone() + .into_iter() + .filter(|s| { + if let Some(value) = auto_signals + .get(s) + .expect("auto definition not found") + .eval(assignments) + { + assignments.insert(s.clone(), value); + } + assignments.get(s).is_none() + }) + .collect::>(), + ); // in each round at least one new signal should be assigned if pending.len() == pending_amount { From c78b5dc2a6145a7f09c0b1bc91f831a6904a38f8 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Tue, 27 Aug 2024 18:53:50 +0800 Subject: [PATCH 20/24] ccs: modify serde version for Clippy Check --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cbd6be8c..2b84ab0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.gi polyexen = { git = "https://github.com/Dhole/polyexen.git", rev = "16a85c5411f804dc49bbf373d24ff9eedadedfbe" } num-bigint = { version = "0.4", features = ["rand"] } uuid = { version = "1.4.0", features = ["v1", "rng"] } -serde = { version = "1.0", features = ["derive"] } +serde = { version = "=1.0.208", features = ["derive"] } serde_json = "1.0" hyperplonk_benchmark = { git = "https://github.com/10to4/plonkish.git", branch = "main", package = "benchmark" } plonkish_backend = { git = "https://github.com/10to4/plonkish.git", branch = "main", package = "plonkish_backend" } From da8a22d4961fcf815830252cac710ccb4fa25939 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Tue, 27 Aug 2024 20:52:38 +0800 Subject: [PATCH 21/24] ccs: modify serde version for Clippy Check --- Cargo.toml | 3 +-- rust-toolchain | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2b84ab0c..06003d6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,11 +25,10 @@ halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.gi polyexen = { git = "https://github.com/Dhole/polyexen.git", rev = "16a85c5411f804dc49bbf373d24ff9eedadedfbe" } num-bigint = { version = "0.4", features = ["rand"] } uuid = { version = "1.4.0", features = ["v1", "rng"] } -serde = { version = "=1.0.208", features = ["derive"] } +serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = "1.0" hyperplonk_benchmark = { git = "https://github.com/10to4/plonkish.git", branch = "main", package = "benchmark" } plonkish_backend = { git = "https://github.com/10to4/plonkish.git", branch = "main", package = "plonkish_backend" } -# plonkish_backend = { path = "../../zkevm-circuit/plonkish/plonkish_backend" } regex = "1" ark-ff = "^0.4.0" diff --git a/rust-toolchain b/rust-toolchain index 54227249..3c4c7c2d 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.78.0 +1.78.0 \ No newline at end of file From 0ea358695e200660040f1c68d774be509dc65d8c Mon Sep 17 00:00:00 2001 From: 10to4 Date: Tue, 27 Aug 2024 22:11:17 +0800 Subject: [PATCH 22/24] ccs: modify serde version for Clippy Check --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 06003d6d..25b6b4dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,8 @@ halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.gi polyexen = { git = "https://github.com/Dhole/polyexen.git", rev = "16a85c5411f804dc49bbf373d24ff9eedadedfbe" } num-bigint = { version = "0.4", features = ["rand"] } uuid = { version = "1.4.0", features = ["v1", "rng"] } -serde = { version = "1.0", default-features = false, features = ["derive"] } +serde = { version = "=1.0.203", default-features = false, features = ["derive"] } + serde_json = "1.0" hyperplonk_benchmark = { git = "https://github.com/10to4/plonkish.git", branch = "main", package = "benchmark" } plonkish_backend = { git = "https://github.com/10to4/plonkish.git", branch = "main", package = "plonkish_backend" } From 9b73dd6264351cd9f0f25365a3c790db461dca16 Mon Sep 17 00:00:00 2001 From: 10to4 Date: Wed, 28 Aug 2024 18:10:06 +0800 Subject: [PATCH 23/24] ccs: refactor some functions for matrix, z and CCSCircuit --- src/ccs/backend/sonobe_hypernova.rs | 2 +- src/ccs/compiler/unit.rs | 3 +- src/ccs/ir/circuit.rs | 220 +++++++++++++--------------- 3 files changed, 104 insertions(+), 121 deletions(-) diff --git a/src/ccs/backend/sonobe_hypernova.rs b/src/ccs/backend/sonobe_hypernova.rs index bbd250e4..e063204d 100644 --- a/src/ccs/backend/sonobe_hypernova.rs +++ b/src/ccs/backend/sonobe_hypernova.rs @@ -46,7 +46,7 @@ impl Z { pub fn convert_to_sonobe_inputs(&self, f: fn(&F) -> Fr) -> Vec { [ vec![F::from(1u64)], - self.assignments.clone(), + self.witnesses.clone(), self.public_inputs.clone(), ] .concat() diff --git a/src/ccs/compiler/unit.rs b/src/ccs/compiler/unit.rs index 9f8ea32b..82bee526 100644 --- a/src/ccs/compiler/unit.rs +++ b/src/ccs/compiler/unit.rs @@ -79,8 +79,6 @@ impl From<&astCircuit> for CompilationUnit { impl From> for Circuit { fn from(unit: CompilationUnit) -> Self { - let mut circuit = Circuit::new(unit.ast_id); - let exposed: Vec<(usize, UUID)> = unit .exposed .iter() @@ -104,6 +102,7 @@ impl From> for Circuit { witnesses.insert(*step_uuid, signal_uuids); } + let mut circuit = Circuit::new(unit.ast_id); circuit.write(&unit.matrix_coeffs, &unit.selector, &exposed, &witnesses); circuit diff --git a/src/ccs/ir/circuit.rs b/src/ccs/ir/circuit.rs index 93bfed6e..f24ae28a 100644 --- a/src/ccs/ir/circuit.rs +++ b/src/ccs/ir/circuit.rs @@ -28,11 +28,16 @@ pub fn vec_add + Hash>(vec: &[Vec]) -> Vec { }) } -// input vector z = [1, x, w] +// The satisfying assignment Z consists of an finite field value 1, +// a vector public input and output x, and a vector witness w. +// `n` is the length of z vector +// `l` is the length of x +// `witnesses` is a vector witness w +// `public_inputs` is a vector public input and output pub struct Z> { pub n: usize, pub l: usize, - pub assignments: Vec, + pub witnesses: Vec, pub public_inputs: Vec, } @@ -42,44 +47,49 @@ impl + Hash> Z { Self { n, l, - assignments: Default::default(), + witnesses: Default::default(), public_inputs: Default::default(), } } - pub fn write( - &mut self, - inputs: &HashMap<(usize, UUID), F>, - witnesses: &[(UUID, HashMap)], - assign_pos: &AssignmentOffsets, - ) { - assert_eq!(inputs.len(), self.l); - assert_eq!(assign_pos.len(), self.n); - let witness_len = self.n - self.l - 1; - let mut witness_values = vec![F::ZERO; witness_len]; - let mut public_values = vec![F::ZERO; self.l]; - for ((step_idx, signal_id), idx) in assign_pos.iter() { - if *signal_id == 0 { - continue; - } - if *idx < self.n - self.l { - witness_values[*idx - 1] = *witnesses[*step_idx].1.get(signal_id).unwrap(); - } else { - public_values[*idx - witness_len - 1] = - *inputs.get(&(*step_idx, *signal_id)).unwrap(); - } + pub fn from_values(inputs: &[F], witnesses: &[F]) -> Self { + Self { + l: inputs.len(), + n: inputs.len() + witnesses.len() + 1, + public_inputs: inputs.to_vec(), + witnesses: witnesses.to_vec(), } - self.assignments = witness_values; - self.public_inputs = public_values; } +} - pub fn write_with_values(&mut self, inputs: &[F], witnesses: &[F]) { - assert_eq!(inputs.len(), self.l); - assert_eq!(witnesses.len(), self.n - self.l - 1); - - self.public_inputs.clone_from(&inputs.to_owned()); - self.assignments.clone_from(&witnesses.to_owned()); +pub fn create_z_from_assignments + Hash>( + assignments: &Option>, + inputs: &HashMap<(usize, UUID), F>, + assign_pos: &AssignmentOffsets, +) -> Z { + let l = inputs.len(); + let n = assign_pos.len(); + let mut z = Z::new(n, l); + + let witnesses = assignments.as_deref().unwrap(); + + let witness_len = n - l - 1; + let mut witness_values = vec![F::ZERO; witness_len]; + let mut public_values = vec![F::ZERO; l]; + for ((step_idx, signal_id), idx) in assign_pos.iter() { + if *signal_id == 0 { + continue; + } + if *idx < n - l { + witness_values[*idx - 1] = *witnesses[*step_idx].1.get(signal_id).unwrap(); + } else { + public_values[*idx - witness_len - 1] = *inputs.get(&(*step_idx, *signal_id)).unwrap(); + } } + z.witnesses = witness_values; + z.public_inputs = public_values; + + z } #[derive(Debug, Clone)] @@ -98,6 +108,7 @@ impl Matrix { } } + // Modify parts of cells in a Matrix pub fn write(&mut self, values: &[(usize, usize, F)]) { for &(row, col, value) in values.iter() { assert!(row < self.m); @@ -106,6 +117,21 @@ impl Matrix { } } + pub fn from_values(n: usize, m: usize, values: &[(usize, usize, F)]) -> Self { + let mut mvalues = vec![vec![F::ZERO; n]; m]; + for &(row, col, value) in values.iter() { + assert!(row < m); + assert!(col < n); + mvalues[row][col] = value; + } + + Self { + n, + m, + values: mvalues, + } + } + pub fn get(&self, row: usize, col: usize) -> F { assert!(row < self.m); assert!(col < self.n); @@ -157,24 +183,6 @@ pub struct CCSCircuit { } impl + Hash> CCSCircuit { - pub fn new() -> Self { - let matrices = Vec::new(); - let selectors = Vec::new(); - let constants = Vec::new(); - - Self { - n: 0, - m: 0, - l: 0, - t: 0, - q: 0, - d: 0, - matrices, - selectors, - constants, - } - } - pub fn public_num(&self) -> usize { self.l } @@ -183,58 +191,11 @@ impl + Hash> CCSCircuit { self.n - self.l - 1 } - pub fn write( - &mut self, - matrices: &[Vec<(usize, usize, F)>], - selectors: &[Vec<(usize, F)>], - constants: &[F], - ) { - self.write_constants(constants); - self.write_selectors_and_degree(selectors); - self.write_matrices(matrices); - } - - pub fn write_constants(&mut self, constants: &[F]) { - assert_eq!(constants.len(), self.q); - self.constants.clone_from(&constants.to_owned()); - } - - pub fn write_selectors_and_degree(&mut self, selectors: &[Vec<(usize, F)>]) { - let mut degree = 0; - assert_eq!(selectors.len(), self.q); - for selector in selectors.iter() { - for &(s, _) in selector { - assert!(s < self.t) - } - degree = degree.max(selector.len()) - } - self.selectors.clone_from(&selectors.to_owned()); - self.d = degree; - } - - fn write_matrices(&mut self, matrices: &[Vec<(usize, usize, F)>]) { - assert_eq!(matrices.len(), self.t); - - self.matrices = matrices - .iter() - .map(|cells| { - for &cell in cells.iter() { - assert!(cell.0 < self.m); - assert!(cell.1 < self.n); - } - - let mut matrix = Matrix::new(self.n, self.m); - matrix.write(cells); - matrix - }) - .collect(); - } - pub fn is_satisfied(&self, z: &Z) -> bool { assert_eq!(self.selectors.len(), self.q); assert_eq!(self.constants.len(), self.q); - let mut witnesses = z.assignments.clone(); + let mut witnesses = z.witnesses.clone(); let mut inputs = z.public_inputs.clone(); let mut values = vec![F::ONE]; @@ -272,6 +233,40 @@ impl + Hash> CCSCircuit { } } +pub fn create_ccs_circuit + Hash>( + n: usize, + m: usize, + l: usize, + matrices: &[Vec<(usize, usize, F)>], + selectors: &[Vec<(usize, F)>], + constants: &[F], +) -> CCSCircuit { + let mut degree = 0; + assert_eq!(selectors.len(), constants.len()); + for selector in selectors.iter() { + for &(s, _) in selector { + assert!(s < matrices.len()) + } + degree = degree.max(selector.len()) + } + let matrices: Vec> = matrices + .iter() + .map(|cells| Matrix::from_values(n, m, cells)) + .collect(); + + CCSCircuit { + n, + m, + t: matrices.len(), + q: constants.len(), + l, + d: degree, + constants: constants.to_vec(), + selectors: selectors.to_vec(), + matrices, + } +} + #[derive(Debug)] pub struct Circuit { pub ast_id: UUID, @@ -289,7 +284,7 @@ impl + Hash> Circuit { matrix_coeffs_and_offsets: HashMap::new(), exposed: Vec::new(), witness: HashMap::new(), - ccs: CCSCircuit::new(), + ccs: CCSCircuit::default(), ast_id, } } @@ -376,14 +371,7 @@ impl + Hash> Circuit { self.ccs.m = m; self.ccs.matrices = matrices; - let mut z: Z = Z::new(n, l); - z.write( - &self.instance(&assignments), - assignments.as_deref().unwrap(), - &assign_pos, - ); - - z + create_z_from_assignments(&assignments, &self.instance(&assignments), &assign_pos) } fn assignments_coeff_offset( @@ -435,6 +423,7 @@ impl + Hash> Circuit { .sum() }) .unwrap(); + // Initial a vector of Matrices let mut matrices = vec![Matrix::new(n, num_poly); num]; let mut row = 0; @@ -466,6 +455,7 @@ impl + Hash> Circuit { Some(col) => values.push((row, *col, *value)), } } + // Modify matrices values from steps matrices[*selector].write(&values); } } @@ -518,15 +508,9 @@ mod tests { #[test] fn test_ccs() { let n = 7; + let m = 4; let l = 3; - let mut ccs_circuit: CCSCircuit = CCSCircuit::new(); - ccs_circuit.n = n; - ccs_circuit.m = 4; - ccs_circuit.t = 8; - ccs_circuit.q = 5; - ccs_circuit.l = 3; - let m0 = vec![ (0, 1, Fr::ONE), (1, 2, Fr::ONE), @@ -554,7 +538,7 @@ mod tests { (2, 0, Fr::ONE.neg()), ]; let m7 = vec![(0, 0, Fr::ZERO)]; - let matrics = vec![m0, m1, m2, m3, m4, m5, m6, m7]; + let matrices = vec![m0, m1, m2, m3, m4, m5, m6, m7]; let selectors = vec![ vec![(3, Fr::ONE), (0, Fr::ONE), (1, Fr::ONE)], @@ -564,10 +548,10 @@ mod tests { vec![(7, Fr::ONE)], ]; let constants: Vec = vec![Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE]; - ccs_circuit.write(&matrics, &selectors, &constants); - let mut z = Z::new(n, l); - z.write_with_values( + let ccs_circuit = create_ccs_circuit(n, m, l, &matrices, &selectors, &constants); + + let z = Z::from_values( &[Fr::ZERO, Fr::ONE, Fr::from(2)], &[Fr::from(3), Fr::from(10), Fr::from(43)], ); From 72bbde735fa20f63326b462b56c4fbf554eea54b Mon Sep 17 00:00:00 2001 From: 10to4 Date: Thu, 29 Aug 2024 15:50:21 +0800 Subject: [PATCH 24/24] ccs: refactor ccs circuit in ir --- examples/fibonacci_ccs.rs | 2 +- src/ccs/backend/sonobe_hypernova.rs | 2 +- src/ccs/compiler/mod.rs | 2 +- src/ccs/compiler/unit.rs | 250 +++++++++++++++- src/ccs/ir/assignments.rs | 48 ++++ src/ccs/ir/circuit.rs | 430 ++-------------------------- src/ccs/ir/mod.rs | 143 ++++++++- 7 files changed, 460 insertions(+), 417 deletions(-) diff --git a/examples/fibonacci_ccs.rs b/examples/fibonacci_ccs.rs index 6340afdb..33eb624e 100644 --- a/examples/fibonacci_ccs.rs +++ b/examples/fibonacci_ccs.rs @@ -11,7 +11,7 @@ use chiquito::{ config, step_selector::SimpleStepSelectorBuilder, }, - ir::{assignments::AssignmentGenerator, circuit::Circuit}, + ir::{assignments::AssignmentGenerator, Circuit}, }, field::Field, frontend::dsl::circuit, diff --git a/src/ccs/backend/sonobe_hypernova.rs b/src/ccs/backend/sonobe_hypernova.rs index e063204d..77ac04f5 100644 --- a/src/ccs/backend/sonobe_hypernova.rs +++ b/src/ccs/backend/sonobe_hypernova.rs @@ -3,7 +3,7 @@ use ark_std::log2; use folding_schemes::{ccs::CCS, utils::vec::dense_matrix_to_sparse}; use crate::{ - ccs::ir::circuit::{CCSCircuit, Z}, + ccs::ir::{assignments::Z, circuit::CCSCircuit}, field::Field, }; diff --git a/src/ccs/compiler/mod.rs b/src/ccs/compiler/mod.rs index 2c78d84f..1b30c90f 100644 --- a/src/ccs/compiler/mod.rs +++ b/src/ccs/compiler/mod.rs @@ -6,7 +6,7 @@ use std::{collections::HashMap, hash::Hash, rc::Rc}; use unit::CompilationUnit; use crate::{ - ccs::ir::{assignments::AssignmentGenerator, circuit::Circuit, CoeffsForSteps, Poly, PolyExpr}, + ccs::ir::{assignments::AssignmentGenerator, Circuit, CoeffsForSteps, Poly, PolyExpr}, field::Field, poly::Expr, sbpir::{query::Queriable, ExposeOffset, PIR, SBPIR as astCircuit}, diff --git a/src/ccs/compiler/unit.rs b/src/ccs/compiler/unit.rs index 82bee526..f7cab694 100644 --- a/src/ccs/compiler/unit.rs +++ b/src/ccs/compiler/unit.rs @@ -1,7 +1,8 @@ use crate::{ - ccs::{ - compiler::SignalPlacement, - ir::{circuit::Circuit, CoeffsForSteps, Poly}, + ccs::ir::{ + assignments::{Assignments, Coeffs, Z}, + circuit::{CCSCircuit, Matrix, SelectorsOffsetAndCoeff}, + AssignmentOffsets, Circuit, CoeffsForProds, CoeffsForSteps, Poly, }, field::Field, sbpir::{FixedSignal, ForwardSignal, SharedSignal, StepType, SBPIR as astCircuit}, @@ -10,7 +11,10 @@ use crate::{ use std::{collections::HashMap, hash::Hash, rc::Rc}; -use super::{cell_manager::Placement, step_selector::StepSelector}; +use super::{ + cell_manager::{Placement, SignalPlacement}, + step_selector::{SelectorsForALLMatrix, SelectorsForALLSteps, StepSelector}, +}; #[derive(Debug, Clone)] pub struct CompilationUnit { @@ -108,3 +112,241 @@ impl From> for Circuit { circuit } } + +impl + Hash> Circuit { + pub fn new(ast_id: UUID) -> Self { + Self { + matrix_coeffs_and_offsets: HashMap::new(), + exposed: Vec::new(), + witness: HashMap::new(), + ccs: CCSCircuit::default(), + ast_id, + } + } + + pub fn write( + &mut self, + matrix_coeffs: &HashMap>, + selectors: &StepSelector, + exposed: &[(usize, UUID)], + witness: &HashMap>, + ) { + self.calcu_bounds(&selectors.matrix_selectors); + + self.ccs.constants = vec![F::ONE; self.ccs.q]; + self.ccs.selectors.clone_from(&selectors.matrix_selectors); + exposed.clone_into(&mut self.exposed); + self.witness.clone_from(witness); + + self.construct_matrix_coeffs_and_offsets(matrix_coeffs, &selectors.step_selectors); + } + + fn calcu_bounds(&mut self, matrix_selectors: &SelectorsForALLMatrix) { + let mut t = 0; + let mut d = 0; + self.ccs.q = matrix_selectors.len(); + + for selectors in matrix_selectors.iter() { + d = d.max(selectors.len()); + for (idx, _) in selectors.iter() { + t = t.max(*idx + 1); + } + } + self.ccs.t = t; + self.ccs.d = d; + } + + fn construct_matrix_coeffs_and_offsets( + &mut self, + matrix_coeffs: &HashMap>, + step_selectors: &SelectorsForALLSteps, + ) { + let mut matrix_coeffs_and_offsets = HashMap::new(); + for (step_id, coeffs_one_step) in matrix_coeffs.iter() { + let selectors_one_step = step_selectors.get(step_id).unwrap(); + let v = coeffs_one_step + .iter() + .zip(selectors_one_step.iter()) + .map(|(coeffs_one_poly, selectors_one_poly)| { + coeffs_one_poly + .iter() + .zip(selectors_one_poly.iter()) + .map(|(coeffs_one_chunk, selectors_one_chunk)| { + (coeffs_one_chunk.clone(), *selectors_one_chunk) + }) + .collect() + }) + .collect(); + + matrix_coeffs_and_offsets.insert(*step_id, v); + } + self.matrix_coeffs_and_offsets = matrix_coeffs_and_offsets; + } + + pub fn generate(&mut self, assignments: Option>) -> Z { + let (assign_pos, n, l) = self.assignments_coeff_offset(&assignments); + let matrix_num = calc_matrix_num(&self.ccs.selectors); + + let (matrices, m) = self.export_matrix_vec(&assignments, n, matrix_num, &assign_pos); + + self.ccs.n = n; + self.ccs.l = l; + self.ccs.m = m; + self.ccs.matrices = matrices; + + create_z_from_assignments(&assignments, &self.instance(&assignments), &assign_pos) + } + + fn assignments_coeff_offset( + &self, + witness: &Option>, + ) -> (AssignmentOffsets, usize, usize) { + let mut public_pos = HashMap::new(); + for (offset, (step_idx, signal_uuid)) in self.exposed.iter().enumerate() { + public_pos.insert((*step_idx, *signal_uuid), offset); + } + + let mut witness_pos = HashMap::new(); + let mut offset = 0; + witness_pos.insert((0, 0), offset); + offset += 1; + if let Some(assignments) = witness.as_ref() { + for (step_idx, (step_id, _)) in assignments.iter().enumerate() { + let signal_uuids = self.witness.get(step_id).unwrap(); + for id in signal_uuids.iter() { + let exist = public_pos.get(&(step_idx, *id)); + if exist.is_none() { + witness_pos.insert((step_idx, *id), offset); + offset += 1; + } + } + } + } + + for ((step_idx, id), v) in public_pos.iter() { + witness_pos.insert((*step_idx, *id), v + offset); + offset += 1; + } + (witness_pos, offset, public_pos.len()) + } + + fn export_matrix_vec( + &self, + witness: &Option>, + n: usize, + num: usize, + assign_pos: &AssignmentOffsets, + ) -> (Vec>, usize) { + let num_poly = witness + .as_ref() + .map(|steps_idx| { + steps_idx + .iter() + .map(|(idx, _)| self.matrix_coeffs_and_offsets.get(idx).unwrap().len()) + .sum() + }) + .unwrap(); + // Initial a vector of Matrices + let mut matrices = vec![Matrix::new(n, num_poly); num]; + let mut row = 0; + + if let Some(steps_id) = witness.as_ref() { + let step_num = steps_id.len(); + for (step_idx, (step_id, _)) in steps_id.iter().enumerate() { + for coeffs_one_poly in self.matrix_coeffs_and_offsets.get(step_id).unwrap().iter() { + if is_last_step_with_next_signal(coeffs_one_poly, step_num, step_idx) { + continue; + } + + for (coeffs_chunks, index) in coeffs_one_poly.iter() { + assert!(*index <= self.ccs.selectors.len()); + let selectors = self.ccs.selectors[*index].clone(); + assert_eq!(coeffs_chunks.len(), selectors.len()); + + for (coeffs, (selector, _)) in coeffs_chunks.iter().zip(selectors.iter()) { + // one row in a matrix + let mut values: Vec<(usize, usize, F)> = Vec::new(); + for (value, signal_id, next) in coeffs.iter() { + let col = if *signal_id == 0 { + assign_pos.get(&(0, 0)) + } else { + let idx = if *next { step_idx + 1 } else { step_idx }; + assign_pos.get(&(idx, *signal_id)) + }; + match col { + None => continue, + Some(col) => values.push((row, *col, *value)), + } + } + // Modify matrices values from steps + matrices[*selector].write(&values); + } + } + row += 1; + } + } + }; + + for matrix in matrices.iter_mut() { + matrix.reduce_rows(row); + } + (matrices, row) + } +} + +pub fn create_z_from_assignments + Hash>( + assignments: &Option>, + inputs: &HashMap<(usize, UUID), F>, + assign_pos: &AssignmentOffsets, +) -> Z { + let l = inputs.len(); + let n = assign_pos.len(); + let mut z = Z::new(n, l); + + let witnesses = assignments.as_deref().unwrap(); + + let witness_len = n - l - 1; + let mut witness_values = vec![F::ZERO; witness_len]; + let mut public_values = vec![F::ZERO; l]; + for ((step_idx, signal_id), idx) in assign_pos.iter() { + if *signal_id == 0 { + continue; + } + if *idx < n - l { + witness_values[*idx - 1] = *witnesses[*step_idx].1.get(signal_id).unwrap(); + } else { + public_values[*idx - witness_len - 1] = *inputs.get(&(*step_idx, *signal_id)).unwrap(); + } + } + z.witnesses = witness_values; + z.public_inputs = public_values; + + z +} + +fn calc_matrix_num(selectors: &SelectorsOffsetAndCoeff) -> usize { + let mut matrix_num = 0; + for selector in selectors.iter() { + for (idx, _) in selector.iter() { + matrix_num = matrix_num.max(*idx + 1); + } + } + matrix_num +} + +fn is_last_step_with_next_signal( + coeffs_one_poly: &[(CoeffsForProds, usize)], + step_num: usize, + step_idx: usize, +) -> bool { + if step_idx == step_num - 1 { + for (coeffs_for_prods, _) in coeffs_one_poly.iter() { + for (_, _, next) in coeffs_for_prods.concat().iter() { + if *next { + return true; + } + } + } + } + false +} diff --git a/src/ccs/ir/assignments.rs b/src/ccs/ir/assignments.rs index 1b0882d6..5daf2379 100644 --- a/src/ccs/ir/assignments.rs +++ b/src/ccs/ir/assignments.rs @@ -1,5 +1,6 @@ use std::{ collections::HashMap, + fmt::Debug, hash::Hash, ops::{Deref, DerefMut}, }; @@ -14,6 +15,7 @@ use crate::{ pub type Coeffs = Vec>>>; +// All the assignments values in all the steps. #[derive(Debug, Clone)] pub struct Assignments(pub Vec<(UUID, HashMap)>); @@ -210,3 +212,49 @@ impl PublicInputs { self.len() == 0 } } + +// The satisfying assignment Z consists of an finite field value 1, +// a vector public input and output x, and a vector witness w. +// `n` is the length of z vector +// `l` is the length of x +// `witnesses` is a vector witness w +// `public_inputs` is a vector public input and output +#[derive(Clone, Default)] +pub struct Z { + pub n: usize, + pub l: usize, + pub witnesses: Vec, + pub public_inputs: Vec, +} + +impl Debug for Z { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Z") + .field("n", &self.n) + .field("l", &self.l) + .field("witnesses", &self.witnesses) + .field("public_inputs", &self.public_inputs) + .finish() + } +} + +impl + Hash> Z { + pub fn new(n: usize, l: usize) -> Self { + assert!(n > l); + Self { + n, + l, + witnesses: Default::default(), + public_inputs: Default::default(), + } + } + + pub fn from_values(inputs: &[F], witnesses: &[F]) -> Self { + Self { + l: inputs.len(), + n: inputs.len() + witnesses.len() + 1, + public_inputs: inputs.to_vec(), + witnesses: witnesses.to_vec(), + } + } +} diff --git a/src/ccs/ir/circuit.rs b/src/ccs/ir/circuit.rs index f24ae28a..33a982e9 100644 --- a/src/ccs/ir/circuit.rs +++ b/src/ccs/ir/circuit.rs @@ -1,19 +1,12 @@ -use std::{collections::HashMap, hash::Hash}; +use std::hash::Hash; -use super::{assignments::*, CoeffsForProds}; -use crate::{ - ccs::compiler::step_selector::{SelectorsForALLMatrix, SelectorsForALLSteps, StepSelector}, - field::Field, - util::UUID, -}; +use super::assignments::*; +use crate::field::Field; -pub type MatrixCoeffsAndOffset = Vec, usize)>>; +// A multisets for selector in CCSCircuit pub type SelectorsOffsetAndCoeff = Vec>; -// ((step_idx, step_UUID), assignment_offset) -pub type AssignmentOffsets = HashMap<(usize, u128), usize>; - -pub fn hadamard_product + Hash>(vec1: &[F], vec2: &[F]) -> Vec { +fn hadamard_product + Hash>(vec1: &[F], vec2: &[F]) -> Vec { assert_eq!(vec1.len(), vec2.len()); vec1.iter() .zip(vec2.iter()) @@ -21,77 +14,17 @@ pub fn hadamard_product + Hash>(vec1: &[F], vec2: &[F]) -> .collect() } -pub fn vec_add + Hash>(vec: &[Vec]) -> Vec { +fn vec_add + Hash>(vec: &[Vec]) -> Vec { assert!(vec.len() > 1); vec.iter().fold(vec![F::ZERO; vec[0].len()], |acc, vec| { acc.iter().zip(vec.iter()).map(|(&a, &v)| a + v).collect() }) } -// The satisfying assignment Z consists of an finite field value 1, -// a vector public input and output x, and a vector witness w. -// `n` is the length of z vector -// `l` is the length of x -// `witnesses` is a vector witness w -// `public_inputs` is a vector public input and output -pub struct Z> { - pub n: usize, - pub l: usize, - pub witnesses: Vec, - pub public_inputs: Vec, -} - -impl + Hash> Z { - pub fn new(n: usize, l: usize) -> Self { - assert!(n > l); - Self { - n, - l, - witnesses: Default::default(), - public_inputs: Default::default(), - } - } - - pub fn from_values(inputs: &[F], witnesses: &[F]) -> Self { - Self { - l: inputs.len(), - n: inputs.len() + witnesses.len() + 1, - public_inputs: inputs.to_vec(), - witnesses: witnesses.to_vec(), - } - } -} - -pub fn create_z_from_assignments + Hash>( - assignments: &Option>, - inputs: &HashMap<(usize, UUID), F>, - assign_pos: &AssignmentOffsets, -) -> Z { - let l = inputs.len(); - let n = assign_pos.len(); - let mut z = Z::new(n, l); - - let witnesses = assignments.as_deref().unwrap(); - - let witness_len = n - l - 1; - let mut witness_values = vec![F::ZERO; witness_len]; - let mut public_values = vec![F::ZERO; l]; - for ((step_idx, signal_id), idx) in assign_pos.iter() { - if *signal_id == 0 { - continue; - } - if *idx < n - l { - witness_values[*idx - 1] = *witnesses[*step_idx].1.get(signal_id).unwrap(); - } else { - public_values[*idx - witness_len - 1] = *inputs.get(&(*step_idx, *signal_id)).unwrap(); - } - } - z.witnesses = witness_values; - z.public_inputs = public_values; - - z -} - +// Matrix +// n: The number of columns in one row +// m: The number of rows +// values: A two-dimensional vectors, where each sub vector represents a row of the matrix #[derive(Debug, Clone)] pub struct Matrix { n: usize, @@ -100,6 +33,8 @@ pub struct Matrix { } impl Matrix { + // At the beginning, We can only get the numbers of rows and columns, + // with all values being zero by default. pub fn new(n: usize, m: usize) -> Self { Self { n, @@ -117,6 +52,7 @@ impl Matrix { } } + // Get all of the non-zero values in the matrices. pub fn from_values(n: usize, m: usize, values: &[(usize, usize, F)]) -> Self { let mut mvalues = vec![vec![F::ZERO; n]; m]; for &(row, col, value) in values.iter() { @@ -140,7 +76,6 @@ impl Matrix { pub fn reduce_rows(&mut self, m: usize) { if m < self.m { - // self.values = self.values[0..m].to_owned(); self.values.truncate(m); self.m = m; } @@ -169,6 +104,16 @@ impl + Hash> Matrix { } } +// CCS Circuit +// n: The number of columns in one row, as well as the length of all the assignments +// m: The number of rows +// l: The number of public inputs +// t: The number of matrices +// q: The number of multisets, where +// d: The degree of each multiset is at most d. +// matrices: all of the matrices +// selectors: a sequence of q multisets, where an element in each multiset is the index of a matrix +// constants: q constants for q multisets #[derive(Debug, Default)] pub struct CCSCircuit { pub n: usize, @@ -232,332 +177,3 @@ impl + Hash> CCSCircuit { true } } - -pub fn create_ccs_circuit + Hash>( - n: usize, - m: usize, - l: usize, - matrices: &[Vec<(usize, usize, F)>], - selectors: &[Vec<(usize, F)>], - constants: &[F], -) -> CCSCircuit { - let mut degree = 0; - assert_eq!(selectors.len(), constants.len()); - for selector in selectors.iter() { - for &(s, _) in selector { - assert!(s < matrices.len()) - } - degree = degree.max(selector.len()) - } - let matrices: Vec> = matrices - .iter() - .map(|cells| Matrix::from_values(n, m, cells)) - .collect(); - - CCSCircuit { - n, - m, - t: matrices.len(), - q: constants.len(), - l, - d: degree, - constants: constants.to_vec(), - selectors: selectors.to_vec(), - matrices, - } -} - -#[derive(Debug)] -pub struct Circuit { - pub ast_id: UUID, - - pub matrix_coeffs_and_offsets: HashMap>, - pub exposed: Vec<(usize, UUID)>, - pub witness: HashMap>, - - pub ccs: CCSCircuit, -} - -impl + Hash> Circuit { - pub fn new(ast_id: UUID) -> Self { - Self { - matrix_coeffs_and_offsets: HashMap::new(), - exposed: Vec::new(), - witness: HashMap::new(), - ccs: CCSCircuit::default(), - ast_id, - } - } - - pub fn write( - &mut self, - matrix_coeffs: &HashMap>, - selectors: &StepSelector, - exposed: &[(usize, UUID)], - witness: &HashMap>, - ) { - self.calcu_bounds(&selectors.matrix_selectors); - - self.ccs.constants = vec![F::ONE; self.ccs.q]; - self.ccs.selectors.clone_from(&selectors.matrix_selectors); - exposed.clone_into(&mut self.exposed); - self.witness.clone_from(witness); - - self.construct_matrix_coeffs_and_offsets(matrix_coeffs, &selectors.step_selectors); - } - - fn calcu_bounds(&mut self, matrix_selectors: &SelectorsForALLMatrix) { - let mut t = 0; - let mut d = 0; - self.ccs.q = matrix_selectors.len(); - - for selectors in matrix_selectors.iter() { - d = d.max(selectors.len()); - for (idx, _) in selectors.iter() { - t = t.max(*idx + 1); - } - } - self.ccs.t = t; - self.ccs.d = d; - } - - fn construct_matrix_coeffs_and_offsets( - &mut self, - matrix_coeffs: &HashMap>, - step_selectors: &SelectorsForALLSteps, - ) { - let mut matrix_coeffs_and_offsets = HashMap::new(); - for (step_id, coeffs_one_step) in matrix_coeffs.iter() { - let selectors_one_step = step_selectors.get(step_id).unwrap(); - let v = coeffs_one_step - .iter() - .zip(selectors_one_step.iter()) - .map(|(coeffs_one_poly, selectors_one_poly)| { - coeffs_one_poly - .iter() - .zip(selectors_one_poly.iter()) - .map(|(coeffs_one_chunk, selectors_one_chunk)| { - (coeffs_one_chunk.clone(), *selectors_one_chunk) - }) - .collect() - }) - .collect(); - - matrix_coeffs_and_offsets.insert(*step_id, v); - } - self.matrix_coeffs_and_offsets = matrix_coeffs_and_offsets; - } - - pub fn instance(&self, assignments: &Option>) -> HashMap<(usize, UUID), F> { - let mut exposes = HashMap::new(); - if !self.exposed.is_empty() { - for (step_idx, id) in self.exposed.iter() { - if let Some(witness) = assignments { - exposes.insert((*step_idx, *id), witness.get(*step_idx, *id)); - } - } - } - exposes - } - - pub fn generate(&mut self, assignments: Option>) -> Z { - let (assign_pos, n, l) = self.assignments_coeff_offset(&assignments); - let matrix_num = calc_matrix_num(&self.ccs.selectors); - - let (matrices, m) = self.export_matrix_vec(&assignments, n, matrix_num, &assign_pos); - - self.ccs.n = n; - self.ccs.l = l; - self.ccs.m = m; - self.ccs.matrices = matrices; - - create_z_from_assignments(&assignments, &self.instance(&assignments), &assign_pos) - } - - fn assignments_coeff_offset( - &self, - witness: &Option>, - ) -> (AssignmentOffsets, usize, usize) { - let mut public_pos = HashMap::new(); - for (offset, (step_idx, signal_uuid)) in self.exposed.iter().enumerate() { - public_pos.insert((*step_idx, *signal_uuid), offset); - } - - let mut witness_pos = HashMap::new(); - let mut offset = 0; - witness_pos.insert((0, 0), offset); - offset += 1; - if let Some(assignments) = witness.as_ref() { - for (step_idx, (step_id, _)) in assignments.iter().enumerate() { - let signal_uuids = self.witness.get(step_id).unwrap(); - for id in signal_uuids.iter() { - let exist = public_pos.get(&(step_idx, *id)); - if exist.is_none() { - witness_pos.insert((step_idx, *id), offset); - offset += 1; - } - } - } - } - - for ((step_idx, id), v) in public_pos.iter() { - witness_pos.insert((*step_idx, *id), v + offset); - offset += 1; - } - (witness_pos, offset, public_pos.len()) - } - - fn export_matrix_vec( - &self, - witness: &Option>, - n: usize, - num: usize, - assign_pos: &AssignmentOffsets, - ) -> (Vec>, usize) { - let num_poly = witness - .as_ref() - .map(|steps_idx| { - steps_idx - .iter() - .map(|(idx, _)| self.matrix_coeffs_and_offsets.get(idx).unwrap().len()) - .sum() - }) - .unwrap(); - // Initial a vector of Matrices - let mut matrices = vec![Matrix::new(n, num_poly); num]; - let mut row = 0; - - if let Some(steps_id) = witness.as_ref() { - let step_num = steps_id.len(); - for (step_idx, (step_id, _)) in steps_id.iter().enumerate() { - for coeffs_one_poly in self.matrix_coeffs_and_offsets.get(step_id).unwrap().iter() { - if is_last_step_with_next_signal(coeffs_one_poly, step_num, step_idx) { - continue; - } - - for (coeffs_chunks, index) in coeffs_one_poly.iter() { - assert!(*index <= self.ccs.selectors.len()); - let selectors = self.ccs.selectors[*index].clone(); - assert_eq!(coeffs_chunks.len(), selectors.len()); - - for (coeffs, (selector, _)) in coeffs_chunks.iter().zip(selectors.iter()) { - // one row in a matrix - let mut values: Vec<(usize, usize, F)> = Vec::new(); - for (value, signal_id, next) in coeffs.iter() { - let col = if *signal_id == 0 { - assign_pos.get(&(0, 0)) - } else { - let idx = if *next { step_idx + 1 } else { step_idx }; - assign_pos.get(&(idx, *signal_id)) - }; - match col { - None => continue, - Some(col) => values.push((row, *col, *value)), - } - } - // Modify matrices values from steps - matrices[*selector].write(&values); - } - } - row += 1; - } - } - }; - - for matrix in matrices.iter_mut() { - matrix.reduce_rows(row); - } - (matrices, row) - } -} - -fn calc_matrix_num(selectors: &SelectorsOffsetAndCoeff) -> usize { - let mut matrix_num = 0; - for selector in selectors.iter() { - for (idx, _) in selector.iter() { - matrix_num = matrix_num.max(*idx + 1); - } - } - matrix_num -} - -fn is_last_step_with_next_signal( - coeffs_one_poly: &[(CoeffsForProds, usize)], - step_num: usize, - step_idx: usize, -) -> bool { - if step_idx == step_num - 1 { - for (coeffs_for_prods, _) in coeffs_one_poly.iter() { - for (_, _, next) in coeffs_for_prods.concat().iter() { - if *next { - return true; - } - } - } - } - false -} - -#[cfg(test)] -mod tests { - use std::vec; - - use super::*; - use halo2_proofs::halo2curves::bn256::Fr; - - #[test] - fn test_ccs() { - let n = 7; - let m = 4; - let l = 3; - - let m0 = vec![ - (0, 1, Fr::ONE), - (1, 2, Fr::ONE), - (2, 3, Fr::ONE), - (3, 6, Fr::ONE), - ]; - let m1 = vec![ - (0, 1, Fr::ONE), - (1, 2, Fr::ONE), - (2, 4, Fr::ONE), - (3, 6, Fr::ONE), - ]; - let m2 = vec![ - (0, 1, Fr::ONE), - (1, 2, Fr::ONE), - (2, 5, Fr::ONE), - (3, 6, Fr::ONE), - ]; - let m3 = vec![(0, 0, Fr::ONE), (1, 0, Fr::ONE)]; - let m4 = vec![(2, 0, Fr::from(2))]; - let m5 = vec![(2, 0, Fr::from(2))]; - let m6 = vec![ - (0, 0, Fr::ONE.neg()), - (1, 0, Fr::ONE.neg()), - (2, 0, Fr::ONE.neg()), - ]; - let m7 = vec![(0, 0, Fr::ZERO)]; - let matrices = vec![m0, m1, m2, m3, m4, m5, m6, m7]; - - let selectors = vec![ - vec![(3, Fr::ONE), (0, Fr::ONE), (1, Fr::ONE)], - vec![(4, Fr::ONE), (0, Fr::ONE)], - vec![(5, Fr::ONE), (1, Fr::ONE)], - vec![(6, Fr::ONE), (2, Fr::ONE)], - vec![(7, Fr::ONE)], - ]; - let constants: Vec = vec![Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE]; - - let ccs_circuit = create_ccs_circuit(n, m, l, &matrices, &selectors, &constants); - - let z = Z::from_values( - &[Fr::ZERO, Fr::ONE, Fr::from(2)], - &[Fr::from(3), Fr::from(10), Fr::from(43)], - ); - - let result = ccs_circuit.is_satisfied(&z); - - println!("result = {}", result); - } -} diff --git a/src/ccs/ir/mod.rs b/src/ccs/ir/mod.rs index 7858a33c..bab94d0d 100644 --- a/src/ccs/ir/mod.rs +++ b/src/ccs/ir/mod.rs @@ -1,9 +1,17 @@ -use std::{collections::HashMap, fmt::Debug}; - use crate::{field::Field, poly::Expr, util::UUID}; +use std::{collections::HashMap, fmt::Debug, hash::Hash}; pub mod assignments; pub mod circuit; +use assignments::Assignments; +use circuit::CCSCircuit; + +pub type MatrixCoeffsAndOffset = Vec, usize)>>; + +// Use to mark the public inputs +// ((step_idx, step_UUID), assignment_offset) +pub type AssignmentOffsets = HashMap<(usize, u128), usize>; + #[derive(Clone)] pub struct Poly { pub uuid: UUID, @@ -18,7 +26,7 @@ impl Debug for Poly { } } -// (signal_uuid, step_uuid, annotation, pos) +// (signal_uuid, step_uuid, annotation, is_next) pub type PolyExpr = Expr; pub type Coeff = (F, UUID, bool); @@ -174,3 +182,132 @@ fn factor_mul(polys: Vec>) -> PolyExpr { new_polys } + +// Circuit +// matrix_coeffs_and_offsets: Temporarily record all circuit coefficients in each step +// (It is not possible to build a complete circuit until the trace is +// traced, so it is necessary to temporarily record the circuit structure +// in each step.) exposed: All the offsets of public inputs +// witness: All the offsets of the witnesses +// ccs: The ccs circuit +#[derive(Debug)] +pub struct Circuit { + pub ast_id: UUID, + + pub matrix_coeffs_and_offsets: HashMap>, + pub exposed: Vec<(usize, UUID)>, + pub witness: HashMap>, + + pub ccs: CCSCircuit, +} + +impl + Hash> Circuit { + pub fn instance(&self, assignments: &Option>) -> HashMap<(usize, UUID), F> { + let mut exposes = HashMap::new(); + if !self.exposed.is_empty() { + for (step_idx, id) in self.exposed.iter() { + if let Some(witness) = assignments { + exposes.insert((*step_idx, *id), witness.get(*step_idx, *id)); + } + } + } + exposes + } +} +#[cfg(test)] +mod tests { + use std::vec; + + use super::{assignments::Z, circuit::Matrix, *}; + use halo2_proofs::halo2curves::bn256::Fr; + + fn create_ccs_circuit + Hash>( + n: usize, + m: usize, + l: usize, + matrices: &[Vec<(usize, usize, F)>], + selectors: &[Vec<(usize, F)>], + constants: &[F], + ) -> CCSCircuit { + let mut degree = 0; + assert_eq!(selectors.len(), constants.len()); + for selector in selectors.iter() { + for &(s, _) in selector { + assert!(s < matrices.len()) + } + degree = degree.max(selector.len()) + } + let matrices: Vec> = matrices + .iter() + .map(|cells| Matrix::from_values(n, m, cells)) + .collect(); + + CCSCircuit { + n, + m, + t: matrices.len(), + q: constants.len(), + l, + d: degree, + constants: constants.to_vec(), + selectors: selectors.to_vec(), + matrices, + } + } + + #[test] + fn test_ccs() { + let n = 7; + let m = 4; + let l = 3; + + let m0 = vec![ + (0, 1, Fr::ONE), + (1, 2, Fr::ONE), + (2, 3, Fr::ONE), + (3, 6, Fr::ONE), + ]; + let m1 = vec![ + (0, 1, Fr::ONE), + (1, 2, Fr::ONE), + (2, 4, Fr::ONE), + (3, 6, Fr::ONE), + ]; + let m2 = vec![ + (0, 1, Fr::ONE), + (1, 2, Fr::ONE), + (2, 5, Fr::ONE), + (3, 6, Fr::ONE), + ]; + let m3 = vec![(0, 0, Fr::ONE), (1, 0, Fr::ONE)]; + let m4 = vec![(2, 0, Fr::from(2))]; + let m5 = vec![(2, 0, Fr::from(2))]; + let m6 = vec![ + (0, 0, Fr::ONE.neg()), + (1, 0, Fr::ONE.neg()), + (2, 0, Fr::ONE.neg()), + ]; + let m7 = vec![(0, 0, Fr::ZERO)]; + let matrices = vec![m0, m1, m2, m3, m4, m5, m6, m7]; + + let selectors = vec![ + vec![(3, Fr::ONE), (0, Fr::ONE), (1, Fr::ONE)], + vec![(4, Fr::ONE), (0, Fr::ONE)], + vec![(5, Fr::ONE), (1, Fr::ONE)], + vec![(6, Fr::ONE), (2, Fr::ONE)], + vec![(7, Fr::ONE)], + ]; + let constants: Vec = vec![Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE, Fr::ONE]; + + let ccs_circuit = create_ccs_circuit(n, m, l, &matrices, &selectors, &constants); + + let z = Z::from_values( + &[Fr::ZERO, Fr::ONE, Fr::from(2)], + &[Fr::from(3), Fr::from(10), Fr::from(43)], + ); + + let result = ccs_circuit.is_satisfied(&z); + + println!("result = {}", result); + } +}