1use core::ops::Deref;
2use std::{
3 borrow::{Borrow, BorrowMut},
4 mem::offset_of,
5 sync::{Arc, Mutex},
6};
7
8use itertools::{zip_eq, Itertools};
9use openvm_circuit::{
10 arch::{
11 ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor, Streams,
12 },
13 system::{
14 memory::{
15 offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols},
16 MemoryAddress, MemoryAuxColsFactory, MemoryController, OfflineMemory, RecordId,
17 },
18 program::ProgramBus,
19 },
20};
21use openvm_circuit_primitives::utils::next_power_of_two_or_zero;
22use openvm_circuit_primitives_derive::AlignedBorrow;
23use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode};
24use openvm_native_compiler::{conversion::AS, FriOpcode::FRI_REDUCED_OPENING};
25use openvm_stark_backend::{
26 config::{StarkGenericConfig, Val},
27 interaction::InteractionBuilder,
28 p3_air::{Air, AirBuilder, BaseAir},
29 p3_field::{Field, FieldAlgebra, PrimeField32},
30 p3_matrix::{dense::RowMajorMatrix, Matrix},
31 p3_maybe_rayon::prelude::*,
32 prover::types::AirProofInput,
33 rap::{BaseAirWithPublicValues, PartitionedBaseAir},
34 AirRef, Chip, ChipUsageGetter,
35};
36use serde::{Deserialize, Serialize};
37use static_assertions::const_assert_eq;
38
39use crate::{
40 field_extension::{FieldExtension, EXT_DEG},
41 utils::const_max,
42};
43
44#[cfg(test)]
45mod tests;
46
47#[repr(C)]
48#[derive(Debug, AlignedBorrow)]
49struct WorkloadCols<T> {
50 prefix: PrefixCols<T>,
51
52 a_aux: MemoryWriteAuxCols<T, 1>,
53 b: [T; EXT_DEG],
55 b_aux: MemoryReadAuxCols<T>,
56}
57const WL_WIDTH: usize = WorkloadCols::<u8>::width();
58const_assert_eq!(WL_WIDTH, 27);
59
60#[repr(C)]
61#[derive(Debug, AlignedBorrow)]
62struct Instruction1Cols<T> {
63 prefix: PrefixCols<T>,
64
65 pc: T,
66
67 a_ptr_ptr: T,
68 a_ptr_aux: MemoryReadAuxCols<T>,
69
70 b_ptr_ptr: T,
71 b_ptr_aux: MemoryReadAuxCols<T>,
72
73 write_a_x_is_first: T,
76}
77const INS_1_WIDTH: usize = Instruction1Cols::<u8>::width();
78const_assert_eq!(INS_1_WIDTH, 26);
79const_assert_eq!(
80 offset_of!(WorkloadCols<u8>, prefix),
81 offset_of!(Instruction1Cols<u8>, prefix)
82);
83
84#[repr(C)]
85#[derive(Debug, AlignedBorrow)]
86struct Instruction2Cols<T> {
87 general: GeneralCols<T>,
88 is_first: T,
90
91 length_ptr: T,
92 length_aux: MemoryReadAuxCols<T>,
93
94 alpha_ptr: T,
95 alpha_aux: MemoryReadAuxCols<T>,
96
97 result_ptr: T,
98 result_aux: MemoryWriteAuxCols<T, EXT_DEG>,
99
100 hint_id_ptr: T,
101
102 is_init_ptr: T,
103 is_init_aux: MemoryReadAuxCols<T>,
104
105 write_a_x_is_first: T,
108}
109const INS_2_WIDTH: usize = Instruction2Cols::<u8>::width();
110const_assert_eq!(INS_2_WIDTH, 26);
111const_assert_eq!(
112 offset_of!(WorkloadCols<u8>, prefix) + offset_of!(PrefixCols<u8>, general),
113 offset_of!(Instruction2Cols<u8>, general)
114);
115const_assert_eq!(
116 offset_of!(Instruction1Cols<u8>, prefix) + offset_of!(PrefixCols<u8>, a_or_is_first),
117 offset_of!(Instruction2Cols<u8>, is_first)
118);
119const_assert_eq!(
120 offset_of!(Instruction1Cols<u8>, write_a_x_is_first),
121 offset_of!(Instruction2Cols<u8>, write_a_x_is_first)
122);
123
124pub const OVERALL_WIDTH: usize = const_max(const_max(WL_WIDTH, INS_1_WIDTH), INS_2_WIDTH);
125const_assert_eq!(OVERALL_WIDTH, 27);
126
127#[repr(C)]
129#[derive(Debug, AlignedBorrow)]
130struct GeneralCols<T> {
131 is_workload_row: T,
133 is_ins_row: T,
135 timestamp: T,
139}
140const GENERAL_WIDTH: usize = GeneralCols::<u8>::width();
141const_assert_eq!(GENERAL_WIDTH, 3);
142
143#[repr(C)]
144#[derive(Debug, AlignedBorrow)]
145struct DataCols<T> {
146 a_ptr: T,
149 write_a: T,
154 b_ptr: T,
157 idx: T,
162 result: [T; EXT_DEG],
165 alpha: [T; EXT_DEG],
168}
169#[allow(dead_code)]
170const DATA_WIDTH: usize = DataCols::<u8>::width();
171const_assert_eq!(DATA_WIDTH, 12);
172
173#[repr(C)]
175#[derive(Debug, AlignedBorrow)]
176struct PrefixCols<T> {
177 general: GeneralCols<T>,
178 a_or_is_first: T,
181 data: DataCols<T>,
182}
183const PREFIX_WIDTH: usize = PrefixCols::<u8>::width();
184const_assert_eq!(PREFIX_WIDTH, 16);
185
186const INSTRUCTION_READS: usize = 5;
187
188#[derive(Copy, Clone, Debug)]
223struct FriReducedOpeningAir {
224 execution_bridge: ExecutionBridge,
225 memory_bridge: MemoryBridge,
226}
227
228impl<F: Field> BaseAir<F> for FriReducedOpeningAir {
229 fn width(&self) -> usize {
230 OVERALL_WIDTH
231 }
232}
233
234impl<F: Field> BaseAirWithPublicValues<F> for FriReducedOpeningAir {}
235impl<F: Field> PartitionedBaseAir<F> for FriReducedOpeningAir {}
236impl<AB: InteractionBuilder> Air<AB> for FriReducedOpeningAir {
237 fn eval(&self, builder: &mut AB) {
238 let main = builder.main();
239 let local = main.row_slice(0);
240 let next = main.row_slice(1);
241 let local_slice = local.deref();
242 let next_slice = next.deref();
243 self.eval_general(builder, local_slice, next_slice);
244 self.eval_workload_row(builder, local_slice, next_slice);
245 self.eval_instruction1_row(builder, local_slice, next_slice);
246 self.eval_instruction2_row(builder, local_slice, next_slice);
247 }
248}
249
250impl FriReducedOpeningAir {
251 fn eval_general<AB: InteractionBuilder>(
252 &self,
253 builder: &mut AB,
254 local_slice: &[AB::Var],
255 next_slice: &[AB::Var],
256 ) {
257 let local: &GeneralCols<AB::Var> = local_slice[..GENERAL_WIDTH].borrow();
258 let next: &GeneralCols<AB::Var> = next_slice[..GENERAL_WIDTH].borrow();
259 {
261 builder.assert_bool(local.is_ins_row);
262 builder.assert_bool(local.is_workload_row);
263 builder.assert_bool(local.is_ins_row + local.is_workload_row);
264 }
265 {
267 let mut when_transition = builder.when_transition();
268 let mut when_disabled =
269 when_transition.when_ne(local.is_ins_row + local.is_workload_row, AB::Expr::ONE);
270 when_disabled.assert_zero(next.is_ins_row + next.is_workload_row);
271 }
272 }
273
274 fn eval_workload_row<AB: InteractionBuilder>(
275 &self,
276 builder: &mut AB,
277 local_slice: &[AB::Var],
278 next_slice: &[AB::Var],
279 ) {
280 let local: &WorkloadCols<AB::Var> = local_slice[..WL_WIDTH].borrow();
281 let next: &PrefixCols<AB::Var> = next_slice[..PREFIX_WIDTH].borrow();
282 let local_data = &local.prefix.data;
283 let start_timestamp = next.general.timestamp;
284 let multiplicity = local.prefix.general.is_workload_row;
285 let ptr_reads = AB::F::from_canonical_usize(INSTRUCTION_READS);
287 let native_as = AB::Expr::from_canonical_u32(AS::Native as u32);
288 builder.when(multiplicity).assert_bool(local_data.write_a);
291 self.memory_bridge
293 .read(
294 MemoryAddress::new(native_as.clone(), next.data.a_ptr),
295 [local.prefix.a_or_is_first],
296 start_timestamp + ptr_reads,
297 local.a_aux.as_ref(),
298 )
299 .eval(builder, (AB::Expr::ONE - local_data.write_a) * multiplicity);
300 self.memory_bridge
302 .write(
303 MemoryAddress::new(native_as.clone(), next.data.a_ptr),
304 [local.prefix.a_or_is_first],
305 start_timestamp + ptr_reads,
306 &local.a_aux,
307 )
308 .eval(builder, local_data.write_a * multiplicity);
309 self.memory_bridge
311 .read(
312 MemoryAddress::new(native_as.clone(), next.data.b_ptr),
313 local.b,
314 start_timestamp + ptr_reads + AB::Expr::ONE,
315 &local.b_aux,
316 )
317 .eval(builder, multiplicity);
318 {
319 let mut when_transition = builder.when_transition();
320 let mut builder = when_transition.when(local.prefix.general.is_workload_row);
321 builder.assert_eq(
324 local.prefix.general.timestamp,
325 start_timestamp + AB::Expr::TWO,
326 );
327 builder.assert_eq(local_data.idx + AB::Expr::ONE, next.data.idx);
329 assert_array_eq(&mut builder, local_data.alpha, next.data.alpha);
331 builder.assert_eq(local_data.a_ptr, next.data.a_ptr + AB::F::ONE);
333 builder.assert_eq(local_data.write_a, next.data.write_a);
335 builder.assert_eq(
337 local_data.b_ptr,
338 next.data.b_ptr + AB::F::from_canonical_usize(EXT_DEG),
339 );
340 builder.assert_eq(
342 local.prefix.general.timestamp,
343 next.general.timestamp + AB::Expr::TWO,
344 );
345 let mut expected_result = FieldExtension::multiply(local_data.result, local_data.alpha);
347 expected_result
348 .iter_mut()
349 .zip(local.b.iter())
350 .for_each(|(e, b)| {
351 *e += (*b).into();
352 });
353 expected_result[0] -= local.prefix.a_or_is_first.into();
354 assert_array_eq(&mut builder, expected_result, next.data.result);
355 }
356 {
357 let mut next_ins = builder.when(next.general.is_ins_row);
358 let mut local_non_ins =
359 next_ins.when_ne(local.prefix.general.is_ins_row, AB::Expr::ONE);
360 local_non_ins.assert_one(next.a_or_is_first);
362
363 builder
365 .when(local.prefix.general.is_workload_row)
366 .assert_one(next.general.is_ins_row + next.general.is_workload_row);
367 }
368 {
369 let mut when_first_row = builder.when_first_row();
370 let mut when_enabled = when_first_row
371 .when(local.prefix.general.is_ins_row + local.prefix.general.is_workload_row);
372 when_enabled.assert_one(local.prefix.general.is_workload_row);
374 when_enabled.assert_zero(local.prefix.data.idx);
376 assert_array_eq(
378 &mut when_enabled,
379 local.prefix.data.result,
380 [AB::Expr::ZERO; EXT_DEG],
381 );
382 }
383 }
384
385 fn eval_instruction1_row<AB: InteractionBuilder>(
386 &self,
387 builder: &mut AB,
388 local_slice: &[AB::Var],
389 next_slice: &[AB::Var],
390 ) {
391 let local: &Instruction1Cols<AB::Var> = local_slice[..INS_1_WIDTH].borrow();
392 let next: &Instruction2Cols<AB::Var> = next_slice[..INS_2_WIDTH].borrow();
393 let mut is_ins_row = builder.when(local.prefix.general.is_ins_row);
395 {
397 is_ins_row.assert_eq(
398 local.write_a_x_is_first,
399 local.prefix.data.write_a * local.prefix.a_or_is_first,
400 );
401 is_ins_row.assert_bool(local.write_a_x_is_first);
402 }
403 let mut is_first_ins = is_ins_row.when(local.prefix.a_or_is_first);
404 {
407 is_first_ins.assert_one(next.general.is_ins_row);
408 is_first_ins.assert_zero(next.is_first);
409 }
410
411 let local_data = &local.prefix.data;
412 let length = local.prefix.data.idx;
413 let multiplicity = local.prefix.general.is_ins_row * local.prefix.a_or_is_first;
414 let start_timestamp = local.prefix.general.timestamp;
415 let write_timestamp = start_timestamp
416 + AB::Expr::TWO * length
417 + AB::Expr::from_canonical_usize(INSTRUCTION_READS);
418 let end_timestamp = write_timestamp.clone() + AB::Expr::ONE;
419 let native_as = AB::Expr::from_canonical_u32(AS::Native as u32);
420 self.execution_bridge
421 .execute(
422 AB::F::from_canonical_usize(FRI_REDUCED_OPENING.global_opcode().as_usize()),
423 [
424 local.a_ptr_ptr.into(),
425 local.b_ptr_ptr.into(),
426 next.length_ptr.into(),
427 next.alpha_ptr.into(),
428 next.result_ptr.into(),
429 next.hint_id_ptr.into(),
430 next.is_init_ptr.into(),
431 ],
432 ExecutionState::new(local.pc, local.prefix.general.timestamp),
433 ExecutionState::<AB::Expr>::new(
434 AB::Expr::from_canonical_u32(DEFAULT_PC_STEP) + local.pc,
435 end_timestamp.clone(),
436 ),
437 )
438 .eval(builder, multiplicity.clone());
439 self.memory_bridge
441 .read(
442 MemoryAddress::new(native_as.clone(), next.alpha_ptr),
443 local_data.alpha,
444 start_timestamp,
445 &next.alpha_aux,
446 )
447 .eval(builder, multiplicity.clone());
448 self.memory_bridge
450 .read(
451 MemoryAddress::new(native_as.clone(), next.length_ptr),
452 [length],
453 start_timestamp + AB::Expr::ONE,
454 &next.length_aux,
455 )
456 .eval(builder, multiplicity.clone());
457 self.memory_bridge
459 .read(
460 MemoryAddress::new(native_as.clone(), local.a_ptr_ptr),
461 [local_data.a_ptr],
462 start_timestamp + AB::Expr::TWO,
463 &local.a_ptr_aux,
464 )
465 .eval(builder, multiplicity.clone());
466 self.memory_bridge
468 .read(
469 MemoryAddress::new(native_as.clone(), local.b_ptr_ptr),
470 [local_data.b_ptr],
471 start_timestamp + AB::Expr::from_canonical_u32(3),
472 &local.b_ptr_aux,
473 )
474 .eval(builder, multiplicity.clone());
475 self.memory_bridge
477 .read(
478 MemoryAddress::new(native_as.clone(), next.is_init_ptr),
479 [AB::Expr::ONE - local_data.write_a],
480 start_timestamp + AB::Expr::from_canonical_u32(4),
481 &next.is_init_aux,
482 )
483 .eval(builder, multiplicity.clone());
484 self.memory_bridge
485 .write(
486 MemoryAddress::new(native_as.clone(), next.result_ptr),
487 local_data.result,
488 write_timestamp,
489 &next.result_aux,
490 )
491 .eval(builder, multiplicity.clone());
492 }
493
494 fn eval_instruction2_row<AB: InteractionBuilder>(
495 &self,
496 builder: &mut AB,
497 local_slice: &[AB::Var],
498 next_slice: &[AB::Var],
499 ) {
500 let local: &Instruction2Cols<AB::Var> = local_slice[..INS_2_WIDTH].borrow();
501 let next: &WorkloadCols<AB::Var> = next_slice[..WL_WIDTH].borrow();
502 {
504 let mut last_row = builder.when_last_row();
505 let mut enabled =
506 last_row.when(local.general.is_ins_row + local.general.is_workload_row);
507 enabled.assert_one(local.general.is_ins_row);
508 enabled.assert_zero(local.is_first);
509 }
510 {
511 let mut when_transition = builder.when_transition();
512 let mut is_ins_row = when_transition.when(local.general.is_ins_row);
513 let mut not_first_ins_row = is_ins_row.when_ne(local.is_first, AB::Expr::ONE);
514 not_first_ins_row.assert_zero(next.prefix.general.is_ins_row);
518 not_first_ins_row.assert_zero(next.prefix.data.idx);
520 assert_array_eq(
522 &mut not_first_ins_row,
523 next.prefix.data.result,
524 [AB::Expr::ZERO; EXT_DEG],
525 );
526 }
527 }
528}
529
530fn assert_array_eq<AB: AirBuilder, I1: Into<AB::Expr>, I2: Into<AB::Expr>, const N: usize>(
531 builder: &mut AB,
532 x: [I1; N],
533 y: [I2; N],
534) {
535 for (x, y) in zip_eq(x, y) {
536 builder.assert_eq(x, y);
537 }
538}
539
540fn elem_to_ext<F: Field>(elem: F) -> [F; EXT_DEG] {
541 let mut ret = [F::ZERO; EXT_DEG];
542 ret[0] = elem;
543 ret
544}
545
546#[derive(Serialize, Deserialize)]
547#[serde(bound = "F: Field")]
548pub struct FriReducedOpeningRecord<F: Field> {
549 pub pc: F,
550 pub start_timestamp: F,
551 pub instruction: Instruction<F>,
552 pub alpha_read: RecordId,
553 pub length_read: RecordId,
554 pub a_ptr_read: RecordId,
555 pub is_init_read: RecordId,
556 pub b_ptr_read: RecordId,
557 pub a_rws: Vec<RecordId>,
558 pub b_reads: Vec<RecordId>,
559 pub result_write: RecordId,
560}
561
562impl<F: Field> FriReducedOpeningRecord<F> {
563 pub fn get_height(&self) -> usize {
564 self.a_rws.len() + 2
566 }
567}
568
569pub struct FriReducedOpeningChip<F: Field> {
570 air: FriReducedOpeningAir,
571 pub records: Vec<FriReducedOpeningRecord<F>>,
572 pub height: usize,
573 offline_memory: Arc<Mutex<OfflineMemory<F>>>,
574 streams: Arc<Mutex<Streams<F>>>,
575}
576impl<F: PrimeField32> FriReducedOpeningChip<F> {
577 pub fn new(
578 execution_bus: ExecutionBus,
579 program_bus: ProgramBus,
580 memory_bridge: MemoryBridge,
581 offline_memory: Arc<Mutex<OfflineMemory<F>>>,
582 streams: Arc<Mutex<Streams<F>>>,
583 ) -> Self {
584 let air = FriReducedOpeningAir {
585 execution_bridge: ExecutionBridge::new(execution_bus, program_bus),
586 memory_bridge,
587 };
588 Self {
589 records: vec![],
590 air,
591 height: 0,
592 offline_memory,
593 streams,
594 }
595 }
596}
597impl<F: PrimeField32> InstructionExecutor<F> for FriReducedOpeningChip<F> {
598 fn execute(
599 &mut self,
600 memory: &mut MemoryController<F>,
601 instruction: &Instruction<F>,
602 from_state: ExecutionState<u32>,
603 ) -> Result<ExecutionState<u32>, ExecutionError> {
604 let &Instruction {
605 a: a_ptr_ptr,
606 b: b_ptr_ptr,
607 c: length_ptr,
608 d: alpha_ptr,
609 e: result_ptr,
610 f: hint_id_ptr,
611 g: is_init_ptr,
612 ..
613 } = instruction;
614
615 let addr_space = F::from_canonical_u32(AS::Native as u32);
616 let alpha_read = memory.read(addr_space, alpha_ptr);
617 let length_read = memory.read_cell(addr_space, length_ptr);
618 let a_ptr_read = memory.read_cell(addr_space, a_ptr_ptr);
619 let b_ptr_read = memory.read_cell(addr_space, b_ptr_ptr);
620 let is_init_read = memory.read_cell(addr_space, is_init_ptr);
621 let is_init = is_init_read.1.as_canonical_u32();
622
623 let hint_id_f = memory.unsafe_read_cell(addr_space, hint_id_ptr);
624 let hint_id = hint_id_f.as_canonical_u32() as usize;
625
626 let alpha = alpha_read.1;
627 let length = length_read.1.as_canonical_u32() as usize;
628 let a_ptr = a_ptr_read.1;
629 let b_ptr = b_ptr_read.1;
630
631 let mut a_rws = Vec::with_capacity(length);
632 let mut b_reads = Vec::with_capacity(length);
633 let mut result = [F::ZERO; EXT_DEG];
634
635 let data = if is_init == 0 {
636 let mut streams = self.streams.lock().unwrap();
637 let hint_steam = &mut streams.hint_space[hint_id];
638 hint_steam.drain(0..length).collect()
639 } else {
640 vec![]
641 };
642 #[allow(clippy::needless_range_loop)]
643 for i in 0..length {
644 let a_rw = if is_init == 0 {
645 let (record_id, _) =
646 memory.write_cell(addr_space, a_ptr + F::from_canonical_usize(i), data[i]);
647 (record_id, data[i])
648 } else {
649 memory.read_cell(addr_space, a_ptr + F::from_canonical_usize(i))
650 };
651 let b_read =
652 memory.read::<EXT_DEG>(addr_space, b_ptr + F::from_canonical_usize(EXT_DEG * i));
653 a_rws.push(a_rw);
654 b_reads.push(b_read);
655 }
656
657 for (a_rw, b_read) in a_rws.iter().rev().zip_eq(b_reads.iter().rev()) {
658 let a = a_rw.1;
659 let b = b_read.1;
660 result = FieldExtension::add(
662 FieldExtension::multiply(result, alpha),
663 FieldExtension::subtract(b, elem_to_ext(a)),
664 );
665 }
666
667 let (result_write, _) = memory.write(addr_space, result_ptr, result);
668
669 let record = FriReducedOpeningRecord {
670 pc: F::from_canonical_u32(from_state.pc),
671 start_timestamp: F::from_canonical_u32(from_state.timestamp),
672 instruction: instruction.clone(),
673 alpha_read: alpha_read.0,
674 length_read: length_read.0,
675 a_ptr_read: a_ptr_read.0,
676 is_init_read: is_init_read.0,
677 b_ptr_read: b_ptr_read.0,
678 a_rws: a_rws.into_iter().map(|r| r.0).collect(),
679 b_reads: b_reads.into_iter().map(|r| r.0).collect(),
680 result_write,
681 };
682 self.height += record.get_height();
683 self.records.push(record);
684
685 Ok(ExecutionState {
686 pc: from_state.pc + DEFAULT_PC_STEP,
687 timestamp: memory.timestamp(),
688 })
689 }
690
691 fn get_opcode_name(&self, opcode: usize) -> String {
692 assert_eq!(opcode, FRI_REDUCED_OPENING.global_opcode().as_usize());
693 String::from("FRI_REDUCED_OPENING")
694 }
695}
696
697fn record_to_rows<F: PrimeField32>(
698 record: FriReducedOpeningRecord<F>,
699 aux_cols_factory: &MemoryAuxColsFactory<F>,
700 slice: &mut [F],
701 memory: &OfflineMemory<F>,
702) {
703 let Instruction {
704 a: a_ptr_ptr,
705 b: b_ptr_ptr,
706 c: length_ptr,
707 d: alpha_ptr,
708 e: result_ptr,
709 f: hint_id_ptr,
710 g: is_init_ptr,
711 ..
712 } = record.instruction;
713
714 let length_read = memory.record_by_id(record.length_read);
715 let alpha_read = memory.record_by_id(record.alpha_read);
716 let a_ptr_read = memory.record_by_id(record.a_ptr_read);
717 let b_ptr_read = memory.record_by_id(record.b_ptr_read);
718 let is_init_read = memory.record_by_id(record.is_init_read);
719 let is_init = is_init_read.data_at(0);
720 let write_a = F::ONE - is_init;
721
722 let length = length_read.data_at(0).as_canonical_u32() as usize;
723 let alpha: [F; EXT_DEG] = alpha_read.data_slice().try_into().unwrap();
724 let a_ptr = a_ptr_read.data_at(0);
725 let b_ptr = b_ptr_read.data_at(0);
726
727 let mut result = [F::ZERO; EXT_DEG];
728
729 let alpha_aux = aux_cols_factory.make_read_aux_cols(alpha_read);
730 let length_aux = aux_cols_factory.make_read_aux_cols(length_read);
731 let a_ptr_aux = aux_cols_factory.make_read_aux_cols(a_ptr_read);
732 let b_ptr_aux = aux_cols_factory.make_read_aux_cols(b_ptr_read);
733 let is_init_aux = aux_cols_factory.make_read_aux_cols(is_init_read);
734
735 let result_aux = aux_cols_factory.make_write_aux_cols(memory.record_by_id(record.result_write));
736
737 for (i, (&a_record_id, &b_record_id)) in record
739 .a_rws
740 .iter()
741 .rev()
742 .zip_eq(record.b_reads.iter().rev())
743 .enumerate()
744 {
745 let a_rw = memory.record_by_id(a_record_id);
746 let b_read = memory.record_by_id(b_record_id);
747 let a = a_rw.data_at(0);
748 let b: [F; EXT_DEG] = b_read.data_slice().try_into().unwrap();
749
750 let start = i * OVERALL_WIDTH;
751 let cols: &mut WorkloadCols<F> = slice[start..start + WL_WIDTH].borrow_mut();
752 *cols = WorkloadCols {
753 prefix: PrefixCols {
754 general: GeneralCols {
755 is_workload_row: F::ONE,
756 is_ins_row: F::ZERO,
757 timestamp: record.start_timestamp + F::from_canonical_usize((length - i) * 2),
758 },
759 a_or_is_first: a,
760 data: DataCols {
761 a_ptr: a_ptr + F::from_canonical_usize(length - i),
762 write_a,
763 b_ptr: b_ptr + F::from_canonical_usize((length - i) * EXT_DEG),
764 idx: F::from_canonical_usize(i),
765 result,
766 alpha,
767 },
768 },
769 a_aux: if a_rw.prev_data_slice().is_some() {
772 aux_cols_factory.make_write_aux_cols(a_rw)
773 } else {
774 let read_aux = aux_cols_factory.make_read_aux_cols(a_rw);
775 MemoryWriteAuxCols::from_base(read_aux.get_base(), [F::ZERO])
776 },
777 b,
778 b_aux: aux_cols_factory.make_read_aux_cols(b_read),
779 };
780 result = FieldExtension::add(
782 FieldExtension::multiply(result, alpha),
783 FieldExtension::subtract(b, elem_to_ext(a)),
784 );
785 }
786 {
788 let start = length * OVERALL_WIDTH;
789 let cols: &mut Instruction1Cols<F> = slice[start..start + INS_1_WIDTH].borrow_mut();
790 *cols = Instruction1Cols {
791 prefix: PrefixCols {
792 general: GeneralCols {
793 is_workload_row: F::ZERO,
794 is_ins_row: F::ONE,
795 timestamp: record.start_timestamp,
796 },
797 a_or_is_first: F::ONE,
798 data: DataCols {
799 a_ptr,
800 write_a,
801 b_ptr,
802 idx: F::from_canonical_usize(length),
803 result,
804 alpha,
805 },
806 },
807 pc: record.pc,
808 a_ptr_ptr,
809 a_ptr_aux,
810 b_ptr_ptr,
811 b_ptr_aux,
812 write_a_x_is_first: write_a,
813 };
814 }
815 {
817 let start = (length + 1) * OVERALL_WIDTH;
818 let cols: &mut Instruction2Cols<F> = slice[start..start + INS_2_WIDTH].borrow_mut();
819 *cols = Instruction2Cols {
820 general: GeneralCols {
821 is_workload_row: F::ZERO,
822 is_ins_row: F::ONE,
823 timestamp: record.start_timestamp,
824 },
825 is_first: F::ZERO,
826 length_ptr,
827 length_aux,
828 alpha_ptr,
829 alpha_aux,
830 result_ptr,
831 result_aux,
832 hint_id_ptr,
833 is_init_ptr,
834 is_init_aux,
835 write_a_x_is_first: F::ZERO,
836 };
837 }
838}
839
840impl<F: Field> ChipUsageGetter for FriReducedOpeningChip<F> {
841 fn air_name(&self) -> String {
842 "FriReducedOpeningAir".to_string()
843 }
844
845 fn current_trace_height(&self) -> usize {
846 self.height
847 }
848
849 fn trace_width(&self) -> usize {
850 OVERALL_WIDTH
851 }
852}
853
854impl<SC: StarkGenericConfig> Chip<SC> for FriReducedOpeningChip<Val<SC>>
855where
856 Val<SC>: PrimeField32,
857{
858 fn air(&self) -> AirRef<SC> {
859 Arc::new(self.air)
860 }
861 fn generate_air_proof_input(self) -> AirProofInput<SC> {
862 let height = next_power_of_two_or_zero(self.height);
863 let mut flat_trace = Val::<SC>::zero_vec(OVERALL_WIDTH * height);
864 let chunked_trace = {
865 let sizes: Vec<_> = self
866 .records
867 .par_iter()
868 .map(|record| OVERALL_WIDTH * record.get_height())
869 .collect();
870 variable_chunks_mut(&mut flat_trace, &sizes)
871 };
872
873 let memory = self.offline_memory.lock().unwrap();
874 let aux_cols_factory = memory.aux_cols_factory();
875
876 self.records
877 .into_par_iter()
878 .zip_eq(chunked_trace.into_par_iter())
879 .for_each(|(record, slice)| {
880 record_to_rows(record, &aux_cols_factory, slice, &memory);
881 });
882
883 let matrix = RowMajorMatrix::new(flat_trace, OVERALL_WIDTH);
884 AirProofInput::simple_no_pis(matrix)
885 }
886}
887
888fn variable_chunks_mut<'a, T>(mut slice: &'a mut [T], sizes: &[usize]) -> Vec<&'a mut [T]> {
889 let mut result = Vec::with_capacity(sizes.len());
890 for &size in sizes {
891 let (left, right) = slice.split_at_mut(size);
893 result.push(left);
894 slice = right; }
896 result
897}