sp1_recursion_core/chips/
fri_fold.rs

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/// The preprocessed columns for a FRI fold invocation.
41#[derive(AlignedBorrow, Debug, Clone, Copy)]
42#[repr(C)]
43pub struct FriFoldPreprocessedCols<T: Copy> {
44    pub is_first: T,
45
46    // Memory accesses for the single fields.
47    pub z_mem: MemoryAccessColsChips<T>,
48    pub alpha_mem: MemoryAccessColsChips<T>,
49    pub x_mem: MemoryAccessColsChips<T>,
50
51    // Memory accesses for the vector field inputs.
52    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    // Memory accesses for the vector field outputs.
58    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        // This is a no-op.
97    }
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        // Pad the trace to a power of two.
144        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        // Pad the trace to a power of two.
199        if self.pad {
200            rows.resize(self.num_rows(input).unwrap(), [BabyBear::zero(); NUM_FRI_FOLD_COLS]);
201        }
202
203        // Convert the trace to a row major matrix.
204        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        // Constrain mem read for x.  Read at the first fri fold row.
238        builder.send_single(local_prepr.x_mem.addr, local.x, local_prepr.x_mem.mult);
239
240        // Ensure that the x value is the same for all rows within a fri fold invocation.
241        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        // Constrain mem read for z.  Read at the first fri fold row.
248        builder.send_block(local_prepr.z_mem.addr, local.z, local_prepr.z_mem.mult);
249
250        // Ensure that the z value is the same for all rows within a fri fold invocation.
251        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        // Constrain mem read for alpha.  Read at the first fri fold row.
258        builder.send_block(local_prepr.alpha_mem.addr, local.alpha, local_prepr.alpha_mem.mult);
259
260        // Ensure that the alpha value is the same for all rows within a fri fold invocation.
261        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        // Constrain read for alpha_pow_input.
268        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        // Constrain read for ro_input.
275        builder.send_block(
276            local_prepr.ro_input_mem.addr,
277            local.ro_input,
278            local_prepr.ro_input_mem.mult,
279        );
280
281        // Constrain read for p_at_z.
282        builder.send_block(local_prepr.p_at_z_mem.addr, local.p_at_z, local_prepr.p_at_z_mem.mult);
283
284        // Constrain read for p_at_x.
285        builder.send_block(local_prepr.p_at_x_mem.addr, local.p_at_x, local_prepr.p_at_x_mem.mult);
286
287        // Constrain write for alpha_pow_output.
288        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        // Constrain write for ro_output.
295        builder.send_block(
296            local_prepr.ro_output_mem.addr,
297            local.ro_output,
298            local_prepr.ro_output_mem.mult,
299        );
300
301        // 1. Constrain new_value = old_value * alpha.
302        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        // 2. Constrain new_value = old_alpha_pow * quotient + old_ro,
308        // where quotient = (p_at_x - p_at_z) / (x - z)
309        // <=> (new_ro - old_ro) * (z - x) = old_alpha_pow * (p_at_x - p_at_z)
310        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        // Dummy constraints to normalize to DEGREE.
342        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                // Allocate the memory for a FRI fold instruction. Here, i is the lengths
399                // of the vectors for the vector fields of the instruction.
400                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                // Generate random values for the inputs.
416                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                // Compute the outputs from the inputs.
427                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                // Write the inputs to memory.
441                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                // Generate the FRI fold instruction.
477                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                // Read all the outputs.
492                (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                    // Only need to read z, x, and alpha on the first iteration, hence the
626                    // multiplicities are i==0.
627                    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                    // Read the memory for the input vectors.
637                    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                    // Write the memory for the output vectors.
649                    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}