zinc_piop/sumcheck/
prover.rs1use crypto_primitives::{SemiringConfig, SetConfig};
4use itertools::Itertools;
5#[cfg(feature = "parallel")]
6use rayon::iter::*;
7use std::slice;
8use zinc_poly::mle::{DenseMultilinearExtension, MultilinearExtension};
9use zinc_transcript::{delegate_transcribable, traits::ConstTranscribable};
10use zinc_utils::{cfg_into_iter, cfg_iter_mut};
11
12#[repr(transparent)]
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct NatEvaluatedPolyWithoutConstant<F> {
16 pub tail_evaluations: Vec<F>,
18}
19
20impl<F> NatEvaluatedPolyWithoutConstant<F> {
21 pub fn new(tail_evaluations: Vec<F>) -> Self {
22 Self { tail_evaluations }
23 }
24
25 pub fn try_map<T, E>(
29 &self,
30 f: impl FnMut(&F) -> Result<T, E> + Copy,
31 ) -> Result<NatEvaluatedPolyWithoutConstant<T>, E> {
32 Ok(NatEvaluatedPolyWithoutConstant {
33 tail_evaluations: self.tail_evaluations.iter().map(f).try_collect()?,
34 })
35 }
36}
37
38impl<F> std::ops::Deref for NatEvaluatedPolyWithoutConstant<F> {
39 type Target = [F];
40
41 fn deref(&self) -> &Self::Target {
42 &self.tail_evaluations
43 }
44}
45
46impl<F> std::ops::DerefMut for NatEvaluatedPolyWithoutConstant<F> {
47 fn deref_mut(&mut self) -> &mut Self::Target {
48 &mut self.tail_evaluations
49 }
50}
51
52delegate_transcribable!(NatEvaluatedPolyWithoutConstant<F> { tail_evaluations: Vec<F> }
53 where F: ConstTranscribable);
54
55#[repr(transparent)]
56#[derive(Clone, Debug, PartialEq, Eq)]
57pub struct ProverMsg<F>(pub NatEvaluatedPolyWithoutConstant<F>);
58
59delegate_transcribable!(ProverMsg<F>(NatEvaluatedPolyWithoutConstant<F>)
60 where F: ConstTranscribable);
61
62impl<F> ProverMsg<F> {
63 pub fn try_map<T, E>(
67 &self,
68 f: impl FnMut(&F) -> Result<T, E> + Copy,
69 ) -> Result<ProverMsg<T>, E> {
70 Ok(ProverMsg(self.0.try_map(f)?))
71 }
72}
73
74pub struct ProverState<C: SetConfig> {
76 pub randomness: Vec<C::Element>,
78 pub mles: Vec<DenseMultilinearExtension<C::Element>>,
81 pub num_vars: usize,
83 pub max_degree: usize,
85 pub round: usize,
87 pub asserted_sum: Option<C::Element>,
89 pub skip_next_fold: bool,
95}
96
97impl<C: SetConfig> ProverState<C> {
98 pub fn new(
101 mles: Vec<DenseMultilinearExtension<C::Element>>,
102 nvars: usize,
103 degree: usize,
104 ) -> Self {
105 Self {
106 randomness: Vec::with_capacity(nvars),
107 mles,
108 num_vars: nvars,
109 max_degree: degree,
110 round: 0,
111 asserted_sum: None,
112 skip_next_fold: false,
113 }
114 }
115}
116
117impl<C: SemiringConfig> ProverState<C> {
118 #[allow(clippy::arithmetic_side_effects)]
124 pub fn prove_round(
125 &mut self,
126 v_msg: &Option<C::Element>,
127 comb_fn: impl Fn(&[C::Element]) -> C::Element + Send + Sync,
128 config: &C,
129 ) -> ProverMsg<C::Element> {
130 if let Some(msg) = v_msg {
131 if self.round == 0 {
132 panic!("first round should be prover first.");
133 }
134 self.randomness.push(msg.clone());
135
136 if self.skip_next_fold {
137 self.skip_next_fold = false;
139 } else {
140 let i = self.round;
142 let r = self.randomness[i - 1].clone();
143
144 cfg_iter_mut!(self.mles).for_each(|multiplicand| {
145 multiplicand.fix_variables(config, slice::from_ref(&r));
146 });
147 }
148 } else if self.round > 0 {
149 panic!("verifier message is empty");
150 }
151
152 self.round += 1;
153
154 if self.round > self.num_vars {
155 panic!("Prover is not active");
156 }
157
158 let i = self.round;
159 let nv = self.num_vars;
160 let degree = self.max_degree;
161
162 let polys = &self.mles;
163
164 struct Scratch<R> {
165 evals: Vec<R>,
166 steps: Vec<R>,
167 vals0: Vec<R>,
168 vals1: Vec<R>,
169 vals: Vec<R>,
170 levals: Vec<R>,
171 }
172 let zero = config.zero();
173 let zero_vec_deg = vec![zero.clone(); degree + 1];
174 let zero_vec_poly = vec![zero.clone(); polys.len()];
175 let scratch = || Scratch {
176 evals: zero_vec_deg.clone(),
177 steps: zero_vec_poly.clone(),
178 vals0: zero_vec_poly.clone(),
179 vals1: zero_vec_poly.clone(),
180 vals: zero_vec_poly.clone(),
181 levals: zero_vec_deg.clone(),
182 };
183
184 #[cfg(not(feature = "parallel"))]
185 let zeros = scratch();
186 #[cfg(feature = "parallel")]
187 let zeros = scratch;
188
189 let summer = cfg_into_iter!(0..1 << (nv - i)).fold(zeros, |mut s, b| {
190 let index = b << 1;
191
192 s.vals0
205 .iter_mut()
206 .zip(polys.iter())
207 .for_each(|(v0, poly)| *v0 = poly[index].clone());
208 s.levals[0] = comb_fn(&s.vals0);
209
210 if degree > 0 {
211 s.vals1
212 .iter_mut()
213 .zip(polys.iter())
214 .for_each(|(v1, poly)| *v1 = poly[index + 1].clone());
215 s.levals[1] = comb_fn(&s.vals1);
216
217 for (i, (v1, v0)) in s.vals1.iter().zip(s.vals0.iter()).enumerate() {
218 s.steps[i] = config.sub(v1, v0);
219 s.vals[i] = v1.clone();
220 }
221
222 for eval_point in s.levals.iter_mut().take(degree + 1).skip(2) {
223 for poly_i in 0..polys.len() {
224 config.add_assign(&mut s.vals[poly_i], &s.steps[poly_i]);
225 }
226 *eval_point = comb_fn(&s.vals);
227 }
228 }
229
230 s.evals
236 .iter_mut()
237 .zip(s.levals.iter())
238 .for_each(|(e, l)| config.add_assign(e, l));
239
240 s
241 });
242
243 #[cfg(feature = "parallel")]
245 let evaluations = summer.map(|s| s.evals).reduce(
246 || vec![zero.clone(); degree + 1],
247 |mut evaluations, evals| {
248 evaluations
249 .iter_mut()
250 .zip(evals)
251 .for_each(|(e, l)| config.add_assign(e, &l));
252 evaluations
253 },
254 );
255
256 #[cfg(not(feature = "parallel"))]
257 let evaluations = summer.evals;
258
259 if self.round == 1 {
261 let p0 = evaluations
262 .first()
263 .expect("evaluations should always contain the constant term");
264 let sum = if degree > 0 {
265 let eval = evaluations
266 .get(1)
267 .expect("degree > 0 implies evaluation at 1 is present");
268 config.add(p0, eval)
269 } else {
270 p0.clone()
271 };
272 self.asserted_sum = Some(sum);
273 }
274
275 let mut tail = evaluations;
277 tail.remove(0); ProverMsg(NatEvaluatedPolyWithoutConstant::new(tail))
280 }
281}