This commit is contained in:
Dhole
2023-03-20 13:53:19 +07:00
parent 1cad0ff971
commit fc747aab03
5 changed files with 458 additions and 93 deletions

View File

@@ -1,4 +1,5 @@
use crate::expr::{Expr, Var};
use crate::expr::{modinv, mul, neg, Expr, Var};
use std::fmt::{self, Display};
use num_bigint::{BigInt, BigUint, Sign};
use num_traits::{cast::ToPrimitive, One, Zero};
@@ -10,6 +11,15 @@ pub enum Bound {
Set(Vec<BigUint>), // non contiguous set, always sorted
}
impl Display for Bound {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Bound::Range(start, end) => write!(f, "[{},{}]", start, end),
Bound::Set(xs) => write!(f, "{:?}", xs),
}
}
}
impl Bound {
pub fn new<I: IntoIterator<Item = BigUint>>(iter: I) -> Self {
let one = BigUint::from(1u64);
@@ -100,6 +110,18 @@ impl Bound {
false
}
}
pub fn unique(&self) -> Option<&BigUint> {
match self {
Bound::Set(xs) => {
if xs.len() == 1 {
Some(&xs[0])
} else {
None
}
}
_ => None,
}
}
}
#[derive(Debug)]
@@ -134,7 +156,7 @@ pub fn bound_base(p: &BigUint) -> Bound {
}
pub fn find_bounds_poly<V: Var>(e: &Expr<V>, p: &BigUint, analysis: &mut Analysis<V>) {
let (exhaustive, solutions_list) = find_solutions(e);
let (exhaustive, solutions_list) = find_solutions(e, p);
let mut solutions = HashMap::new();
if exhaustive {
for (var, value) in &solutions_list {
@@ -170,6 +192,10 @@ fn find_solutions_base<V: Var>(e: &Expr<V>) -> (bool, Vec<(V, BigInt)>) {
Var(v) => (true, vec![(v.clone(), BigInt::zero())]),
Neg(e) => find_solutions_base(e),
Sum(es) => {
// println!("DBG1");
// for e in es {
// println!(" {}", e);
// }
let mut var: Option<V> = None;
let mut con: Option<BigInt> = None;
let mut neg = false;
@@ -204,10 +230,51 @@ fn find_solutions_base<V: Var>(e: &Expr<V>) -> (bool, Vec<(V, BigInt)>) {
}
}
fn solve_1(a: &BigUint, b: &BigUint, p: &BigUint) -> BigUint {
// a + b*x = 0
// x = -a * inv(b)
mul(neg(a.clone(), p), &modinv(b.clone(), p), p)
}
pub fn find_solution_1<V: Var>(e: &Expr<V>, p: &BigUint) -> Option<(V, BigInt)> {
use Expr::*;
match e {
Mul(xs) => {
if xs.len() != 2 {
return None;
}
if let (Const(b), Var(v)) = (&xs[0], &xs[1]) {
let res = solve_1(&BigUint::zero(), &b, p);
return Some((v.clone(), BigInt::from(res)));
}
return None;
}
Sum(xs) => {
if xs.len() != 2 {
return None;
}
if let (Const(a), Mul(ys)) = (&xs[0], &xs[1]) {
if ys.len() != 2 {
return None;
}
if let (Const(b), Var(v)) = (&ys[0], &ys[1]) {
let res = solve_1(&a, &b, p);
return Some((v.clone(), BigInt::from(res)));
}
}
return None;
}
_ => None,
}
}
// Attempt to find solutions to `e(X) == 0` by matching on the pattern `(x - A)(y - B)...`.
// Returns true when the solutions returned are exhaustive.
pub fn find_solutions<V: Var>(e: &Expr<V>) -> (bool, Vec<(V, BigInt)>) {
pub fn find_solutions<V: Var>(e: &Expr<V>, p: &BigUint) -> (bool, Vec<(V, BigInt)>) {
use Expr::*;
if let Some(solution) = find_solution_1(e, p) {
return (true, vec![solution]);
}
match e {
Mul(es) => {
let mut exhaustive = true;
@@ -235,7 +302,8 @@ mod tests_with_parser {
}
#[test]
fn test_find_solutions() {
fn test_find_solutions00() {
let p = prime();
for (e_str, sol_str, expected_exhaustive) in [
("(a - 5) * (b + 8)", vec![("a", "5"), ("b", "-8")], true),
("(a - 5) * (a - 7)", vec![("a", "5"), ("a", "7")], true),
@@ -257,7 +325,7 @@ mod tests_with_parser {
}
expected_solutions.sort();
let (exhaustive, mut solutions) = find_solutions(&e);
let (exhaustive, mut solutions) = find_solutions(&e, &p);
solutions.sort();
assert_eq!(exhaustive, expected_exhaustive, "{}", e_str);
assert_eq!(solutions, expected_solutions, "{}", e_str);
@@ -265,7 +333,7 @@ mod tests_with_parser {
}
#[test]
fn test_find_bounds_poly() {
fn test_find_bounds_poly00() {
let p = prime();
let poly1 = parse_expr("(a - 0) * (a - 1)").unwrap();
@@ -293,4 +361,13 @@ mod tests_with_parser {
find_bounds_poly(&poly1, &p, &mut analysis);
println!("{:?}", &analysis);
}
#[test]
fn test_find_bounds_poly01() {
let p = prime();
let poly1 = parse_expr("5*(1 - tag[1])").unwrap();
let mut analysis = Analysis::new();
find_bounds_poly(&poly1, &p, &mut analysis);
println!("{:?}", &analysis);
}
}

View File

@@ -308,15 +308,44 @@ fn add(lhs: BigUint, rhs: BigUint, p: &BigUint) -> BigUint {
}
}
fn neg(n: BigUint, p: &BigUint) -> BigUint {
pub(crate) fn neg(n: BigUint, p: &BigUint) -> BigUint {
p - n
}
fn mul(lhs: BigUint, rhs: &BigUint, p: &BigUint) -> BigUint {
pub(crate) fn mul(lhs: BigUint, rhs: &BigUint, p: &BigUint) -> BigUint {
(lhs * rhs).mod_floor(p)
}
pub(crate) fn modinv(n: BigUint, p: &BigUint) -> BigUint {
if p.is_one() {
return BigUint::one();
}
let n = BigInt::from(n);
let p = BigInt::from(p.clone());
let (mut a, mut m, mut x, mut inv) = (n.clone(), p.clone(), BigInt::zero(), BigInt::one());
let one = BigInt::one();
while a > one {
let (div, rem) = a.div_rem(&m);
inv -= div * &x;
a = rem;
std::mem::swap(&mut a, &mut m);
std::mem::swap(&mut x, &mut inv);
}
if inv < BigInt::zero() {
inv += p
}
let (sign, res) = inv.into_parts();
assert_eq!(sign, Sign::Plus);
res
}
impl<V: Var> Expr<V> {
// p is the base field
pub fn eval(&self, p: &BigUint, vars: &HashMap<V, BigUint>) -> BigUint {
use Expr::*;
match self {
@@ -446,6 +475,8 @@ impl<V: Var> Expr<V> {
r.extend(tail.into_iter());
let m = if r.len() == 1 {
r.swap_remove(0)
} else if r.len() == 0 {
Const(BigUint::one())
} else {
Mul(r)
};
@@ -599,6 +630,8 @@ impl<V: Var> Expr<V> {
_ => self,
}
}
/// Simplify the expression in places where it can be partially evaluated
pub fn simplify_move(self, p: &BigUint) -> Self {
let ip = BigInt::from(p.clone());
let e = self._simplify(p, &ip);
@@ -607,6 +640,7 @@ impl<V: Var> Expr<V> {
e
}
/// Simplify the expression in places where it can be partially evaluated
pub fn simplify(&mut self, p: &BigUint) -> &mut Self {
let e = mem::replace(self, Expr::Const(BigUint::zero()));
let e = e.simplify_move(p);
@@ -614,48 +648,168 @@ impl<V: Var> Expr<V> {
self
}
/// Take a list of multiplicands and return a Mul expression whith the multiplication of
/// coefficients evaluated
fn _mul_const(xs: Vec<Expr<V>>, p: &BigUint) -> Expr<V> {
use Expr::*;
let mut mul_const = BigUint::one();
let mut mul_exprs = Vec::new();
for x in xs {
match x {
Const(f) => mul_const = mul(mul_const, &f, p),
e => mul_exprs.push(e),
}
}
if mul_exprs.len() == 0 {
return Const(mul_const);
}
let mut xs = Vec::new();
if !mul_const.is_one() {
xs.push(Const(mul_const))
}
xs.extend_from_slice(&mul_exprs[..]);
Mul(xs)
}
/// Apply "a * b % p" where a and b are expressions. Evaluate coefficient multiplication in
/// the resulting expression.
fn _normalize_mul(a: Expr<V>, b: Expr<V>, p: &BigUint) -> Expr<V> {
use Expr::*;
match (a, b) {
(Const(a), Const(b)) => Const(mul(a, &b, p)),
(Mul(mut xs), Mul(ys)) => {
xs.extend_from_slice(&ys[..]);
Self::_mul_const(xs, p)
}
(e, Mul(xs)) => {
let mut ys = vec![e];
ys.extend_from_slice(&xs[..]);
Self::_mul_const(ys, p)
}
(Mul(mut xs), e) => {
xs.push(e.clone());
Self::_mul_const(xs, p)
}
(a, b) => Mul(vec![a, b]),
}
}
fn _normalize(self, p: &BigUint) -> Self {
use Expr::*;
// p-1 == -1
let p_1 = p.clone() - BigUint::one();
match self {
Neg(e) => Mul(vec![Const(p_1), *e]),
Neg(e) => Mul(vec![Const(p_1), *e])._normalize(p),
Sum(xs) => {
let xs: Vec<Expr<V>> = xs.into_iter().map(|x| x.normalize_move(p)).collect();
let xs = xs.into_iter().map(|x: Expr<V>| x._normalize(p));
let mut sum_const = BigUint::zero();
let mut sum_exprs = Vec::new();
for x in xs {
match x {
Const(f) => sum_const = add(sum_const, f, p),
Sum(xs) => {
for x in xs {
match x {
Const(f) => sum_const = add(sum_const, f, p),
e => sum_exprs.push(e),
}
}
}
e => sum_exprs.push(e),
}
}
let mut xs = Vec::new();
if !sum_const.is_zero() {
xs.push(Const(sum_const))
}
xs.extend_from_slice(&sum_exprs[..]);
Sum(xs)
}
Mul(xs) => {
let mut xs: Vec<Expr<V>> = xs.into_iter().map(|x| x.normalize_move(p)).collect();
xs.sort();
let mut iter = xs.into_iter();
let mul_acc = iter.next().unwrap();
let mut mul_acc = match mul_acc {
Sum(xs) => xs,
e => vec![e],
};
for next in iter {
let e = match next {
Sum(xs) => xs,
e => vec![e],
};
let mut acc = Vec::new();
for a in mul_acc.into_iter() {
for b in &e {
acc.push(Mul(vec![a.clone(), b.clone()]));
// println!("DBG1 {}", Mul(xs.clone()));
let xs = xs.into_iter().map(|x| x._normalize(p));
// flat muls
let mut ys = Vec::new();
for x in xs {
match x {
Mul(xs) => {
ys.extend_from_slice(&xs[..]);
}
_ => ys.push(x),
}
}
let xs = ys;
let mut mul_const = BigUint::one();
let mut mul_vars: Vec<Expr<V>> = Vec::new();
let mut mul_sums: Vec<Vec<Expr<V>>> = Vec::new();
for x in xs {
match x {
Const(f) => mul_const = mul(mul_const, &f, p),
Var(v) => mul_vars.push(Var(v)),
Sum(xs) => mul_sums.push(xs),
_ => {
dbg!(x);
unreachable!();
}
}
mul_acc = acc;
}
Sum(mul_acc)
// println!("DBG4 {:?}", mul_sums);
let mut first = Vec::new();
if !mul_const.is_one() {
first.push(Const(mul_const))
}
first.extend_from_slice(&mul_vars[..]);
while mul_sums.len() >= 2 {
let mut result = Vec::new();
let lhs = &mul_sums[mul_sums.len() - 1];
let rhs = &mul_sums[mul_sums.len() - 2];
for a in lhs {
for b in rhs {
result.push(Self::_normalize_mul(a.clone(), b.clone(), p));
}
}
mul_sums.pop();
let last_index = mul_sums.len() - 1;
mul_sums[last_index] = result;
}
if mul_sums.len() > 0 {
for e in mul_sums[0].iter_mut() {
*e = Self::_normalize_mul(Mul(first.clone()), e.clone(), p);
}
// println!("DBG2 {}", Sum(mul_sums[0].clone()));
Sum(mul_sums.pop().unwrap())
} else {
// println!("DBG3 {}", Mul(first.clone()));
Self::_mul_const(first, p)
}
}
Pow(e, f) => {
let e = e._normalize(p);
match e {
Const(b) => {
let mut res = BigUint::one();
let exp: u32 = f.to_u32().expect("exponent too big");
// TODO: Implement efficient expmod
for _ in 0..exp {
res = mul(res, &b, p);
}
Const(res)
}
e => Pow(Box::new(e), f),
}
}
_ => self,
}
}
/// Return the expression in coefficient form
pub fn normalize_move(self, p: &BigUint) -> Self {
self.simplify_move(p)._normalize(p).simplify_move(p)
self.simplify_move(p)._normalize(p)
}
/// Return the expression in coefficient form
pub fn normalize(&mut self, p: &BigUint) -> &mut Self {
let e = mem::replace(self, Expr::Const(BigUint::zero()));
let e = e.normalize_move(p);
@@ -684,6 +838,7 @@ impl<V: Var> Expr<V> {
}
// TODO: Document the probability of success
// Using SchwartzZippel lemma
pub fn test_eq<R: Rng>(&self, rng: &mut R, other: &Self) -> bool {
let p = BigUint::parse_bytes(b"100000000000000000000000000000000", 16).unwrap()
- BigUint::from(159u64);
@@ -899,7 +1054,7 @@ mod tests {
}
#[test]
fn test_normalize() {
fn test_normalize00() {
let p = BigUint::from(0x10000u64 - 15);
let vars: HashMap<&'static str, BigUint> = {
let mut rng = ChaCha20Rng::seed_from_u64(0);
@@ -996,4 +1151,25 @@ mod tests_with_parser {
let s2 = format!("{}", e2);
assert_eq!(s2, e2_str);
}
#[test]
fn test_normalize01() {
let p = prime();
let e1_str = "1*5*(1 - (1 - (1 - 0)*(1 - tag[1]))*(1 - 0))";
// let e1_str = "1 - (1 - tag[1])";
let e1 = parse_expr(e1_str).unwrap();
let e2 = e1.normalize_move(&p);
// let e2 = e2.normalize_move(&p);
println!("{}", e2);
}
#[test]
fn test_simplify01() {
let p = prime();
let e1_str = "(1 - 0)*1";
let e1 = parse_expr(e1_str).unwrap();
let e2 = e1.simplify_move(&p);
println!("{:?}", e2);
println!("{}", e2);
}
}

View File

@@ -27,26 +27,22 @@ lazy_static! {
pub fn parse_expr_pairs(expression: Pairs<Rule>) -> Ex {
use Expr::*;
PRATT_PARSER
.map_primary(|primary| {
dbg!(&primary);
match primary.as_rule() {
Rule::dec => (
Const(BigUint::parse_bytes(primary.as_str().as_bytes(), 10).unwrap()),
true,
),
Rule::hex => (
Const(BigUint::parse_bytes(primary.as_str().as_bytes(), 16).unwrap()),
true,
),
Rule::var => (Var(primary.as_str().to_string()), true),
Rule::neg => (Neg(Box::new(parse_expr_pairs(primary.into_inner()))), true),
Rule::expr => (parse_expr_pairs(primary.into_inner()), false),
_ => unreachable!(),
}
.map_primary(|primary| match primary.as_rule() {
Rule::dec => (
Const(BigUint::parse_bytes(primary.as_str().as_bytes(), 10).unwrap()),
true,
),
Rule::hex => (
Const(BigUint::parse_bytes(primary.as_str().as_bytes(), 16).unwrap()),
true,
),
Rule::var => (Var(primary.as_str().to_string()), true),
Rule::neg => (Neg(Box::new(parse_expr_pairs(primary.into_inner()))), true),
Rule::expr => (parse_expr_pairs(primary.into_inner()), false),
_ => unreachable!(),
})
// lcont and rcont tell wether the lhs and rhs terms belong to the same expr or not
.map_infix(|(lhs, lcont), op, (rhs, rcont)| {
dbg!(&lhs, &op, &rhs);
(
match op.as_rule() {
Rule::add => match (lhs, rhs, lcont & rcont) {

View File

@@ -1,6 +1,7 @@
use crate::expr::{self, Column, ColumnKind, Expr, PlonkVar as Var};
use num_bigint::BigUint;
use num_traits::Zero;
use std::collections::HashMap;
use std::fmt::{self, Debug, Display, Write};
pub mod backends;
@@ -22,7 +23,7 @@ pub struct Witness {
num_rows: usize,
columns: Vec<ColumnWitness>,
// The advice cells in the circuit, arranged as [column][row].
witness: Vec<Vec<Option<BigUint>>>,
pub witness: Vec<Vec<Option<BigUint>>>,
}
/// Adaptor struct to format the witness columns assignments as CSV
@@ -68,7 +69,7 @@ impl ColumnWitness {
phase,
}
}
fn name(&self) -> &String {
pub fn name(&self) -> &String {
self.aliases.get(0).unwrap_or(&self.name)
}
}
@@ -86,7 +87,7 @@ impl ColumnFixed {
aliases: Vec::new(),
}
}
fn name(&self) -> &String {
pub fn name(&self) -> &String {
self.aliases.get(0).unwrap_or(&self.name)
}
}
@@ -104,7 +105,7 @@ impl ColumnPublic {
aliases: Vec::new(),
}
}
fn name(&self) -> &String {
pub fn name(&self) -> &String {
self.aliases.get(0).unwrap_or(&self.name)
}
}
@@ -139,7 +140,7 @@ pub struct Columns {
pub public: Vec<ColumnPublic>,
}
/// Polynomial constraint
/// Polynomial identity constraint
#[derive(Debug, Clone)]
pub struct Poly {
pub name: String,
@@ -163,7 +164,7 @@ pub struct CopyC {
/// Circuit general information
#[derive(Debug, Default, Clone)]
pub struct Info {
/// Field modulus
/// Field modulus / Size of the field
pub p: BigUint,
/// Number of rows. This is always a power of 2 in halo2.
pub num_rows: usize,
@@ -276,38 +277,6 @@ impl Plaf {
}
}
pub fn resolve(&self, e: &Expr<Var>, offset: usize) -> Expr<Cell> {
use Expr::*;
match e {
Neg(e) => Neg(Box::new(self.resolve(e, offset))),
Const(f) => Const(f.clone()),
Var(v) => match v {
expr::PlonkVar::ColumnQuery { column, rotation } => {
let offset =
(offset as i32 + rotation).rem_euclid(self.info.num_rows as i32) as usize;
match column.kind {
ColumnKind::Fixed => Const(
self.fixed[column.index][offset]
.clone()
.unwrap_or_else(BigUint::zero),
),
_ => Var(Cell {
column: *column,
offset,
}),
}
}
expr::PlonkVar::Challenge { index: _, phase: _ } => {
// TODO: Figure out something better :P
Const(BigUint::from(1234u64))
}
},
Sum(es) => Sum(es.iter().map(|e| self.resolve(e, offset)).collect()),
Mul(es) => Mul(es.iter().map(|e| self.resolve(e, offset)).collect()),
Pow(e, f) => Pow(Box::new(self.resolve(e, offset)), *f),
}
}
/// Simplify expressions in polynomial constraints and lookups.
pub fn simplify(&mut self) {
let p = &self.info.p;
@@ -323,8 +292,159 @@ impl Plaf {
}
}
}
pub fn alias_map(&self) -> AliasMap {
let mut map = HashMap::new();
for (index, challenge) in self.info.challenges.iter().enumerate() {
let var = Var::Challenge {
index,
phase: challenge.phase,
};
map.insert(challenge.name.clone(), var.clone());
if let Some(alias) = &challenge.alias {
map.insert(alias.clone(), var.clone());
}
}
for (index, column) in self.columns.witness.iter().enumerate() {
let var = Var::ColumnQuery {
column: Column {
kind: ColumnKind::Witness,
index,
},
rotation: 0,
};
map.insert(column.name.clone(), var.clone());
for alias in &column.aliases {
map.insert(alias.clone(), var.clone());
}
}
for (index, column) in self.columns.fixed.iter().enumerate() {
let var = Var::ColumnQuery {
column: Column {
kind: ColumnKind::Fixed,
index,
},
rotation: 0,
};
map.insert(column.name.clone(), var.clone());
for alias in &column.aliases {
map.insert(alias.clone(), var.clone());
}
}
for (index, column) in self.columns.public.iter().enumerate() {
let var = Var::ColumnQuery {
column: Column {
kind: ColumnKind::Public,
index,
},
rotation: 0,
};
map.insert(column.name.clone(), var.clone());
for alias in &column.aliases {
map.insert(alias.clone(), var.clone());
}
}
AliasMap(map)
}
pub fn gen_empty_witness(&self) -> Witness {
let mut witness = Vec::with_capacity(self.columns.witness.len());
for i in 0..self.columns.witness.len() {
witness.push(vec![None; self.info.num_rows]);
}
Witness {
num_rows: self.info.num_rows,
columns: self.columns.witness.clone(),
witness,
}
}
pub fn eval_partial<F, V>(&self, e: &Expr<V>, eval_var: &F, offset: usize) -> Expr<Cell>
where
V: expr::Var,
F: Fn(&V, usize) -> Expr<Cell>,
{
use Expr::*;
match e {
Neg(e) => Neg(Box::new(self.eval_partial(e, eval_var, offset))),
Const(f) => Const(f.clone()),
Var(v) => eval_var(v, offset),
Sum(es) => Sum(es
.iter()
.map(|e| self.eval_partial(e, eval_var, offset))
.collect()),
Mul(es) => Mul(es
.iter()
.map(|e| self.eval_partial(e, eval_var, offset))
.collect()),
Pow(e, f) => Pow(Box::new(self.eval_partial(e, eval_var, offset)), *f),
}
}
pub fn resolve(&self, e: &Expr<Var>, offset: usize) -> Expr<Cell> {
self.eval_partial(
e,
&|v: &Var, offset: usize| -> Expr<Cell> {
match v {
expr::PlonkVar::ColumnQuery { column, rotation } => {
let offset = (offset as i32 + rotation)
.rem_euclid(self.info.num_rows as i32)
as usize;
match column.kind {
ColumnKind::Fixed => Expr::Const(
self.fixed[column.index][offset]
.clone()
.unwrap_or_else(BigUint::zero),
),
_ => Expr::Var(Cell {
column: *column,
offset,
}),
}
}
expr::PlonkVar::Challenge { index: _, phase: _ } => {
// TODO: Figure out something better :P
Expr::Const(BigUint::from(1234u64))
}
}
},
offset,
)
}
pub fn _eval_partial(&self, e: &Expr<Cell>, witness: &Witness, offset: usize) -> Expr<Cell> {
use Expr::*;
match e {
Neg(e) => Neg(Box::new(self._eval_partial(e, witness, offset))),
Const(f) => Const(f.clone()),
Var(v) => {
let Cell { column, offset } = v;
match column.kind {
ColumnKind::Witness => {
if let Some(f) = &witness.witness[column.index][*offset] {
Const(f.clone())
} else {
e.clone()
}
}
_ => e.clone(),
}
}
Sum(es) => Sum(es
.iter()
.map(|e| self._eval_partial(e, witness, offset))
.collect()),
Mul(es) => Mul(es
.iter()
.map(|e| self._eval_partial(e, witness, offset))
.collect()),
Pow(e, f) => Pow(Box::new(self._eval_partial(e, witness, offset)), *f),
}
}
}
pub struct AliasMap(pub HashMap<String, Var>);
/// Adaptor struct to format the fixed columns assignments as CSV
pub struct PlafDisplayFixedCSV<'a>(pub &'a Plaf);
@@ -361,6 +481,7 @@ impl Display for PlafDisplayBaseTOML<'_> {
let this = self.0;
writeln!(f, "[info]")?;
writeln!(f, "num_rows = {}", this.info.num_rows)?;
writeln!(f, "p = {}", this.info.p)?;
writeln!(f)?;
writeln!(f, "[info.challenges]")?;

View File

@@ -379,20 +379,15 @@ pub fn gen_witness<F: Field + PrimeField<Repr = [u8; 32]>, ConcreteCircuit: Circ
cs.constants().clone(),
)?;
let mut witness = Witness {
num_rows: n,
columns: plaf.columns.witness.clone(),
witness: vec![vec![]; plaf.columns.witness.len()],
};
let mut witness = plaf.gen_empty_witness();
for i in 0..plaf.columns.witness.len() {
let mut column = vec![None; n];
let column = &mut witness.witness[i];
for (j, cell) in assembly.advice[i].iter().enumerate() {
if let CellValue::Assigned(v) = cell {
column[j] = Some(BigUint::from_bytes_le(&v.to_repr()[..]));
}
}
witness.witness[i] = column;
}
Ok(witness)