1#![allow(clippy::needless_range_loop)]
2
3use core::borrow::Borrow;
4use p3_air::{Air, AirBuilder, BaseAir, PairBuilder};
5use p3_field::PrimeField32;
6use p3_matrix::{dense::RowMajorMatrix, Matrix};
7use sp1_core_machine::utils::next_power_of_two;
8use sp1_derive::AlignedBorrow;
9use sp1_stark::air::{BaseAirBuilder, BinomialExtension, ExtensionAirBuilder, MachineAir};
10
11use crate::{air::Block, builder::SP1RecursionAirBuilder, ExecutionRecord};
12
13#[cfg(feature = "sys")]
14use {
15 crate::{runtime::Instruction, FriFoldEvent, FriFoldInstr},
16 itertools::Itertools,
17 p3_baby_bear::BabyBear,
18 p3_field::AbstractField,
19 sp1_core_machine::utils::pad_rows_fixed,
20 std::borrow::BorrowMut,
21 tracing::instrument,
22};
23
24use super::mem::MemoryAccessColsChips;
25
26pub const NUM_FRI_FOLD_COLS: usize = core::mem::size_of::<FriFoldCols<u8>>();
27pub const NUM_FRI_FOLD_PREPROCESSED_COLS: usize =
28 core::mem::size_of::<FriFoldPreprocessedCols<u8>>();
29
30pub struct FriFoldChip<const DEGREE: usize> {
31 pub fixed_log2_rows: Option<usize>,
32 pub pad: bool,
33}
34
35impl<const DEGREE: usize> Default for FriFoldChip<DEGREE> {
36 fn default() -> Self {
37 Self { fixed_log2_rows: None, pad: true }
38 }
39}
40
41#[derive(AlignedBorrow, Debug, Clone, Copy)]
43#[repr(C)]
44pub struct FriFoldPreprocessedCols<T: Copy> {
45 pub is_first: T,
46
47 pub z_mem: MemoryAccessColsChips<T>,
49 pub alpha_mem: MemoryAccessColsChips<T>,
50 pub x_mem: MemoryAccessColsChips<T>,
51
52 pub alpha_pow_input_mem: MemoryAccessColsChips<T>,
54 pub ro_input_mem: MemoryAccessColsChips<T>,
55 pub p_at_x_mem: MemoryAccessColsChips<T>,
56 pub p_at_z_mem: MemoryAccessColsChips<T>,
57
58 pub ro_output_mem: MemoryAccessColsChips<T>,
60 pub alpha_pow_output_mem: MemoryAccessColsChips<T>,
61
62 pub is_real: T,
63}
64
65#[derive(AlignedBorrow, Debug, Clone, Copy)]
66#[repr(C)]
67pub struct FriFoldCols<T: Copy> {
68 pub z: Block<T>,
69 pub alpha: Block<T>,
70 pub x: T,
71
72 pub p_at_x: Block<T>,
73 pub p_at_z: Block<T>,
74 pub alpha_pow_input: Block<T>,
75 pub ro_input: Block<T>,
76
77 pub alpha_pow_output: Block<T>,
78 pub ro_output: Block<T>,
79}
80
81impl<F, const DEGREE: usize> BaseAir<F> for FriFoldChip<DEGREE> {
82 fn width(&self) -> usize {
83 NUM_FRI_FOLD_COLS
84 }
85}
86
87impl<F: PrimeField32, const DEGREE: usize> MachineAir<F> for FriFoldChip<DEGREE> {
88 type Record = ExecutionRecord<F>;
89
90 type Program = crate::RecursionProgram<F>;
91
92 fn name(&self) -> String {
93 "FriFold".to_string()
94 }
95
96 fn generate_dependencies(&self, _: &Self::Record, _: &mut Self::Record) {
97 }
99
100 fn preprocessed_width(&self) -> usize {
101 NUM_FRI_FOLD_PREPROCESSED_COLS
102 }
103
104 #[cfg(not(feature = "sys"))]
105 fn generate_preprocessed_trace(&self, _program: &Self::Program) -> Option<RowMajorMatrix<F>> {
106 unimplemented!("To generate traces, enable feature `sp1-recursion-core/sys`");
107 }
108
109 #[cfg(feature = "sys")]
110 fn generate_preprocessed_trace(&self, program: &Self::Program) -> Option<RowMajorMatrix<F>> {
111 assert_eq!(
112 std::any::TypeId::of::<F>(),
113 std::any::TypeId::of::<BabyBear>(),
114 "generate_trace only supports BabyBear field"
115 );
116
117 let mut rows: Vec<[BabyBear; NUM_FRI_FOLD_PREPROCESSED_COLS]> = Vec::new();
118 program
119 .inner
120 .iter()
121 .filter_map(|instruction| match instruction {
122 Instruction::FriFold(instr) => Some(unsafe {
123 std::mem::transmute::<&Box<FriFoldInstr<F>>, &Box<FriFoldInstr<BabyBear>>>(
124 instr,
125 )
126 }),
127 _ => None,
128 })
129 .for_each(|instruction| {
130 let mut row_add = vec![
131 [BabyBear::zero(); NUM_FRI_FOLD_PREPROCESSED_COLS];
132 instruction.ext_vec_addrs.ps_at_z.len()
133 ];
134
135 row_add.iter_mut().enumerate().for_each(|(row_idx, row)| {
136 let cols: &mut FriFoldPreprocessedCols<BabyBear> =
137 row.as_mut_slice().borrow_mut();
138 unsafe {
139 crate::sys::fri_fold_instr_to_row_babybear(
140 &instruction.into(),
141 row_idx,
142 cols,
143 );
144 }
145 });
146
147 rows.extend(row_add);
148 });
149
150 if self.pad {
152 pad_rows_fixed(
153 &mut rows,
154 || [BabyBear::zero(); NUM_FRI_FOLD_PREPROCESSED_COLS],
155 self.fixed_log2_rows,
156 );
157 }
158
159 let trace = RowMajorMatrix::new(
160 unsafe {
161 std::mem::transmute::<Vec<BabyBear>, Vec<F>>(
162 rows.into_iter().flatten().collect::<Vec<BabyBear>>(),
163 )
164 },
165 NUM_FRI_FOLD_PREPROCESSED_COLS,
166 );
167 Some(trace)
168 }
169
170 fn num_rows(&self, input: &Self::Record) -> Option<usize> {
171 let events = &input.fri_fold_events;
172 Some(next_power_of_two(events.len(), input.fixed_log2_rows(self)))
173 }
174
175 #[cfg(not(feature = "sys"))]
176 fn generate_trace(&self, _input: &Self::Record, _: &mut Self::Record) -> RowMajorMatrix<F> {
177 unimplemented!("To generate traces, enable feature `sp1-recursion-core/sys`");
178 }
179
180 #[cfg(feature = "sys")]
181 #[instrument(name = "generate fri fold trace", level = "debug", skip_all, fields(rows = input.fri_fold_events.len()))]
182 fn generate_trace(
183 &self,
184 input: &ExecutionRecord<F>,
185 _: &mut ExecutionRecord<F>,
186 ) -> RowMajorMatrix<F> {
187 assert_eq!(
188 std::any::TypeId::of::<F>(),
189 std::any::TypeId::of::<BabyBear>(),
190 "generate_trace only supports BabyBear field"
191 );
192
193 let events = unsafe {
194 std::mem::transmute::<&Vec<FriFoldEvent<F>>, &Vec<FriFoldEvent<BabyBear>>>(
195 &input.fri_fold_events,
196 )
197 };
198 let mut rows = events
199 .iter()
200 .map(|event| {
201 let mut row = [BabyBear::zero(); NUM_FRI_FOLD_COLS];
202 let cols: &mut FriFoldCols<BabyBear> = row.as_mut_slice().borrow_mut();
203 unsafe {
204 crate::sys::fri_fold_event_to_row_babybear(event, cols);
205 }
206
207 row
208 })
209 .collect_vec();
210
211 if self.pad {
213 rows.resize(self.num_rows(input).unwrap(), [BabyBear::zero(); NUM_FRI_FOLD_COLS]);
214 }
215
216 let trace = RowMajorMatrix::new(
218 unsafe {
219 std::mem::transmute::<Vec<BabyBear>, Vec<F>>(
220 rows.into_iter().flatten().collect::<Vec<BabyBear>>(),
221 )
222 },
223 NUM_FRI_FOLD_COLS,
224 );
225
226 #[cfg(debug_assertions)]
227 eprintln!(
228 "fri fold trace dims is width: {:?}, height: {:?}",
229 trace.width(),
230 trace.height()
231 );
232
233 trace
234 }
235
236 fn included(&self, _record: &Self::Record) -> bool {
237 true
238 }
239}
240
241impl<const DEGREE: usize> FriFoldChip<DEGREE> {
242 pub fn eval_fri_fold<AB: SP1RecursionAirBuilder>(
243 &self,
244 builder: &mut AB,
245 local: &FriFoldCols<AB::Var>,
246 next: &FriFoldCols<AB::Var>,
247 local_prepr: &FriFoldPreprocessedCols<AB::Var>,
248 next_prepr: &FriFoldPreprocessedCols<AB::Var>,
249 ) {
250 builder.send_single(local_prepr.x_mem.addr, local.x, local_prepr.x_mem.mult);
252
253 builder
255 .when_transition()
256 .when(next_prepr.is_real)
257 .when_not(next_prepr.is_first)
258 .assert_eq(local.x, next.x);
259
260 builder.send_block(local_prepr.z_mem.addr, local.z, local_prepr.z_mem.mult);
262
263 builder
265 .when_transition()
266 .when(next_prepr.is_real)
267 .when_not(next_prepr.is_first)
268 .assert_ext_eq(local.z.as_extension::<AB>(), next.z.as_extension::<AB>());
269
270 builder.send_block(local_prepr.alpha_mem.addr, local.alpha, local_prepr.alpha_mem.mult);
272
273 builder
275 .when_transition()
276 .when(next_prepr.is_real)
277 .when_not(next_prepr.is_first)
278 .assert_ext_eq(local.alpha.as_extension::<AB>(), next.alpha.as_extension::<AB>());
279
280 builder.send_block(
282 local_prepr.alpha_pow_input_mem.addr,
283 local.alpha_pow_input,
284 local_prepr.alpha_pow_input_mem.mult,
285 );
286
287 builder.send_block(
289 local_prepr.ro_input_mem.addr,
290 local.ro_input,
291 local_prepr.ro_input_mem.mult,
292 );
293
294 builder.send_block(local_prepr.p_at_z_mem.addr, local.p_at_z, local_prepr.p_at_z_mem.mult);
296
297 builder.send_block(local_prepr.p_at_x_mem.addr, local.p_at_x, local_prepr.p_at_x_mem.mult);
299
300 builder.send_block(
302 local_prepr.alpha_pow_output_mem.addr,
303 local.alpha_pow_output,
304 local_prepr.alpha_pow_output_mem.mult,
305 );
306
307 builder.send_block(
309 local_prepr.ro_output_mem.addr,
310 local.ro_output,
311 local_prepr.ro_output_mem.mult,
312 );
313
314 let alpha = local.alpha.as_extension::<AB>();
316 let old_alpha_pow = local.alpha_pow_input.as_extension::<AB>();
317 let new_alpha_pow = local.alpha_pow_output.as_extension::<AB>();
318 builder.assert_ext_eq(old_alpha_pow.clone() * alpha, new_alpha_pow.clone());
319
320 let p_at_z = local.p_at_z.as_extension::<AB>();
324 let p_at_x = local.p_at_x.as_extension::<AB>();
325 let z = local.z.as_extension::<AB>();
326 let x = local.x.into();
327 let old_ro = local.ro_input.as_extension::<AB>();
328 let new_ro = local.ro_output.as_extension::<AB>();
329 builder.assert_ext_eq(
330 (new_ro.clone() - old_ro) * (BinomialExtension::from_base(x) - z),
331 (p_at_x - p_at_z) * old_alpha_pow,
332 );
333 }
334
335 pub const fn do_memory_access<T: Copy>(local: &FriFoldPreprocessedCols<T>) -> T {
336 local.is_real
337 }
338}
339
340impl<AB, const DEGREE: usize> Air<AB> for FriFoldChip<DEGREE>
341where
342 AB: SP1RecursionAirBuilder + PairBuilder,
343{
344 fn eval(&self, builder: &mut AB) {
345 let main = builder.main();
346 let (local, next) = (main.row_slice(0), main.row_slice(1));
347 let local: &FriFoldCols<AB::Var> = (*local).borrow();
348 let next: &FriFoldCols<AB::Var> = (*next).borrow();
349 let prepr = builder.preprocessed();
350 let (prepr_local, prepr_next) = (prepr.row_slice(0), prepr.row_slice(1));
351 let prepr_local: &FriFoldPreprocessedCols<AB::Var> = (*prepr_local).borrow();
352 let prepr_next: &FriFoldPreprocessedCols<AB::Var> = (*prepr_next).borrow();
353
354 let lhs = (0..DEGREE).map(|_| prepr_local.is_real.into()).product::<AB::Expr>();
356 let rhs = (0..DEGREE).map(|_| prepr_local.is_real.into()).product::<AB::Expr>();
357 builder.assert_eq(lhs, rhs);
358
359 self.eval_fri_fold::<AB>(builder, local, next, prepr_local, prepr_next);
360 }
361}
362
363#[cfg(all(test, feature = "sys"))]
364mod tests {
365 #![allow(clippy::print_stdout)]
366
367 use crate::{
368 air::Block,
369 chips::{fri_fold::FriFoldChip, mem::MemoryAccessCols, test_fixtures},
370 machine::tests::test_recursion_linear_program,
371 runtime::{instruction as instr, ExecutionRecord},
372 stark::BabyBearPoseidon2Outer,
373 FriFoldBaseIo, FriFoldEvent, FriFoldExtSingleIo, FriFoldExtVecIo, Instruction,
374 MemAccessKind, RecursionProgram,
375 };
376 use p3_baby_bear::BabyBear;
377 use p3_field::{AbstractExtensionField, AbstractField};
378 use p3_matrix::dense::RowMajorMatrix;
379 use rand::{rngs::StdRng, Rng, SeedableRng};
380 use sp1_core_machine::utils::setup_logger;
381 use sp1_stark::{air::MachineAir, StarkGenericConfig};
382 use std::mem::size_of;
383
384 use super::*;
385
386 const DEGREE: usize = 3;
387
388 #[test]
389 fn prove_babybear_circuit_fri_fold() {
390 setup_logger();
391 type SC = BabyBearPoseidon2Outer;
392 type F = <SC as StarkGenericConfig>::Val;
393 type EF = <SC as StarkGenericConfig>::Challenge;
394
395 let mut rng = StdRng::seed_from_u64(0xDEADBEEF);
396 let mut random_felt = move || -> F { F::from_canonical_u32(rng.gen_range(0..1 << 16)) };
397 let mut rng = StdRng::seed_from_u64(0xDEADBEEF);
398 let mut random_block =
399 move || Block::from([F::from_canonical_u32(rng.gen_range(0..1 << 16)); 4]);
400 let mut addr = 0;
401
402 let num_ext_vecs: u32 = size_of::<FriFoldExtVecIo<u8>>() as u32;
403 let num_singles: u32 =
404 size_of::<FriFoldBaseIo<u8>>() as u32 + size_of::<FriFoldExtSingleIo<u8>>() as u32;
405
406 let instructions = (2..17)
407 .flat_map(|i: u32| {
408 let alloc_size = i * (num_ext_vecs + 2) + num_singles;
409
410 let mat_opening_a = (0..i).map(|x| x + addr).collect::<Vec<_>>();
413 let ps_at_z_a = (0..i).map(|x| x + i + addr).collect::<Vec<_>>();
414
415 let alpha_pow_input_a = (0..i).map(|x: u32| x + addr + 2 * i).collect::<Vec<_>>();
416 let ro_input_a = (0..i).map(|x: u32| x + addr + 3 * i).collect::<Vec<_>>();
417
418 let alpha_pow_output_a = (0..i).map(|x: u32| x + addr + 4 * i).collect::<Vec<_>>();
419 let ro_output_a = (0..i).map(|x: u32| x + addr + 5 * i).collect::<Vec<_>>();
420
421 let x_a = addr + 6 * i;
422 let z_a = addr + 6 * i + 1;
423 let alpha_a = addr + 6 * i + 2;
424
425 addr += alloc_size;
426
427 let x = random_felt();
429 let z = random_block();
430 let alpha = random_block();
431
432 let alpha_pow_input = (0..i).map(|_| random_block()).collect::<Vec<_>>();
433 let ro_input = (0..i).map(|_| random_block()).collect::<Vec<_>>();
434
435 let ps_at_z = (0..i).map(|_| random_block()).collect::<Vec<_>>();
436 let mat_opening = (0..i).map(|_| random_block()).collect::<Vec<_>>();
437
438 let alpha_pow_output = (0..i)
440 .map(|i| alpha_pow_input[i as usize].ext::<EF>() * alpha.ext::<EF>())
441 .collect::<Vec<EF>>();
442 let ro_output = (0..i)
443 .map(|i| {
444 let i = i as usize;
445 ro_input[i].ext::<EF>() +
446 alpha_pow_input[i].ext::<EF>() *
447 (-ps_at_z[i].ext::<EF>() + mat_opening[i].ext::<EF>()) /
448 (-z.ext::<EF>() + x)
449 })
450 .collect::<Vec<EF>>();
451
452 let mut instructions = vec![instr::mem_single(MemAccessKind::Write, 1, x_a, x)];
454
455 instructions.push(instr::mem_block(MemAccessKind::Write, 1, z_a, z));
456
457 instructions.push(instr::mem_block(MemAccessKind::Write, 1, alpha_a, alpha));
458
459 (0..i).for_each(|j_32| {
460 let j = j_32 as usize;
461 instructions.push(instr::mem_block(
462 MemAccessKind::Write,
463 1,
464 mat_opening_a[j],
465 mat_opening[j],
466 ));
467 instructions.push(instr::mem_block(
468 MemAccessKind::Write,
469 1,
470 ps_at_z_a[j],
471 ps_at_z[j],
472 ));
473
474 instructions.push(instr::mem_block(
475 MemAccessKind::Write,
476 1,
477 alpha_pow_input_a[j],
478 alpha_pow_input[j],
479 ));
480 instructions.push(instr::mem_block(
481 MemAccessKind::Write,
482 1,
483 ro_input_a[j],
484 ro_input[j],
485 ));
486 });
487
488 instructions.push(instr::fri_fold(
490 z_a,
491 alpha_a,
492 x_a,
493 mat_opening_a.clone(),
494 ps_at_z_a.clone(),
495 alpha_pow_input_a.clone(),
496 ro_input_a.clone(),
497 alpha_pow_output_a.clone(),
498 ro_output_a.clone(),
499 vec![1; i as usize],
500 vec![1; i as usize],
501 ));
502
503 (0..i).for_each(|j| {
505 let j = j as usize;
506 instructions.push(instr::mem_block(
507 MemAccessKind::Read,
508 1,
509 alpha_pow_output_a[j],
510 Block::from(alpha_pow_output[j].as_base_slice()),
511 ));
512 instructions.push(instr::mem_block(
513 MemAccessKind::Read,
514 1,
515 ro_output_a[j],
516 Block::from(ro_output[j].as_base_slice()),
517 ));
518 });
519
520 instructions
521 })
522 .collect::<Vec<Instruction<F>>>();
523
524 test_recursion_linear_program(instructions);
525 }
526
527 #[test]
528 fn generate_fri_fold_circuit_trace() {
529 type F = BabyBear;
530
531 let mut rng = StdRng::seed_from_u64(0xDEADBEEF);
532 let mut rng2 = StdRng::seed_from_u64(0xDEADBEEF);
533 let mut random_felt = move || -> F { F::from_canonical_u32(rng.gen_range(0..1 << 16)) };
534 let mut random_block = move || Block::from([random_felt(); 4]);
535
536 let shard = ExecutionRecord {
537 fri_fold_events: (0..17)
538 .map(|_| FriFoldEvent {
539 base_single: FriFoldBaseIo {
540 x: F::from_canonical_u32(rng2.gen_range(0..1 << 16)),
541 },
542 ext_single: FriFoldExtSingleIo { z: random_block(), alpha: random_block() },
543 ext_vec: crate::FriFoldExtVecIo {
544 mat_opening: random_block(),
545 ps_at_z: random_block(),
546 alpha_pow_input: random_block(),
547 ro_input: random_block(),
548 alpha_pow_output: random_block(),
549 ro_output: random_block(),
550 },
551 })
552 .collect(),
553 ..Default::default()
554 };
555 let chip = FriFoldChip::<3>::default();
556 let trace: RowMajorMatrix<F> = chip.generate_trace(&shard, &mut ExecutionRecord::default());
557 println!("{:?}", trace.values)
558 }
559
560 fn generate_trace_reference<const DEGREE: usize>(
561 input: &ExecutionRecord<BabyBear>,
562 _: &mut ExecutionRecord<BabyBear>,
563 ) -> RowMajorMatrix<BabyBear> {
564 type F = BabyBear;
565
566 let mut rows = input
567 .fri_fold_events
568 .iter()
569 .map(|event| {
570 let mut row = [F::zero(); NUM_FRI_FOLD_COLS];
571
572 let cols: &mut FriFoldCols<F> = row.as_mut_slice().borrow_mut();
573
574 cols.x = event.base_single.x;
575 cols.z = event.ext_single.z;
576 cols.alpha = event.ext_single.alpha;
577
578 cols.p_at_z = event.ext_vec.ps_at_z;
579 cols.p_at_x = event.ext_vec.mat_opening;
580 cols.alpha_pow_input = event.ext_vec.alpha_pow_input;
581 cols.ro_input = event.ext_vec.ro_input;
582
583 cols.alpha_pow_output = event.ext_vec.alpha_pow_output;
584 cols.ro_output = event.ext_vec.ro_output;
585
586 row
587 })
588 .collect_vec();
589
590 rows.resize(
591 FriFoldChip::<DEGREE>::default().num_rows(input).unwrap(),
592 [F::zero(); NUM_FRI_FOLD_COLS],
593 );
594
595 RowMajorMatrix::new(rows.into_iter().flatten().collect(), NUM_FRI_FOLD_COLS)
596 }
597
598 #[test]
599 fn test_generate_trace() {
600 let shard = test_fixtures::shard();
601 let mut execution_record = test_fixtures::default_execution_record();
602 let chip = FriFoldChip::<DEGREE>::default();
603 let trace = chip.generate_trace(&shard, &mut execution_record);
604 assert!(trace.height() >= test_fixtures::MIN_TEST_CASES);
605
606 assert_eq!(trace, generate_trace_reference::<DEGREE>(&shard, &mut execution_record));
607 }
608
609 fn generate_preprocessed_trace_reference<const DEGREE: usize>(
610 program: &RecursionProgram<BabyBear>,
611 ) -> RowMajorMatrix<BabyBear> {
612 type F = BabyBear;
613
614 let mut rows: Vec<[F; NUM_FRI_FOLD_PREPROCESSED_COLS]> = Vec::new();
615 program
616 .inner
617 .iter()
618 .filter_map(|instruction| match instruction {
619 Instruction::FriFold(instr) => Some(instr),
620 _ => None,
621 })
622 .for_each(|instruction| {
623 let FriFoldInstr {
624 base_single_addrs,
625 ext_single_addrs,
626 ext_vec_addrs,
627 alpha_pow_mults,
628 ro_mults,
629 } = instruction.as_ref();
630 let mut row_add =
631 vec![[F::zero(); NUM_FRI_FOLD_PREPROCESSED_COLS]; ext_vec_addrs.ps_at_z.len()];
632
633 row_add.iter_mut().enumerate().for_each(|(i, row)| {
634 let row: &mut FriFoldPreprocessedCols<F> = row.as_mut_slice().borrow_mut();
635 row.is_first = F::from_bool(i == 0);
636
637 row.z_mem =
640 MemoryAccessCols { addr: ext_single_addrs.z, mult: -F::from_bool(i == 0) };
641 row.x_mem =
642 MemoryAccessCols { addr: base_single_addrs.x, mult: -F::from_bool(i == 0) };
643 row.alpha_mem = MemoryAccessCols {
644 addr: ext_single_addrs.alpha,
645 mult: -F::from_bool(i == 0),
646 };
647
648 row.alpha_pow_input_mem = MemoryAccessCols {
650 addr: ext_vec_addrs.alpha_pow_input[i],
651 mult: F::neg_one(),
652 };
653 row.ro_input_mem =
654 MemoryAccessCols { addr: ext_vec_addrs.ro_input[i], mult: F::neg_one() };
655 row.p_at_z_mem =
656 MemoryAccessCols { addr: ext_vec_addrs.ps_at_z[i], mult: F::neg_one() };
657 row.p_at_x_mem =
658 MemoryAccessCols { addr: ext_vec_addrs.mat_opening[i], mult: F::neg_one() };
659
660 row.alpha_pow_output_mem = MemoryAccessCols {
662 addr: ext_vec_addrs.alpha_pow_output[i],
663 mult: alpha_pow_mults[i],
664 };
665 row.ro_output_mem =
666 MemoryAccessCols { addr: ext_vec_addrs.ro_output[i], mult: ro_mults[i] };
667
668 row.is_real = F::one();
669 });
670 rows.extend(row_add);
671 });
672
673 pad_rows_fixed(&mut rows, || [F::zero(); NUM_FRI_FOLD_PREPROCESSED_COLS], None);
674
675 RowMajorMatrix::new(rows.into_iter().flatten().collect(), NUM_FRI_FOLD_PREPROCESSED_COLS)
676 }
677
678 #[test]
679 #[ignore = "Failing due to merge conflicts. Will be fixed shortly."]
680 fn generate_preprocessed_trace() {
681 let program = test_fixtures::program();
682 let chip = FriFoldChip::<DEGREE>::default();
683 let trace = chip.generate_preprocessed_trace(&program).unwrap();
684 assert!(trace.height() >= test_fixtures::MIN_TEST_CASES);
685
686 assert_eq!(trace, generate_preprocessed_trace_reference::<DEGREE>(&program));
687 }
688}