sp1_recursion_core/chips/
fri_fold.rs

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/// The preprocessed columns for a FRI fold invocation.
42#[derive(AlignedBorrow, Debug, Clone, Copy)]
43#[repr(C)]
44pub struct FriFoldPreprocessedCols<T: Copy> {
45    pub is_first: T,
46
47    // Memory accesses for the single fields.
48    pub z_mem: MemoryAccessColsChips<T>,
49    pub alpha_mem: MemoryAccessColsChips<T>,
50    pub x_mem: MemoryAccessColsChips<T>,
51
52    // Memory accesses for the vector field inputs.
53    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    // Memory accesses for the vector field outputs.
59    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        // This is a no-op.
98    }
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        // Pad the trace to a power of two.
151        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        // Pad the trace to a power of two.
212        if self.pad {
213            rows.resize(self.num_rows(input).unwrap(), [BabyBear::zero(); NUM_FRI_FOLD_COLS]);
214        }
215
216        // Convert the trace to a row major matrix.
217        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        // Constrain mem read for x.  Read at the first fri fold row.
251        builder.send_single(local_prepr.x_mem.addr, local.x, local_prepr.x_mem.mult);
252
253        // Ensure that the x value is the same for all rows within a fri fold invocation.
254        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        // Constrain mem read for z.  Read at the first fri fold row.
261        builder.send_block(local_prepr.z_mem.addr, local.z, local_prepr.z_mem.mult);
262
263        // Ensure that the z value is the same for all rows within a fri fold invocation.
264        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        // Constrain mem read for alpha.  Read at the first fri fold row.
271        builder.send_block(local_prepr.alpha_mem.addr, local.alpha, local_prepr.alpha_mem.mult);
272
273        // Ensure that the alpha value is the same for all rows within a fri fold invocation.
274        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        // Constrain read for alpha_pow_input.
281        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        // Constrain read for ro_input.
288        builder.send_block(
289            local_prepr.ro_input_mem.addr,
290            local.ro_input,
291            local_prepr.ro_input_mem.mult,
292        );
293
294        // Constrain read for p_at_z.
295        builder.send_block(local_prepr.p_at_z_mem.addr, local.p_at_z, local_prepr.p_at_z_mem.mult);
296
297        // Constrain read for p_at_x.
298        builder.send_block(local_prepr.p_at_x_mem.addr, local.p_at_x, local_prepr.p_at_x_mem.mult);
299
300        // Constrain write for alpha_pow_output.
301        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        // Constrain write for ro_output.
308        builder.send_block(
309            local_prepr.ro_output_mem.addr,
310            local.ro_output,
311            local_prepr.ro_output_mem.mult,
312        );
313
314        // 1. Constrain new_value = old_value * alpha.
315        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        // 2. Constrain new_value = old_alpha_pow * quotient + old_ro,
321        // where quotient = (p_at_x - p_at_z) / (x - z)
322        // <=> (new_ro - old_ro) * (z - x) = old_alpha_pow * (p_at_x - p_at_z)
323        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        // Dummy constraints to normalize to DEGREE.
355        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                // Allocate the memory for a FRI fold instruction. Here, i is the lengths
411                // of the vectors for the vector fields of the instruction.
412                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                // Generate random values for the inputs.
428                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                // Compute the outputs from the inputs.
439                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                // Write the inputs to memory.
453                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                // Generate the FRI fold instruction.
489                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                // Read all the outputs.
504                (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                    // Only need to read z, x, and alpha on the first iteration, hence the
638                    // multiplicities are i==0.
639                    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                    // Read the memory for the input vectors.
649                    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                    // Write the memory for the output vectors.
661                    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}