1#[cfg(feature = "parallel")]
2use rayon::prelude::*;
3
4use crypto_primitives::{FromWithConfig, PrimeField, Semiring};
5use std::{collections::HashMap, iter};
6use zinc_poly::{
7 EvaluationError,
8 mle::DenseMultilinearExtension,
9 univariate::dynamic::over_field::{DynamicPolyFInnerProduct, DynamicPolynomialF},
10};
11use zinc_uair::{Uair, UairTrace, collect_scalars::collect_scalars};
12use zinc_utils::{
13 UNCHECKED, cfg_extend, cfg_into_iter, cfg_iter, cfg_iter_mut, inner_product::InnerProduct,
14 powers,
15};
16
17pub type RowMajorTrace<F> = Vec<Vec<DynamicPolynomialF<F>>>;
21
22pub type ColumnMajorTrace<F> = Vec<DenseMultilinearExtension<DynamicPolynomialF<F>>>;
26
27#[derive(Clone, Debug)]
30pub enum ProjectedTrace<F: PrimeField> {
31 RowMajor(RowMajorTrace<F>),
32 ColumnMajor(ColumnMajorTrace<F>),
33}
34
35#[allow(clippy::arithmetic_side_effects)]
41pub fn project_trace_coeffs_row_major<F, PolyCoeff, Int, const DEGREE_PLUS_ONE: usize>(
42 trace: &UairTrace<PolyCoeff, Int, DEGREE_PLUS_ONE>,
43 field_cfg: &F::Config,
44) -> RowMajorTrace<F>
45where
46 F: for<'a> FromWithConfig<&'a PolyCoeff> + for<'a> FromWithConfig<&'a Int> + Send + Sync,
47 PolyCoeff: Clone + Send + Sync,
48 Int: Clone + Send + Sync,
49{
50 let zero = F::zero_with_cfg(field_cfg);
51 let one = F::one_with_cfg(field_cfg);
52
53 let binary_len = trace.binary_poly.len();
54 let arbitrary_len = trace.arbitrary_poly.len();
55 let num_cols = trace.binary_poly.len() + trace.arbitrary_poly.len() + trace.int.len();
56
57 let num_rows = trace
59 .binary_poly
60 .first()
61 .map(|c| c.len())
62 .or_else(|| trace.arbitrary_poly.first().map(|c| c.len()))
63 .or_else(|| trace.int.first().map(|c| c.len()))
64 .unwrap_or(0);
65
66 let mut result: RowMajorTrace<F> = iter::repeat_with(|| Vec::with_capacity(num_cols))
69 .take(num_rows)
70 .collect();
71
72 cfg_iter_mut!(result)
74 .enumerate()
75 .for_each(|(row_idx, row)| {
76 let spare = row.spare_capacity_mut();
77
78 cfg_iter_mut!(spare[..binary_len])
80 .zip(cfg_iter!(trace.binary_poly))
81 .for_each(|(slot, col)| {
82 let binary_poly = &col.evaluations[row_idx];
83 slot.write(
84 binary_poly
85 .iter()
86 .map(|coeff| {
87 if coeff.into_inner() {
88 one.clone()
89 } else {
90 zero.clone()
91 }
92 })
93 .collect(),
94 );
95 });
96
97 cfg_iter_mut!(spare[binary_len..binary_len + arbitrary_len])
99 .zip(cfg_iter!(trace.arbitrary_poly))
100 .for_each(|(slot, col)| {
101 let arbitrary_poly = &col.evaluations[row_idx];
102 slot.write(
103 arbitrary_poly
104 .iter()
105 .map(|coeff| F::from_with_cfg(coeff, field_cfg))
106 .collect(),
107 );
108 });
109
110 cfg_iter_mut!(spare[binary_len + arbitrary_len..])
112 .zip(cfg_iter!(trace.int))
113 .for_each(|(slot, col)| {
114 let int_val = &col.evaluations[row_idx];
115 slot.write(DynamicPolynomialF {
116 coeffs: vec![F::from_with_cfg(int_val, field_cfg)],
117 });
118 });
119
120 unsafe { row.set_len(num_cols) };
122 });
123 result
124}
125
126#[allow(clippy::arithmetic_side_effects)]
132pub fn project_trace_coeffs_column_major<F, PolyCoeff, Int, const DEGREE_PLUS_ONE: usize>(
133 trace: &UairTrace<PolyCoeff, Int, DEGREE_PLUS_ONE>,
134 field_cfg: &F::Config,
135) -> ColumnMajorTrace<F>
136where
137 F: for<'a> FromWithConfig<&'a PolyCoeff> + for<'a> FromWithConfig<&'a Int> + Send + Sync,
138 PolyCoeff: Clone + Send + Sync,
139 Int: Clone + Send + Sync,
140{
141 let zero = F::zero_with_cfg(field_cfg);
142 let one = F::one_with_cfg(field_cfg);
143
144 let num_vars = [
145 trace.binary_poly.first().map(|c| c.num_vars),
146 trace.arbitrary_poly.first().map(|c| c.num_vars),
147 trace.int.first().map(|c| c.num_vars),
148 ]
149 .into_iter()
150 .flatten()
151 .max()
152 .unwrap_or(0);
153
154 let mut result =
155 Vec::with_capacity(trace.binary_poly.len() + trace.arbitrary_poly.len() + trace.int.len());
156
157 cfg_extend!(
159 result,
160 cfg_iter!(trace.binary_poly).map(|column| {
161 let evaluations: Vec<DynamicPolynomialF<F>> = column
162 .iter()
163 .map(|binary_poly| {
164 binary_poly
165 .iter()
166 .map(|coeff| {
167 if coeff.into_inner() {
168 one.clone()
169 } else {
170 zero.clone()
171 }
172 })
173 .collect()
174 })
175 .collect();
176 DenseMultilinearExtension {
177 evaluations,
178 num_vars,
179 }
180 })
181 );
182
183 cfg_extend!(
185 result,
186 cfg_iter!(trace.arbitrary_poly).map(|column| {
187 let evaluations: Vec<DynamicPolynomialF<F>> = column
188 .iter()
189 .map(|arbitrary_poly| {
190 arbitrary_poly
191 .iter()
192 .map(|coeff| F::from_with_cfg(coeff, field_cfg))
193 .collect()
194 })
195 .collect();
196 DenseMultilinearExtension {
197 evaluations,
198 num_vars,
199 }
200 })
201 );
202
203 cfg_extend!(
205 result,
206 cfg_iter!(trace.int).map(|column| {
207 let evaluations: Vec<DynamicPolynomialF<F>> = column
208 .iter()
209 .map(|int| DynamicPolynomialF {
210 coeffs: vec![F::from_with_cfg(int, field_cfg)],
211 })
212 .collect();
213 DenseMultilinearExtension {
214 evaluations,
215 num_vars,
216 }
217 })
218 );
219
220 result
221}
222
223#[allow(clippy::arithmetic_side_effects)]
227pub fn evaluate_trace_to_column_mles<F: PrimeField + 'static>(
228 trace: &ProjectedTrace<F>,
229 projecting_element: &F,
230) -> Vec<DenseMultilinearExtension<F::Inner>> {
231 let zero = F::zero_with_cfg(projecting_element.cfg());
232 let one = F::one_with_cfg(projecting_element.cfg());
233
234 let max_coeffs_len = {
235 macro_rules! common_code {
237 ($v:expr) => {
238 $v.iter()
239 .flat_map(|row| row.iter())
240 .map(|poly| poly.degree().map_or(0, |d| d + 1))
241 .max()
242 .unwrap_or(0)
243 .max(1)
244 };
245 }
246 match trace {
247 ProjectedTrace::RowMajor(t) => common_code!(t),
248 ProjectedTrace::ColumnMajor(t) => common_code!(t),
249 }
250 };
251
252 let projection_powers: Vec<F> = powers(projecting_element.clone(), one, max_coeffs_len);
253
254 let evaluate_poly = |poly: &DynamicPolynomialF<F>| -> F::Inner {
255 let deg = poly.degree().map_or(0, |d| d + 1);
256 DynamicPolyFInnerProduct::inner_product::<UNCHECKED>(
257 &poly.coeffs[..deg],
258 &projection_powers[..deg],
259 zero.clone(),
260 )
261 .expect("inner product cannot fail here")
262 .into_inner()
263 };
264
265 match trace {
266 ProjectedTrace::RowMajor(t) => {
267 let num_rows = t.len();
268 let num_cols = t.first().map(|r| r.len()).unwrap_or(0);
269 let num_vars = num_rows.next_power_of_two().trailing_zeros() as usize;
270
271 cfg_into_iter!(0..num_cols)
272 .map(|col_idx| {
273 let evaluations: Vec<F::Inner> = (0..num_rows)
274 .map(|row_idx| evaluate_poly(&t[row_idx][col_idx]))
275 .collect();
276 DenseMultilinearExtension::from_evaluations_vec(
277 num_vars,
278 evaluations,
279 zero.inner().clone(),
280 )
281 })
282 .collect()
283 }
284 ProjectedTrace::ColumnMajor(t) => cfg_iter!(t)
285 .map(|col_mle| {
286 let evaluations: Vec<F::Inner> = cfg_iter!(col_mle).map(evaluate_poly).collect();
287 DenseMultilinearExtension::from_evaluations_vec(
288 col_mle.num_vars,
289 evaluations,
290 zero.inner().clone(),
291 )
292 })
293 .collect(),
294 }
295}
296
297pub fn project_scalars<F: PrimeField, U: Uair>(
299 project: impl Fn(&U::Scalar) -> DynamicPolynomialF<F>,
300) -> HashMap<U::Scalar, DynamicPolynomialF<F>> {
301 let uair_scalars = collect_scalars::<U>();
302
303 uair_scalars
306 .into_iter()
307 .map(|scalar| {
308 (scalar.clone(), {
309 let mut dynamic_poly = project(&scalar);
310
311 dynamic_poly.trim();
312
313 dynamic_poly
314 })
315 })
316 .collect()
317}
318
319#[allow(clippy::arithmetic_side_effects)]
321pub fn project_scalars_to_field<R: Semiring + 'static, F: PrimeField>(
322 scalars: HashMap<R, DynamicPolynomialF<F>>,
323 projecting_element: &F,
324) -> Result<HashMap<R, F>, (R, F, EvaluationError)> {
325 let one = F::one_with_cfg(projecting_element.cfg());
329 let zero = F::zero_with_cfg(projecting_element.cfg());
330
331 let max_coeffs_len = scalars
332 .values()
333 .map(|poly| poly.degree().map_or(0, |d| d + 1))
334 .max()
335 .unwrap_or(0)
336 .max(1);
337
338 let projection_powers: Vec<F> = powers(projecting_element.clone(), one, max_coeffs_len);
339
340 Ok(scalars
341 .into_iter()
342 .map(|(scalar, value)| {
343 let deg = value.degree().map_or(0, |d| d + 1);
344 (
345 scalar,
346 DynamicPolyFInnerProduct::inner_product::<UNCHECKED>(
347 &value.coeffs[..deg],
348 &projection_powers[..deg],
349 zero.clone(),
350 )
351 .expect("inner product cannot fail here"),
352 )
353 })
354 .collect())
355}