diff --git a/air/src/logup_gkr.rs b/air/src/logup_gkr.rs index f709a02d1..3defdb8c7 100644 --- a/air/src/logup_gkr.rs +++ b/air/src/logup_gkr.rs @@ -7,8 +7,8 @@ use winter_air::{EvaluationFrame, LogUpGkrEvaluator, LogUpGkrOracle}; use crate::{ decoder::{ DECODER_ADDR_COL_IDX, DECODER_GROUP_COUNT_COL_IDX, DECODER_HASHER_STATE_OFFSET, - DECODER_IN_SPAN_COL_IDX, DECODER_OP_BATCH_FLAGS_OFFSET, DECODER_OP_BITS_EXTRA_COLS_OFFSET, - DECODER_OP_BITS_OFFSET, DECODER_USER_OP_HELPERS_OFFSET, + DECODER_IN_SPAN_COL_IDX, DECODER_IS_LOOP_BODY_FLAG_COL_IDX, DECODER_OP_BATCH_FLAGS_OFFSET, + DECODER_OP_BITS_EXTRA_COLS_OFFSET, DECODER_OP_BITS_OFFSET, DECODER_USER_OP_HELPERS_OFFSET, }, trace::{ chiplets::{MEMORY_D0_COL_IDX, MEMORY_D1_COL_IDX}, @@ -29,8 +29,13 @@ pub const RANGE_CHECKER_NUM_RAND_VALUES: usize = 1; pub const OP_GROUP_TABLE_RAND_VALUES_OFFSET: usize = 1; pub const OP_GROUP_TABLE_NUM_RAND_VALUES: usize = 4; -pub const TOTAL_NUM_RAND_VALUES: usize = +pub const BLOCK_HASH_TABLE_RAND_VALUES_OFFSET: usize = OP_GROUP_TABLE_RAND_VALUES_OFFSET + OP_GROUP_TABLE_NUM_RAND_VALUES; +pub const BLOCK_HASH_TABLE_NUM_RAND_VALUES: usize = 8; + +pub const TOTAL_NUM_RAND_VALUES: usize = + BLOCK_HASH_TABLE_RAND_VALUES_OFFSET + BLOCK_HASH_TABLE_NUM_RAND_VALUES; + // Fractions pub const RANGE_CHECKER_FRACTIONS_OFFSET: usize = 0; @@ -40,8 +45,12 @@ pub const OP_GROUP_TABLE_FRACTIONS_OFFSET: usize = RANGE_CHECKER_FRACTIONS_OFFSET + RANGE_CHECKER_NUM_FRACTIONS; pub const OP_GROUP_TABLE_NUM_FRACTIONS: usize = 12; -pub const PADDING_FRACTIONS_OFFSET: usize = +pub const BLOCK_HASH_TABLE_FRACTIONS_OFFSET: usize = OP_GROUP_TABLE_FRACTIONS_OFFSET + OP_GROUP_TABLE_NUM_FRACTIONS; +pub const BLOCK_HASH_TABLE_NUM_FRACTIONS: usize = 8; + +pub const PADDING_FRACTIONS_OFFSET: usize = + BLOCK_HASH_TABLE_FRACTIONS_OFFSET + BLOCK_HASH_TABLE_NUM_FRACTIONS; pub const PADDING_NUM_FRACTIONS: usize = TOTAL_NUM_FRACTIONS - PADDING_FRACTIONS_OFFSET; pub const TOTAL_NUM_FRACTIONS: usize = 32; @@ -118,6 +127,7 @@ impl LogUpGkrEvaluator for MidenLogUpGkrEval { let query_next = &query[TRACE_WIDTH..]; let op_flags_current = LogUpOpFlags::new(query_current); + let op_flags_next = LogUpOpFlags::new(query_next); range_checker( query_current, @@ -134,23 +144,41 @@ impl LogUpGkrEvaluator for MidenLogUpGkrEval { &mut numerator[range(OP_GROUP_TABLE_FRACTIONS_OFFSET, OP_GROUP_TABLE_NUM_FRACTIONS)], &mut denominator[range(OP_GROUP_TABLE_FRACTIONS_OFFSET, OP_GROUP_TABLE_NUM_FRACTIONS)], ); + block_hash_table( + query_current, + query_next, + &op_flags_current, + &op_flags_next, + &rand_values + [range(BLOCK_HASH_TABLE_RAND_VALUES_OFFSET, BLOCK_HASH_TABLE_NUM_RAND_VALUES)], + &mut numerator + [range(BLOCK_HASH_TABLE_FRACTIONS_OFFSET, BLOCK_HASH_TABLE_NUM_FRACTIONS)], + &mut denominator + [range(BLOCK_HASH_TABLE_FRACTIONS_OFFSET, BLOCK_HASH_TABLE_NUM_FRACTIONS)], + ); padding( &mut numerator[range(PADDING_FRACTIONS_OFFSET, PADDING_NUM_FRACTIONS)], &mut denominator[range(PADDING_FRACTIONS_OFFSET, PADDING_NUM_FRACTIONS)], ); } - fn compute_claim(&self, _inputs: &Self::PublicInputs, _rand_values: &[E]) -> E + fn compute_claim(&self, inputs: &Self::PublicInputs, rand_values: &[E]) -> E where E: FieldElement, { - E::ZERO + // block hash table + let block_hash_table_claim = { + let alphas = &rand_values + [range(BLOCK_HASH_TABLE_RAND_VALUES_OFFSET, BLOCK_HASH_TABLE_NUM_RAND_VALUES)]; + let program_hash = inputs.program_info.program_hash(); + + -(alphas[0] + inner_product(&alphas[2..6], program_hash.as_elements())) + }; + + block_hash_table_claim.inv() } } -// HELPERS -// ----------------------------------------------------------------------------------------------- - /// TODO(plafer): docs #[inline(always)] fn range_checker( @@ -289,6 +317,78 @@ fn op_group_table( denominator[11] = v7; } +#[inline(always)] +fn block_hash_table( + query_current: &[F], + query_next: &[F], + op_flags_current: &LogUpOpFlags, + op_flags_next: &LogUpOpFlags, + alphas: &[E], + numerator: &mut [E], + denominator: &mut [E], +) where + F: FieldElement, + E: FieldElement + ExtensionOf, +{ + let stack_0 = query_current[STACK_TRACE_OFFSET + STACK_TOP_OFFSET]; + + // numerators + let f_join: E = op_flags_current.f_join().into(); + + numerator[0] = op_flags_current.f_end().into(); + numerator[1] = f_join; + numerator[2] = f_join; + numerator[3] = op_flags_current.f_split().into(); + numerator[4] = (op_flags_current.f_loop() * stack_0).into(); + numerator[5] = op_flags_current.f_repeat().into(); + numerator[6] = op_flags_current.f_dyn().into(); + // TODO(plafer): update docs (no mention of call or syscall) + numerator[7] = (op_flags_current.f_call() + op_flags_current.f_syscall()).into(); + + // denominators + let addr_next = query_next[DECODER_ADDR_COL_IDX]; + let h0_to_3 = &query_current[range(DECODER_HASHER_STATE_OFFSET, 4)]; + let h4_to_7 = &query_current[range(DECODER_HASHER_STATE_OFFSET + 4, 4)]; + let stack_1 = query_current[STACK_TRACE_OFFSET + STACK_TOP_OFFSET + 1]; + let stack_2 = query_current[STACK_TRACE_OFFSET + STACK_TOP_OFFSET + 2]; + let stack_3 = query_current[STACK_TRACE_OFFSET + STACK_TOP_OFFSET + 3]; + // TODO(plafer): update docs (this is h4 in docs) + let f_is_loop_body = query_current[DECODER_IS_LOOP_BODY_FLAG_COL_IDX]; + let child1 = alphas[0] + alphas[1].mul_base(addr_next) + inner_product(&alphas[2..6], h0_to_3); + let child2 = alphas[0] + alphas[1].mul_base(addr_next) + inner_product(&alphas[2..6], h4_to_7); + + let u_end = { + // TODO(plafer): update docs (f_halt missing) + let is_first_child = + F::ONE - (op_flags_next.f_end() + op_flags_next.f_repeat() + op_flags_next.f_halt()); + + // TODO(plafer): Double check addr_next; docs inconsistent with BlockHashTableRow + alphas[0] + + alphas[1].mul_base(addr_next) + + inner_product(&alphas[2..6], h0_to_3) + + alphas[6].mul_base(is_first_child) + + alphas[7].mul_base(f_is_loop_body) + }; + + let v_join_1 = child1 + alphas[6]; + let v_join_2 = child2; + let v_split = child1.mul_base(stack_0) + child2.mul_base(F::ONE - stack_0); + let v_loop = child1 + alphas[7]; + let v_repeat = child1 + alphas[7]; + let v_dyn = alphas[0] + + alphas[1].mul_base(addr_next) + + inner_product(&alphas[2..6], &[stack_3, stack_2, stack_1, stack_0]); + + denominator[0] = -u_end; + denominator[1] = v_join_1; + denominator[2] = v_join_2; + denominator[3] = v_split; + denominator[4] = v_loop; + denominator[5] = v_repeat; + denominator[6] = v_dyn; + denominator[7] = child1; +} + /// TODO(plafer): docs fn padding(numerator: &mut [E], denominator: &mut [E]) where @@ -308,6 +408,7 @@ struct LogUpOpFlags { b5: F, b6: F, e0: F, + e1: F, } impl LogUpOpFlags { @@ -321,6 +422,7 @@ impl LogUpOpFlags { b5: query[DECODER_OP_BITS_OFFSET + 5], b6: query[DECODER_OP_BITS_OFFSET + 6], e0: query[DECODER_OP_BITS_EXTRA_COLS_OFFSET], + e1: query[DECODER_OP_BITS_EXTRA_COLS_OFFSET + 1], } } @@ -349,4 +451,54 @@ impl LogUpOpFlags { pub fn f_range_check(&self) -> F { (F::ONE - self.b4) * (F::ONE - self.b5) * self.b6 } + + pub fn f_join(&self) -> F { + self.e0 * (F::ONE - self.b3) * self.b2 * self.b1 * self.b0 + } + + pub fn f_split(&self) -> F { + self.e0 * (F::ONE - self.b3) * self.b2 * (F::ONE - self.b1) * (F::ONE - self.b0) + } + + pub fn f_loop(&self) -> F { + self.e0 * (F::ONE - self.b3) * self.b2 * (F::ONE - self.b1) * self.b0 + } + + pub fn f_dyn(&self) -> F { + self.e0 * self.b3 * (F::ONE - self.b2) * (F::ONE - self.b1) * (F::ONE - self.b0) + } + + pub fn f_repeat(&self) -> F { + self.e1 * self.b4 * (F::ONE - self.b3) * self.b2 + } + + pub fn f_end(&self) -> F { + self.e1 * self.b4 * (F::ONE - self.b3) * (F::ONE - self.b2) + } + + pub fn f_syscall(&self) -> F { + self.e1 * (F::ONE - self.b4) * self.b3 * (F::ONE - self.b2) + } + + pub fn f_call(&self) -> F { + self.e1 * (F::ONE - self.b4) * self.b3 * self.b2 + } + + pub fn f_halt(&self) -> F { + self.e1 * self.b4 * self.b3 * self.b2 + } +} + +// HELPERS +// ----------------------------------------------------------------------------------------------- + +fn inner_product(alphas: &[E], eles: &[F]) -> E +where + F: FieldElement, + E: FieldElement + ExtensionOf, +{ + alphas + .iter() + .zip(eles.iter()) + .fold(E::ZERO, |acc, (alpha, ele)| acc + alpha.mul_base(*ele)) } diff --git a/air/src/trace/decoder/mod.rs b/air/src/trace/decoder/mod.rs index 278c2d611..529ac7c08 100644 --- a/air/src/trace/decoder/mod.rs +++ b/air/src/trace/decoder/mod.rs @@ -94,9 +94,6 @@ pub const IS_SYSCALL_FLAG_COL_IDX: usize = HASHER_STATE_RANGE.start + 7; /// Running product column representing block stack table. pub const P1_COL_IDX: usize = DECODER_AUX_TRACE_OFFSET; -/// Running product column representing block hash table -pub const P2_COL_IDX: usize = DECODER_AUX_TRACE_OFFSET + 1; - // --- GLOBALLY-INDEXED DECODER COLUMN ACCESSORS -------------------------------------------------- pub const DECODER_ADDR_COL_IDX: usize = super::DECODER_TRACE_OFFSET + ADDR_COL_IDX; pub const DECODER_OP_BITS_OFFSET: usize = super::DECODER_TRACE_OFFSET + OP_BITS_OFFSET; @@ -109,3 +106,5 @@ pub const DECODER_OP_BATCH_FLAGS_OFFSET: usize = super::DECODER_TRACE_OFFSET + OP_BATCH_FLAGS_OFFSET; pub const DECODER_OP_BITS_EXTRA_COLS_OFFSET: usize = super::DECODER_TRACE_OFFSET + OP_BITS_EXTRA_COLS_OFFSET; +pub const DECODER_IS_LOOP_BODY_FLAG_COL_IDX: usize = + super::DECODER_TRACE_OFFSET + IS_LOOP_BODY_FLAG_COL_IDX; diff --git a/air/src/trace/mod.rs b/air/src/trace/mod.rs index 893bd26d8..c96ae7f32 100644 --- a/air/src/trace/mod.rs +++ b/air/src/trace/mod.rs @@ -60,12 +60,12 @@ pub const TRACE_WIDTH: usize = CHIPLETS_OFFSET + CHIPLETS_WIDTH; // ------------------------------------------------------------------------------------------------ // decoder stack hasher chiplets -// (2 columns) (1 column) (1 column) (1 column) +// (1 columns) (1 column) (1 column) (1 column) // ├───────────────┴──────────────┴─────────────────┴───────────────┤ // Decoder auxiliary columns pub const DECODER_AUX_TRACE_OFFSET: usize = 0; -pub const DECODER_AUX_TRACE_WIDTH: usize = 2; +pub const DECODER_AUX_TRACE_WIDTH: usize = 1; pub const DECODER_AUX_TRACE_RANGE: Range = range(DECODER_AUX_TRACE_OFFSET, DECODER_AUX_TRACE_WIDTH); diff --git a/processor/src/decoder/aux_trace/block_hash_table.rs b/processor/src/decoder/aux_trace/block_hash_table.rs deleted file mode 100644 index cba054d0b..000000000 --- a/processor/src/decoder/aux_trace/block_hash_table.rs +++ /dev/null @@ -1,267 +0,0 @@ -use miden_air::RowIndex; -use vm_core::{ - Word, OPCODE_DYN, OPCODE_END, OPCODE_HALT, OPCODE_JOIN, OPCODE_LOOP, OPCODE_REPEAT, - OPCODE_SPLIT, ZERO, -}; - -use super::{AuxColumnBuilder, Felt, FieldElement, MainTrace, ONE}; - -// BLOCK HASH TABLE COLUMN BUILDER -// ================================================================================================ - -/// Builds the execution trace of the decoder's `p2` column which describes the state of the block -/// hash table via multiset checks. -/// -/// At any point in time, the block hash table contains the hashes of the blocks whose parents have -/// been visited, and that remain to be executed. For example, when we encounter the beginning of a -/// JOIN block, we add both children to the table, since both will be executed at some point in the -/// future. However, when we encounter the beginning of a SPLIT block, we only push the left or the -/// right child, depending on the current value on the stack (since only one child gets executed in -/// a SPLIT block). When we encounter an `END` operation, we remove the block from the table that -/// corresponds to the block that just ended. The table is initialized with the root block's hash, -/// since it doesn't have a parent, and so would never be added to the table otherwise. -#[derive(Default)] -pub struct BlockHashTableColumnBuilder {} - -impl> AuxColumnBuilder for BlockHashTableColumnBuilder { - fn init_responses(&self, main_trace: &MainTrace, alphas: &[E]) -> E { - BlockHashTableRow::table_init(main_trace).collapse(alphas) - } - - /// Removes a row from the block hash table. - fn get_requests_at(&self, main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E { - let op_code = main_trace.get_op_code(row).as_int() as u8; - - match op_code { - OPCODE_END => BlockHashTableRow::from_end(main_trace, row).collapse(alphas), - _ => E::ONE, - } - } - - /// Adds a row to the block hash table. - fn get_responses_at(&self, main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E { - let op_code = main_trace.get_op_code(row).as_int() as u8; - - match op_code { - OPCODE_JOIN => { - let left_child_row = BlockHashTableRow::from_join(main_trace, row, true); - let right_child_row = BlockHashTableRow::from_join(main_trace, row, false); - - // Note: this adds the 2 rows separately to the block hash table. - left_child_row.collapse(alphas) * right_child_row.collapse(alphas) - }, - OPCODE_SPLIT => BlockHashTableRow::from_split(main_trace, row).collapse(alphas), - OPCODE_LOOP => BlockHashTableRow::from_loop(main_trace, row) - .map(|row| row.collapse(alphas)) - .unwrap_or(E::ONE), - OPCODE_REPEAT => BlockHashTableRow::from_repeat(main_trace, row).collapse(alphas), - OPCODE_DYN => BlockHashTableRow::from_dyn(main_trace, row).collapse(alphas), - _ => E::ONE, - } - } -} - -// BLOCK HASH TABLE ROW -// ================================================================================================ - -/// Describes a single entry in the block hash table. An entry in the block hash table is a tuple -/// (parent_id, block_hash, is_first_child, is_loop_body), where each column is defined as follows: -/// - parent_block_id: contains the ID of the current block. Note: the current block's ID is the -/// parent block's ID from the perspective of the block being added to the table. -/// - block_hash: these 4 columns hold the hash of the current block's child which will be executed -/// at some point in time in the future. -/// - is_first_child: set to true if the table row being added represents the first child of the -/// current block. If the current block has only one child, set to false. -/// - is_loop_body: Set to true when the current block block is a LOOP code block (and hence, the -/// current block's child being added to the table is the body of a loop). -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct BlockHashTableRow { - parent_block_id: Felt, - child_block_hash: Word, - is_first_child: bool, - is_loop_body: bool, -} - -impl BlockHashTableRow { - // CONSTRUCTORS - // ---------------------------------------------------------------------------------------------- - - // Instantiates the initial row in the block hash table. - pub fn table_init(main_trace: &MainTrace) -> Self { - let program_hash = - main_trace.decoder_hasher_state_first_half(main_trace.last_program_row()); - Self { - parent_block_id: ZERO, - child_block_hash: program_hash, - is_first_child: false, - is_loop_body: false, - } - } - - /// Computes the row to be removed from the block hash table when encountering an `END` - /// operation. - pub fn from_end(main_trace: &MainTrace, row: RowIndex) -> Self { - let op_code_next = main_trace.get_op_code(row + 1).as_int() as u8; - let parent_block_id = main_trace.addr(row + 1); - let child_block_hash = main_trace.decoder_hasher_state_first_half(row); - - // A block can only be a first child of a JOIN block; every other control block only - // executes one child. Hence, it is easier to identify the conditions that only a - // "second child" (i.e. a JOIN block's second child, as well as all other control - // block's child) can find itself in. That is, when the opcode of the next row is: - // - END: this marks the end of the parent block, which only a second child can be in - // - REPEAT: this means that the current block is the child of a LOOP block, and hence a - // "second child" - // - HALT: The end of the program, which a first child can't find itself in (since the - // second child needs to execute first) - let is_first_child = op_code_next != OPCODE_END - && op_code_next != OPCODE_REPEAT - && op_code_next != OPCODE_HALT; - let is_loop_body = main_trace - .is_loop_body_flag(row) - .try_into() - .expect("expected loop body flag to be a boolean"); - - Self { - parent_block_id, - child_block_hash, - is_first_child, - is_loop_body, - } - } - - /// Computes the row corresponding to the left or right child to add to the block hash table - /// when encountering a `JOIN` operation. - pub fn from_join(main_trace: &MainTrace, row: RowIndex, is_first_child: bool) -> Self { - let child_block_hash = if is_first_child { - main_trace.decoder_hasher_state_first_half(row) - } else { - main_trace.decoder_hasher_state_second_half(row) - }; - - Self { - parent_block_id: main_trace.addr(row + 1), - child_block_hash, - is_first_child, - is_loop_body: false, - } - } - - /// Computes the row to add to the block hash table when encountering a `SPLIT` operation. - pub fn from_split(main_trace: &MainTrace, row: RowIndex) -> Self { - let stack_top = main_trace.stack_element(0, row); - let parent_block_id = main_trace.addr(row + 1); - // Note: only one child of a split block is executed. Hence, `is_first_child` is always - // false. - let is_first_child = false; - let is_loop_body = false; - - if stack_top == ONE { - let left_child_block_hash = main_trace.decoder_hasher_state_first_half(row); - Self { - parent_block_id, - child_block_hash: left_child_block_hash, - is_first_child, - is_loop_body, - } - } else { - let right_child_block_hash = main_trace.decoder_hasher_state_second_half(row); - - Self { - parent_block_id, - child_block_hash: right_child_block_hash, - is_first_child, - is_loop_body, - } - } - } - - /// Computes the row (optionally) to add to the block hash table when encountering a `LOOP` - /// operation. That is, a loop will have a child to execute when the top of the stack is 1. - pub fn from_loop(main_trace: &MainTrace, row: RowIndex) -> Option { - let stack_top = main_trace.stack_element(0, row); - - if stack_top == ONE { - Some(Self { - parent_block_id: main_trace.addr(row + 1), - child_block_hash: main_trace.decoder_hasher_state_first_half(row), - is_first_child: false, - is_loop_body: true, - }) - } else { - None - } - } - - /// Computes the row to add to the block hash table when encountering a `REPEAT` operation. A - /// `REPEAT` marks the start of a new loop iteration, and hence the loop's child block needs to - /// be added to the block hash table once again (since it was removed in the previous `END` - /// instruction). - pub fn from_repeat(main_trace: &MainTrace, row: RowIndex) -> Self { - Self { - parent_block_id: main_trace.addr(row + 1), - child_block_hash: main_trace.decoder_hasher_state_first_half(row), - is_first_child: false, - is_loop_body: true, - } - } - - /// Computes the row to add to the block hash table when encountering a `DYN` operation. - pub fn from_dyn(main_trace: &MainTrace, row: RowIndex) -> Self { - let child_block_hash = { - // Note: the child block hash is found on the stack, and hence in reverse order. - let s0 = main_trace.stack_element(0, row); - let s1 = main_trace.stack_element(1, row); - let s2 = main_trace.stack_element(2, row); - let s3 = main_trace.stack_element(3, row); - - [s3, s2, s1, s0] - }; - - Self { - parent_block_id: main_trace.addr(row + 1), - child_block_hash, - is_first_child: false, - is_loop_body: false, - } - } - - // COLLAPSE - // ---------------------------------------------------------------------------------------------- - - /// Collapses this row to a single field element in the field specified by E by taking a random - /// linear combination of all the columns. This requires 8 alpha values, which are assumed to - /// have been drawn randomly. - pub fn collapse>(&self, alphas: &[E]) -> E { - let is_first_child = if self.is_first_child { ONE } else { ZERO }; - let is_loop_body = if self.is_loop_body { ONE } else { ZERO }; - alphas[0] - + alphas[1].mul_base(self.parent_block_id) - + alphas[2].mul_base(self.child_block_hash[0]) - + alphas[3].mul_base(self.child_block_hash[1]) - + alphas[4].mul_base(self.child_block_hash[2]) - + alphas[5].mul_base(self.child_block_hash[3]) - + alphas[6].mul_base(is_first_child) - + alphas[7].mul_base(is_loop_body) - } - - // TEST - // ---------------------------------------------------------------------------------------------- - - /// Returns a new [BlockHashTableRow] instantiated with the specified parameters. This is - /// used for test purpose only. - #[cfg(test)] - pub fn new_test( - parent_id: Felt, - block_hash: Word, - is_first_child: bool, - is_loop_body: bool, - ) -> Self { - Self { - parent_block_id: parent_id, - child_block_hash: block_hash, - is_first_child, - is_loop_body, - } - } -} diff --git a/processor/src/decoder/aux_trace/mod.rs b/processor/src/decoder/aux_trace/mod.rs index 3d752c346..cefcbf502 100644 --- a/processor/src/decoder/aux_trace/mod.rs +++ b/processor/src/decoder/aux_trace/mod.rs @@ -6,11 +6,6 @@ use vm_core::FieldElement; use super::{Felt, ONE, ZERO}; use crate::trace::AuxColumnBuilder; -mod block_hash_table; -use block_hash_table::BlockHashTableColumnBuilder; -#[cfg(test)] -pub use block_hash_table::BlockHashTableRow; - mod block_stack_table; use block_stack_table::BlockStackColumnBuilder; @@ -31,11 +26,8 @@ impl AuxTraceBuilder { rand_elements: &[E], ) -> Vec> { let block_stack_column_builder = BlockStackColumnBuilder::default(); - let block_hash_column_builder = BlockHashTableColumnBuilder::default(); - let p1 = block_stack_column_builder.build_aux_column(main_trace, rand_elements); - let p2 = block_hash_column_builder.build_aux_column(main_trace, rand_elements); - vec![p1, p2] + vec![p1] } } diff --git a/processor/src/decoder/mod.rs b/processor/src/decoder/mod.rs index 3e677a3b4..16e283168 100644 --- a/processor/src/decoder/mod.rs +++ b/processor/src/decoder/mod.rs @@ -28,8 +28,6 @@ use trace::DecoderTrace; mod aux_trace; pub use aux_trace::AuxTraceBuilder; -#[cfg(test)] -pub use aux_trace::BlockHashTableRow; mod block_stack; use block_stack::{BlockStack, BlockType, ExecutionContextInfo}; diff --git a/processor/src/trace/tests/decoder.rs b/processor/src/trace/tests/decoder.rs index eaee5fb6d..d15957220 100644 --- a/processor/src/trace/tests/decoder.rs +++ b/processor/src/trace/tests/decoder.rs @@ -1,12 +1,6 @@ -use miden_air::trace::{ - decoder::{P1_COL_IDX, P2_COL_IDX}, - AUX_TRACE_RAND_ELEMENTS, -}; +use miden_air::trace::{decoder::P1_COL_IDX, AUX_TRACE_RAND_ELEMENTS}; use test_utils::rand::rand_array; -use vm_core::{ - mast::{MastForest, MastNode}, - FieldElement, Operation, Program, Word, ONE, ZERO, -}; +use vm_core::{mast::MastForest, FieldElement, Operation, Program, Word, ONE, ZERO}; use super::{ super::{ @@ -15,7 +9,7 @@ use super::{ }, Felt, }; -use crate::{decoder::BlockHashTableRow, ContextId}; +use crate::ContextId; // BLOCK STACK TABLE TESTS // ================================================================================================ @@ -305,337 +299,6 @@ fn decoder_p1_loop_with_repeat() { } } -// BLOCK HASH TABLE TESTS -// ================================================================================================ - -#[test] -#[allow(clippy::needless_range_loop)] -fn decoder_p2_span_with_respan() { - let program = { - let mut mast_forest = MastForest::new(); - - let (ops, _) = build_span_with_respan_ops(); - let basic_block_id = mast_forest.add_block(ops, None).unwrap(); - mast_forest.make_root(basic_block_id); - - Program::new(mast_forest.into(), basic_block_id) - }; - let trace = build_trace_from_program(&program, &[]); - let alphas = rand_array::(); - let aux_columns = trace.build_aux_trace(&alphas).unwrap(); - let p2 = aux_columns.get_column(P2_COL_IDX); - - let row_values = [ - BlockHashTableRow::new_test(ZERO, program.hash().into(), false, false).collapse(&alphas) - ]; - - // make sure the first entry is initialized to program hash - let mut expected_value = row_values[0]; - assert_eq!(expected_value, p2[0]); - - // as operations inside the span execute (including RESPAN), the table is not affected - for i in 1..22 { - assert_eq!(expected_value, p2[i]); - } - - // at cycle 22, the END operation is executed and the table is cleared - expected_value *= row_values[0].inv(); - assert_eq!(expected_value, ONE); - for i in 22..p2.len() { - assert_eq!(ONE, p2[i]); - } -} - -#[test] -#[allow(clippy::needless_range_loop)] -fn decoder_p2_join() { - let mut mast_forest = MastForest::new(); - - let basic_block_1 = MastNode::new_basic_block(vec![Operation::Mul], None).unwrap(); - let basic_block_1_id = mast_forest.add_node(basic_block_1.clone()).unwrap(); - - let basic_block_2 = MastNode::new_basic_block(vec![Operation::Add], None).unwrap(); - let basic_block_2_id = mast_forest.add_node(basic_block_2.clone()).unwrap(); - - let join = MastNode::new_join(basic_block_1_id, basic_block_2_id, &mast_forest).unwrap(); - let join_id = mast_forest.add_node(join.clone()).unwrap(); - mast_forest.make_root(join_id); - - let program = Program::new(mast_forest.into(), join_id); - - let trace = build_trace_from_program(&program, &[]); - let alphas = rand_array::(); - let aux_columns = trace.build_aux_trace(&alphas).unwrap(); - let p2 = aux_columns.get_column(P2_COL_IDX); - - let row_values = [ - BlockHashTableRow::new_test(ZERO, join.digest().into(), false, false).collapse(&alphas), - BlockHashTableRow::new_test(ONE, basic_block_1.digest().into(), true, false) - .collapse(&alphas), - BlockHashTableRow::new_test(ONE, basic_block_2.digest().into(), false, false) - .collapse(&alphas), - ]; - - // make sure the first entry is initialized to program hash - let mut expected_value = row_values[0]; - assert_eq!(expected_value, p2[0]); - - // when JOIN operation is executed, entries for both children are added to the table - expected_value *= row_values[1] * row_values[2]; - assert_eq!(expected_value, p2[1]); - - // for the next 2 cycles, the table is not affected - assert_eq!(expected_value, p2[2]); - assert_eq!(expected_value, p2[3]); - - // when the first SPAN block ends, its entry is removed from the table - expected_value *= row_values[1].inv(); - assert_eq!(expected_value, p2[4]); - - // for the next 2 cycles, the table is not affected - assert_eq!(expected_value, p2[5]); - assert_eq!(expected_value, p2[6]); - - // when the second SPAN block ends, its entry is removed from the table - expected_value *= row_values[2].inv(); - assert_eq!(expected_value, p2[7]); - - // when the JOIN block ends, its entry is removed from the table - expected_value *= row_values[0].inv(); - assert_eq!(expected_value, p2[8]); - - // at this point the table should be empty, and thus, all subsequent values must be ONE - assert_eq!(expected_value, ONE); - for i in 9..p2.len() { - assert_eq!(ONE, p2[i]); - } -} - -#[test] -#[allow(clippy::needless_range_loop)] -fn decoder_p2_split_true() { - // build program - let mut mast_forest = MastForest::new(); - - let basic_block_1 = MastNode::new_basic_block(vec![Operation::Mul], None).unwrap(); - let basic_block_1_id = mast_forest.add_node(basic_block_1.clone()).unwrap(); - let basic_block_2_id = mast_forest.add_block(vec![Operation::Add], None).unwrap(); - let split_id = mast_forest.add_split(basic_block_1_id, basic_block_2_id).unwrap(); - mast_forest.make_root(split_id); - - let program = Program::new(mast_forest.into(), split_id); - - // build trace from program - let trace = build_trace_from_program(&program, &[1]); - let alphas = rand_array::(); - let aux_columns = trace.build_aux_trace(&alphas).unwrap(); - let p2 = aux_columns.get_column(P2_COL_IDX); - - let row_values = [ - BlockHashTableRow::new_test(ZERO, program.hash().into(), false, false).collapse(&alphas), - BlockHashTableRow::new_test(ONE, basic_block_1.digest().into(), false, false) - .collapse(&alphas), - ]; - - // make sure the first entry is initialized to program hash - let mut expected_value = row_values[0]; - assert_eq!(expected_value, p2[0]); - - // when SPLIT operation is executed, entry for the true branch is added to the table - expected_value *= row_values[1]; - assert_eq!(expected_value, p2[1]); - - // for the next 2 cycles, the table is not affected - assert_eq!(expected_value, p2[2]); - assert_eq!(expected_value, p2[3]); - - // when the SPAN block ends, its entry is removed from the table - expected_value *= row_values[1].inv(); - assert_eq!(expected_value, p2[4]); - - // when the SPLIT block ends, its entry is removed from the table - expected_value *= row_values[0].inv(); - assert_eq!(expected_value, p2[5]); - - // at this point the table should be empty, and thus, all subsequent values must be ONE - assert_eq!(expected_value, ONE); - for i in 6..p2.len() { - assert_eq!(ONE, p2[i]); - } -} - -#[test] -#[allow(clippy::needless_range_loop)] -fn decoder_p2_split_false() { - // build program - let mut mast_forest = MastForest::new(); - - let basic_block_1 = MastNode::new_basic_block(vec![Operation::Mul], None).unwrap(); - let basic_block_1_id = mast_forest.add_node(basic_block_1.clone()).unwrap(); - - let basic_block_2 = MastNode::new_basic_block(vec![Operation::Add], None).unwrap(); - let basic_block_2_id = mast_forest.add_node(basic_block_2.clone()).unwrap(); - - let split_id = mast_forest.add_split(basic_block_1_id, basic_block_2_id).unwrap(); - mast_forest.make_root(split_id); - - let program = Program::new(mast_forest.into(), split_id); - - // build trace from program - let trace = build_trace_from_program(&program, &[0]); - let alphas = rand_array::(); - let aux_columns = trace.build_aux_trace(&alphas).unwrap(); - let p2 = aux_columns.get_column(P2_COL_IDX); - - let row_values = [ - BlockHashTableRow::new_test(ZERO, program.hash().into(), false, false).collapse(&alphas), - BlockHashTableRow::new_test(ONE, basic_block_2.digest().into(), false, false) - .collapse(&alphas), - ]; - - // make sure the first entry is initialized to program hash - let mut expected_value = row_values[0]; - assert_eq!(expected_value, p2[0]); - - // when SPLIT operation is executed, entry for the false branch is added to the table - expected_value *= row_values[1]; - assert_eq!(expected_value, p2[1]); - - // for the next 2 cycles, the table is not affected - assert_eq!(expected_value, p2[2]); - assert_eq!(expected_value, p2[3]); - - // when the SPAN block ends, its entry is removed from the table - expected_value *= row_values[1].inv(); - assert_eq!(expected_value, p2[4]); - - // when the SPLIT block ends, its entry is removed from the table - expected_value *= row_values[0].inv(); - assert_eq!(expected_value, p2[5]); - - // at this point the table should be empty, and thus, all subsequent values must be ONE - assert_eq!(expected_value, ONE); - for i in 6..p2.len() { - assert_eq!(ONE, p2[i]); - } -} - -#[test] -#[allow(clippy::needless_range_loop)] -fn decoder_p2_loop_with_repeat() { - // build program - let mut mast_forest = MastForest::new(); - - let basic_block_1 = MastNode::new_basic_block(vec![Operation::Pad], None).unwrap(); - let basic_block_1_id = mast_forest.add_node(basic_block_1.clone()).unwrap(); - - let basic_block_2 = MastNode::new_basic_block(vec![Operation::Drop], None).unwrap(); - let basic_block_2_id = mast_forest.add_node(basic_block_2.clone()).unwrap(); - - let join = MastNode::new_join(basic_block_1_id, basic_block_2_id, &mast_forest).unwrap(); - let join_id = mast_forest.add_node(join.clone()).unwrap(); - - let loop_node_id = mast_forest.add_loop(join_id).unwrap(); - mast_forest.make_root(loop_node_id); - - let program = Program::new(mast_forest.into(), loop_node_id); - - // build trace from program - let trace = build_trace_from_program(&program, &[0, 1, 1]); - let alphas = rand_array::(); - let aux_columns = trace.build_aux_trace(&alphas).unwrap(); - let p2 = aux_columns.get_column(P2_COL_IDX); - - let a_9 = Felt::new(9); // address of the JOIN block in the first iteration - let a_33 = Felt::new(33); // address of the JOIN block in the second iteration - let row_values = [ - BlockHashTableRow::new_test(ZERO, program.hash().into(), false, false).collapse(&alphas), - BlockHashTableRow::new_test(ONE, join.digest().into(), false, true).collapse(&alphas), - BlockHashTableRow::new_test(a_9, basic_block_1.digest().into(), true, false) - .collapse(&alphas), - BlockHashTableRow::new_test(a_9, basic_block_2.digest().into(), false, false) - .collapse(&alphas), - BlockHashTableRow::new_test(a_33, basic_block_1.digest().into(), true, false) - .collapse(&alphas), - BlockHashTableRow::new_test(a_33, basic_block_2.digest().into(), false, false) - .collapse(&alphas), - ]; - - // make sure the first entry is initialized to program hash - let mut expected_value = row_values[0]; - assert_eq!(expected_value, p2[0]); - - // --- first iteration ---------------------------------------------------- - - // when LOOP operation is executed, entry for loop body is added to the table - expected_value *= row_values[1]; - assert_eq!(expected_value, p2[1]); - - // when JOIN operation is executed, entries for both children are added to the table - expected_value *= row_values[2] * row_values[3]; - assert_eq!(expected_value, p2[2]); - - // for the next 2 cycles, the table is not affected - assert_eq!(expected_value, p2[3]); - assert_eq!(expected_value, p2[4]); - - // when the first SPAN block ends, its entry is removed from the table - expected_value *= row_values[2].inv(); - assert_eq!(expected_value, p2[5]); - - // for the next 2 cycles, the table is not affected - assert_eq!(expected_value, p2[6]); - assert_eq!(expected_value, p2[7]); - - // when the second SPAN block ends, its entry is removed from the table - expected_value *= row_values[3].inv(); - assert_eq!(expected_value, p2[8]); - - // when the JOIN block ends, its entry is removed from the table - expected_value *= row_values[1].inv(); - assert_eq!(expected_value, p2[9]); - - // --- second iteration --------------------------------------------------- - - // when REPEAT operation is executed, entry for loop body is again added to the table - expected_value *= row_values[1]; - assert_eq!(expected_value, p2[10]); - - // when JOIN operation is executed, entries for both children are added to the table - expected_value *= row_values[4] * row_values[5]; - assert_eq!(expected_value, p2[11]); - - // for the next 2 cycles, the table is not affected - assert_eq!(expected_value, p2[12]); - assert_eq!(expected_value, p2[13]); - - // when the first SPAN block ends, its entry is removed from the table - expected_value *= row_values[4].inv(); - assert_eq!(expected_value, p2[14]); - - // for the next 2 cycles, the table is not affected - assert_eq!(expected_value, p2[15]); - assert_eq!(expected_value, p2[16]); - - // when the second SPAN block ends, its entry is removed from the table - expected_value *= row_values[5].inv(); - assert_eq!(expected_value, p2[17]); - - // when the JOIN block ends, its entry is removed from the table - expected_value *= row_values[1].inv(); - assert_eq!(expected_value, p2[18]); - - // when the LOOP block ends, its entry is removed from the table - expected_value *= row_values[0].inv(); - assert_eq!(expected_value, p2[19]); - - // at this point the table should be empty, and thus, all subsequent values must be ONE - assert_eq!(expected_value, ONE); - for i in 20..p2.len() { - assert_eq!(ONE, p2[i]); - } -} - // HELPER STRUCTS AND METHODS // ================================================================================================