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