1use crate::{
5 columns::ExtendedFoldingColumn,
6 error_term::{eval_sided, ExtendedEnv, Side},
7 expressions::{Degree, FoldingExp},
8 FoldingConfig,
9};
10use std::collections::{BTreeMap, HashMap, VecDeque};
11
12pub struct Quadraticized<C: FoldingConfig> {
13 pub original_constraints: Vec<FoldingExp<C>>,
14 pub extra_constraints: Vec<FoldingExp<C>>,
15 pub extended_witness_generator: ExtendedWitnessGenerator<C>,
16}
17
18pub fn quadraticize<C: FoldingConfig>(
21 constraints: Vec<FoldingExp<C>>,
22) -> (Quadraticized<C>, usize) {
23 let mut recorder = ExpRecorder::new();
24 let original_constraints = constraints
25 .into_iter()
26 .map(|exp| lower_degree_to_2(exp, &mut recorder))
27 .collect();
28 let added_columns = recorder.next;
29 let (extra_constraints, exprs) = recorder.into_constraints();
30 (
31 Quadraticized {
32 original_constraints,
33 extra_constraints,
34 extended_witness_generator: ExtendedWitnessGenerator { exprs },
35 },
36 added_columns,
37 )
38}
39
40struct ExpRecorder<C: FoldingConfig> {
42 recorded_exprs: HashMap<FoldingExp<C>, usize>,
43 pub(crate) next: usize,
44}
45
46impl<C: FoldingConfig> ExpRecorder<C> {
47 fn new() -> Self {
48 Self {
49 recorded_exprs: Default::default(),
50 next: 0,
51 }
52 }
53
54 fn get_id(&mut self, exp: FoldingExp<C>) -> usize {
55 *self.recorded_exprs.entry(exp).or_insert_with(|| {
56 let id = self.next;
57 self.next += 1;
58 id
59 })
60 }
61
62 #[allow(clippy::type_complexity)]
63 fn into_constraints(self) -> (Vec<FoldingExp<C>>, VecDeque<(usize, FoldingExp<C>)>) {
64 let ExpRecorder { recorded_exprs, .. } = self;
65 let mut witness_generator = VecDeque::with_capacity(recorded_exprs.len());
66 let mut new_constraints = BTreeMap::new();
67 for (exp, id) in recorded_exprs.into_iter() {
68 let left = FoldingExp::Atom(ExtendedFoldingColumn::WitnessExtended(id));
69 let constraint = FoldingExp::Sub(Box::new(left), Box::new(exp.clone()));
70 new_constraints.insert(id, constraint);
71 witness_generator.push_front((id, exp));
72 }
73 (new_constraints.into_values().collect(), witness_generator)
74 }
75}
76
77impl<C: FoldingConfig> FoldingExp<C> {
78 fn degree(&self) -> usize {
79 match self {
80 e @ FoldingExp::Atom(_) => match e.folding_degree() {
81 Degree::Zero => 0,
82 Degree::One => 1,
83 Degree::Two => 2,
84 },
85 FoldingExp::Double(exp) => exp.degree(),
86 FoldingExp::Square(exp) => exp.degree() * 2,
87 FoldingExp::Add(e1, e2) | FoldingExp::Sub(e1, e2) => {
88 core::cmp::max(e1.degree(), e2.degree())
89 }
90 FoldingExp::Mul(e1, e2) => e1.degree() + e2.degree(),
91 FoldingExp::Pow(e, i) => e.degree() * (*i as usize),
92 }
93 }
94}
95
96fn lower_degree_to_1<C: FoldingConfig>(
97 exp: FoldingExp<C>,
98 rec: &mut ExpRecorder<C>,
99) -> FoldingExp<C> {
100 let degree = exp.degree();
101 match degree {
102 0 => exp,
103 1 => exp,
104 _ => match exp {
105 FoldingExp::Add(e1, e2) => FoldingExp::Add(
106 Box::new(lower_degree_to_1(*e1, rec)),
107 Box::new(lower_degree_to_1(*e2, rec)),
108 ),
109 FoldingExp::Sub(e1, e2) => FoldingExp::Sub(
110 Box::new(lower_degree_to_1(*e1, rec)),
111 Box::new(lower_degree_to_1(*e2, rec)),
112 ),
113 e @ FoldingExp::Square(_) | e @ FoldingExp::Mul(_, _) => {
114 let exp = lower_degree_to_2(e, rec);
115 let id = rec.get_id(exp);
116 FoldingExp::Atom(ExtendedFoldingColumn::WitnessExtended(id))
117 }
118 FoldingExp::Double(exp) => FoldingExp::Double(Box::new(lower_degree_to_1(*exp, rec))),
119 _ => todo!(),
120 },
121 }
122}
123
124fn lower_degree_to_2<C: FoldingConfig>(
125 exp: FoldingExp<C>,
126 rec: &mut ExpRecorder<C>,
127) -> FoldingExp<C> {
128 use FoldingExp::*;
129 let degree = exp.degree();
130 if degree <= 2 {
131 return exp;
132 }
133
134 match exp {
135 FoldingExp::Atom(_) => panic!("a column shouldn't be above degree 1"),
136 FoldingExp::Double(exp) => Double(Box::new(lower_degree_to_2(*exp, rec))),
137 FoldingExp::Square(exp) => Square(Box::new(lower_degree_to_1(*exp, rec))),
138 FoldingExp::Add(e1, e2) => {
139 let e1 = lower_degree_to_2(*e1, rec);
140 let e2 = lower_degree_to_2(*e2, rec);
141 Add(Box::new(e1), Box::new(e2))
142 }
143 FoldingExp::Sub(e1, e2) => {
144 let e1 = lower_degree_to_2(*e1, rec);
145 let e2 = lower_degree_to_2(*e2, rec);
146 Sub(Box::new(e1), Box::new(e2))
147 }
148 FoldingExp::Mul(e1, e2) => {
149 let d1 = e1.degree();
150 let d2 = e2.degree();
151 assert_eq!(degree, d1 + d2);
152 let (e1, e2) = (*e1, *e2);
153 let (e1, e2) = match (d1, d2) {
154 (0, _) => (e1, lower_degree_to_2(e2, rec)),
155 (_, 0) => (lower_degree_to_2(e1, rec), e2),
156 (_, _) => (lower_degree_to_1(e1, rec), lower_degree_to_1(e2, rec)),
157 };
158 Mul(Box::new(e1), Box::new(e2))
159 }
160 exp @ FoldingExp::Pow(_, 0) => exp,
161 FoldingExp::Pow(e, 1) => lower_degree_to_2(*e, rec),
162 FoldingExp::Pow(e, 2) => FoldingExp::Pow(Box::new(lower_degree_to_1(*e, rec)), 2),
163 FoldingExp::Pow(e, i) => {
164 let e = lower_degree_to_1(*e, rec);
165 let mut acc = e.clone();
166 for _ in 1..i - 1 {
167 acc = lower_degree_to_1(FoldingExp::Mul(Box::new(e.clone()), Box::new(acc)), rec);
168 }
169 FoldingExp::Mul(Box::new(e.clone()), Box::new(acc))
170 }
171 }
172}
173
174pub struct ExtendedWitnessGenerator<C: FoldingConfig> {
175 exprs: VecDeque<(usize, FoldingExp<C>)>,
176}
177
178impl<C: FoldingConfig> ExtendedWitnessGenerator<C> {
179 pub(crate) fn compute_extended_witness(
180 &self,
181 mut env: ExtendedEnv<C>,
182 side: Side,
183 ) -> ExtendedEnv<C> {
184 if env.needs_extension(side) {
185 let mut pending = self.exprs.clone();
186
187 while let Some((i, exp)) = pending.pop_front() {
188 if check_evaluable(&exp, &env, side) {
189 let evals = eval_sided(&exp, &env, side).unwrap();
190 env.add_witness_evals(i, evals, side);
191 } else {
192 pending.push_back((i, exp))
193 }
194 }
195 }
196
197 env
198 }
199}
200
201fn check_evaluable<C: FoldingConfig>(
203 exp: &FoldingExp<C>,
204 env: &ExtendedEnv<C>,
205 side: Side,
206) -> bool {
207 match exp {
208 FoldingExp::Atom(col) => env.col_try(col, side),
209 FoldingExp::Double(e) | FoldingExp::Square(e) => check_evaluable(e, env, side),
210 FoldingExp::Add(e1, e2) | FoldingExp::Sub(e1, e2) | FoldingExp::Mul(e1, e2) => {
211 check_evaluable(e1, env, side) && check_evaluable(e2, env, side)
212 }
213 FoldingExp::Pow(e, _) => check_evaluable(e, env, side),
214 }
215}