From c9429904e01aef8624c087a3b794806fd8ce68b0 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 3 May 2023 12:54:09 +0200 Subject: [PATCH 01/72] turn cross-table constraints into circuits This streamlines future AIR optimization & modification. --- constraint-evaluation-generator/src/main.rs | 19 +- triton-vm/src/stark.rs | 15 +- triton-vm/src/table/challenges.rs | 14 - triton-vm/src/table/constraints.rs | 1 + .../cross_table_argument_constraints.rs | 14 + triton-vm/src/table/cross_table_argument.rs | 274 +++++++----------- 6 files changed, 143 insertions(+), 194 deletions(-) create mode 100644 triton-vm/src/table/constraints/cross_table_argument_constraints.rs diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index a43c10e9..9b245c82 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -9,6 +9,7 @@ use triton_vm::table::cascade_table::ExtCascadeTable; use triton_vm::table::constraint_circuit::CircuitExpression; use triton_vm::table::constraint_circuit::ConstraintCircuit; use triton_vm::table::constraint_circuit::InputIndicator; +use triton_vm::table::cross_table_argument::GrandCrossTableArg; use triton_vm::table::hash_table::ExtHashTable; use triton_vm::table::jump_stack_table::ExtJumpStackTable; use triton_vm::table::lookup_table::ExtLookupTable; @@ -119,6 +120,18 @@ fn main() { ); write(&table_name_snake, source_code); + let table_name_snake = "cross_table_argument"; + let table_name_camel = "GrandCrossTableArg"; + let source_code = gen( + table_name_snake, + table_name_camel, + &mut GrandCrossTableArg::ext_initial_constraints_as_circuits(), + &mut GrandCrossTableArg::ext_consistency_constraints_as_circuits(), + &mut GrandCrossTableArg::ext_transition_constraints_as_circuits(), + &mut GrandCrossTableArg::ext_terminal_constraints_as_circuits(), + ); + write(table_name_snake, source_code); + if let Err(fmt_failed) = Command::new("cargo").arg("fmt").output() { println!("cargo fmt failed: {fmt_failed}"); } @@ -134,7 +147,7 @@ fn construct_needed_table_identifiers(table_name_constituents: &[&str]) -> (Stri format!("{first_char_upper}{rest}") }) .collect_vec(); - let table_name_camel = format!("{}Table", title_case.iter().join("")); + let table_name_camel = format!("Ext{}Table", title_case.iter().join("")); (table_name_snake, table_name_camel) } @@ -147,14 +160,12 @@ fn write(table_name_snake: &str, rust_source_code: String) { fn gen( table_name_snake: &str, - table_id_name: &str, + table_mod_name: &str, initial_constraint_circuits: &mut [ConstraintCircuit], consistency_constraint_circuits: &mut [ConstraintCircuit], transition_constraint_circuits: &mut [ConstraintCircuit], terminal_constraint_circuits: &mut [ConstraintCircuit], ) -> String { - let table_mod_name = format!("Ext{table_id_name}"); - let num_initial_constraints = initial_constraint_circuits.len(); let num_consistency_constraints = consistency_constraint_circuits.len(); let num_transition_constraints = transition_constraint_circuits.len(); diff --git a/triton-vm/src/stark.rs b/triton-vm/src/stark.rs index 86035e55..4ac3c8b7 100644 --- a/triton-vm/src/stark.rs +++ b/triton-vm/src/stark.rs @@ -1199,15 +1199,12 @@ pub(crate) mod triton_stark_tests { last_master_ext_row, &all_challenges, ); - assert_eq!( - 1, - evaluated_terminal_constraints.len(), - "The number of terminal constraints must be 1 – has the design changed?" - ); - assert!( - evaluated_terminal_constraints[0].is_zero(), - "The terminal constraint must evaluate to 0 for TASM snippet #{code_idx}." - ); + for (i, evaluation) in evaluated_terminal_constraints.iter().enumerate() { + assert!( + evaluation.is_zero(), + "Terminal constraint {i} must evaluate to 0 for snippet #{code_idx}." + ); + } } } diff --git a/triton-vm/src/table/challenges.rs b/triton-vm/src/table/challenges.rs index 9c31fdf4..72f380de 100644 --- a/triton-vm/src/table/challenges.rs +++ b/triton-vm/src/table/challenges.rs @@ -158,20 +158,6 @@ pub enum ChallengeId { U32CiWeight, U32ResultWeight, - ProcessorToProgramWeight, - InputToProcessorWeight, - ProcessorToOutputWeight, - ProcessorToOpStackWeight, - ProcessorToRamWeight, - ProcessorToJumpStackWeight, - HashInputWeight, - HashDigestWeight, - SpongeWeight, - HashToCascadeWeight, - CascadeToLookupWeight, - ProcessorToU32Weight, - ClockJumpDifferenceLookupWeight, - /// The terminal for the Evaluation Argument with standard input. StandardInputTerminal, diff --git a/triton-vm/src/table/constraints.rs b/triton-vm/src/table/constraints.rs index a1987dc8..a4638a01 100644 --- a/triton-vm/src/table/constraints.rs +++ b/triton-vm/src/table/constraints.rs @@ -1,4 +1,5 @@ pub mod cascade_table_constraints; +pub mod cross_table_argument_constraints; pub mod hash_table_constraints; pub mod jump_stack_table_constraints; pub mod lookup_table_constraints; diff --git a/triton-vm/src/table/constraints/cross_table_argument_constraints.rs b/triton-vm/src/table/constraints/cross_table_argument_constraints.rs new file mode 100644 index 00000000..dc58f432 --- /dev/null +++ b/triton-vm/src/table/constraints/cross_table_argument_constraints.rs @@ -0,0 +1,14 @@ +use twenty_first::shared_math::b_field_element::BFieldElement; +use twenty_first::shared_math::x_field_element::XFieldElement; + +use crate::table::cross_table_argument::GrandCrossTableArg; +use crate::table::extension_table::Evaluable; +use crate::table::extension_table::Quotientable; + +// This file is a placeholder for auto-generated code +// Run `cargo run --bin constraint-evaluation-generator` +// to fill in this file with optimized constraints. +impl Evaluable for GrandCrossTableArg {} +impl Evaluable for GrandCrossTableArg {} + +impl Quotientable for GrandCrossTableArg {} diff --git a/triton-vm/src/table/cross_table_argument.rs b/triton-vm/src/table/cross_table_argument.rs index 27940fab..332d27ac 100644 --- a/triton-vm/src/table/cross_table_argument.rs +++ b/triton-vm/src/table/cross_table_argument.rs @@ -2,29 +2,34 @@ use std::ops::Add; use std::ops::Mul; use itertools::Itertools; -use ndarray::ArrayView1; use num_traits::One; use num_traits::Zero; use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::mpolynomial::Degree; -use twenty_first::shared_math::traits::FiniteField; use twenty_first::shared_math::traits::Inverse; use twenty_first::shared_math::x_field_element::XFieldElement; use crate::table::challenges::ChallengeId::*; -use crate::table::challenges::Challenges; -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; +use crate::table::constraint_circuit::ConstraintCircuit; +use crate::table::constraint_circuit::ConstraintCircuitBuilder; +use crate::table::constraint_circuit::ConstraintCircuitMonad; +use crate::table::constraint_circuit::DualRowIndicator; +use crate::table::constraint_circuit::SingleRowIndicator; +use crate::table::constraint_circuit::SingleRowIndicator::ExtRow; use crate::table::table_column::CascadeExtTableColumn; use crate::table::table_column::HashExtTableColumn; +use crate::table::table_column::HashExtTableColumn::*; use crate::table::table_column::JumpStackExtTableColumn; use crate::table::table_column::LookupExtTableColumn; +use crate::table::table_column::LookupExtTableColumn::*; use crate::table::table_column::MasterExtTableColumn; use crate::table::table_column::OpStackExtTableColumn; use crate::table::table_column::ProcessorExtTableColumn; +use crate::table::table_column::ProcessorExtTableColumn::*; use crate::table::table_column::ProgramExtTableColumn; +use crate::table::table_column::ProgramExtTableColumn::*; use crate::table::table_column::RamExtTableColumn; use crate::table::table_column::U32ExtTableColumn; +use crate::table::table_column::U32ExtTableColumn::*; pub trait CrossTableArg { fn default_initial() -> XFieldElement @@ -127,175 +132,110 @@ impl LookupArg { #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct GrandCrossTableArg {} -impl Evaluable for GrandCrossTableArg { - fn evaluate_initial_constraints( - _base_row: ArrayView1, - _ext_row: ArrayView1, - _challenges: &Challenges, - ) -> Vec { +impl GrandCrossTableArg { + pub fn ext_initial_constraints_as_circuits() -> Vec> { + // no further constraints vec![] } - fn evaluate_consistency_constraints( - _base_row: ArrayView1, - _ext_row: ArrayView1, - _challenges: &Challenges, - ) -> Vec { + pub fn ext_consistency_constraints_as_circuits() -> Vec> { + // no further constraints vec![] } - fn evaluate_transition_constraints( - _current_base_row: ArrayView1, - _current_ext_row: ArrayView1, - _next_base_row: ArrayView1, - _next_ext_row: ArrayView1, - _challenges: &Challenges, - ) -> Vec { + pub fn ext_transition_constraints_as_circuits() -> Vec> { + // no further constraints vec![] } - fn evaluate_terminal_constraints( - _base_row: ArrayView1, - ext_row: ArrayView1, - challenges: &Challenges, - ) -> Vec { - let input_to_processor = challenges.get_challenge(StandardInputTerminal) - - ext_row[ProcessorExtTableColumn::InputTableEvalArg.master_ext_table_index()]; - let processor_to_output = ext_row - [ProcessorExtTableColumn::OutputTableEvalArg.master_ext_table_index()] - - challenges.get_challenge(StandardOutputTerminal); - - let instruction_lookup = ext_row - [ProcessorExtTableColumn::InstructionLookupClientLogDerivative - .master_ext_table_index()] - - ext_row[ProgramExtTableColumn::InstructionLookupServerLogDerivative - .master_ext_table_index()]; - let processor_to_op_stack = ext_row - [ProcessorExtTableColumn::OpStackTablePermArg.master_ext_table_index()] - - ext_row[OpStackExtTableColumn::RunningProductPermArg.master_ext_table_index()]; - let processor_to_ram = ext_row - [ProcessorExtTableColumn::RamTablePermArg.master_ext_table_index()] - - ext_row[RamExtTableColumn::RunningProductPermArg.master_ext_table_index()]; - let processor_to_jump_stack = ext_row - [ProcessorExtTableColumn::JumpStackTablePermArg.master_ext_table_index()] - - ext_row[JumpStackExtTableColumn::RunningProductPermArg.master_ext_table_index()]; - let hash_input = ext_row - [ProcessorExtTableColumn::HashInputEvalArg.master_ext_table_index()] - - ext_row[HashExtTableColumn::HashInputRunningEvaluation.master_ext_table_index()]; - let hash_digest = ext_row - [HashExtTableColumn::HashDigestRunningEvaluation.master_ext_table_index()] - - ext_row[ProcessorExtTableColumn::HashDigestEvalArg.master_ext_table_index()]; - let sponge = ext_row[ProcessorExtTableColumn::SpongeEvalArg.master_ext_table_index()] - - ext_row[HashExtTableColumn::SpongeRunningEvaluation.master_ext_table_index()]; - let hash_to_cascade = ext_row - [CascadeExtTableColumn::HashTableServerLogDerivative.master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState0HighestClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState0MidHighClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState0MidLowClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState0LowestClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState1HighestClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState1MidHighClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState1MidLowClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState1LowestClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState2HighestClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState2MidHighClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState2MidLowClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState2LowestClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState3HighestClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState3MidHighClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState3MidLowClientLogDerivative - .master_ext_table_index()] - - ext_row[HashExtTableColumn::CascadeState3LowestClientLogDerivative - .master_ext_table_index()]; - let cascade_to_lookup = ext_row - [CascadeExtTableColumn::LookupTableClientLogDerivative.master_ext_table_index()] - - ext_row - [LookupExtTableColumn::CascadeTableServerLogDerivative.master_ext_table_index()]; - let processor_to_u32 = ext_row - [ProcessorExtTableColumn::U32LookupClientLogDerivative.master_ext_table_index()] - - ext_row[U32ExtTableColumn::LookupServerLogDerivative.master_ext_table_index()]; - let clock_jump_difference_lookup = ext_row - [ProcessorExtTableColumn::ClockJumpDifferenceLookupServerLogDerivative - .master_ext_table_index()] - - ext_row[OpStackExtTableColumn::ClockJumpDifferenceLookupClientLogDerivative - .master_ext_table_index()] - - ext_row[RamExtTableColumn::ClockJumpDifferenceLookupClientLogDerivative - .master_ext_table_index()] - - ext_row[JumpStackExtTableColumn::ClockJumpDifferenceLookupClientLogDerivative - .master_ext_table_index()]; - - let linear_sum = challenges.get_challenge(ProcessorToProgramWeight) * instruction_lookup - + challenges.get_challenge(InputToProcessorWeight) * input_to_processor - + challenges.get_challenge(ProcessorToOutputWeight) * processor_to_output - + challenges.get_challenge(ProcessorToOpStackWeight) * processor_to_op_stack - + challenges.get_challenge(ProcessorToRamWeight) * processor_to_ram - + challenges.get_challenge(ProcessorToJumpStackWeight) * processor_to_jump_stack - + challenges.get_challenge(HashInputWeight) * hash_input - + challenges.get_challenge(HashDigestWeight) * hash_digest - + challenges.get_challenge(SpongeWeight) * sponge - + challenges.get_challenge(HashToCascadeWeight) * hash_to_cascade - + challenges.get_challenge(CascadeToLookupWeight) * cascade_to_lookup - + challenges.get_challenge(ProcessorToU32Weight) * processor_to_u32 - + challenges.get_challenge(ClockJumpDifferenceLookupWeight) - * clock_jump_difference_lookup; - vec![linear_sum] - } -} - -impl Quotientable for GrandCrossTableArg { - fn num_initial_quotients() -> usize { - 0 - } - - fn num_consistency_quotients() -> usize { - 0 - } - - fn num_transition_quotients() -> usize { - 0 - } - - fn num_terminal_quotients() -> usize { - 1 - } - - fn initial_quotient_degree_bounds(_interpolant_degree: Degree) -> Vec { - vec![] - } - - fn consistency_quotient_degree_bounds( - _interpolant_degree: Degree, - _padded_height: usize, - ) -> Vec { - vec![] - } - - fn transition_quotient_degree_bounds( - _interpolant_degree: Degree, - _padded_height: usize, - ) -> Vec { - vec![] - } - - fn terminal_quotient_degree_bounds(interpolant_degree: Degree) -> Vec { - let zerofier_degree = 1 as Degree; - let max_columns_involved_in_one_cross_table_argument = 1; - vec![ - interpolant_degree * max_columns_involved_in_one_cross_table_argument - zerofier_degree, - ] + pub fn ext_terminal_constraints_as_circuits() -> Vec> { + let circuit_builder = ConstraintCircuitBuilder::new(); + let challenge = |c| circuit_builder.challenge(c); + let ext_row = |col_index| circuit_builder.input(ExtRow(col_index)); + + // Closures cannot take arguments of type `impl Trait`. Hence: some more helpers. \o/ + let program_ext_row = + |column: ProgramExtTableColumn| ext_row(column.master_ext_table_index()); + let processor_ext_row = + |column: ProcessorExtTableColumn| ext_row(column.master_ext_table_index()); + let op_stack_ext_row = + |column: OpStackExtTableColumn| ext_row(column.master_ext_table_index()); + let ram_ext_row = |column: RamExtTableColumn| ext_row(column.master_ext_table_index()); + let jump_stack_ext_row = + |column: JumpStackExtTableColumn| ext_row(column.master_ext_table_index()); + let hash_ext_row = |column: HashExtTableColumn| ext_row(column.master_ext_table_index()); + let cascade_ext_row = + |column: CascadeExtTableColumn| ext_row(column.master_ext_table_index()); + let lookup_ext_row = + |column: LookupExtTableColumn| ext_row(column.master_ext_table_index()); + let u32_ext_row = |column: U32ExtTableColumn| ext_row(column.master_ext_table_index()); + + let input_to_processor = + challenge(StandardInputTerminal) - processor_ext_row(InputTableEvalArg); + let processor_to_output = + processor_ext_row(OutputTableEvalArg) - challenge(StandardOutputTerminal); + let instruction_lookup = processor_ext_row(InstructionLookupClientLogDerivative) + - program_ext_row(InstructionLookupServerLogDerivative); + let processor_to_op_stack = processor_ext_row(OpStackTablePermArg) + - op_stack_ext_row(OpStackExtTableColumn::RunningProductPermArg); + let processor_to_ram = processor_ext_row(RamTablePermArg) + - ram_ext_row(RamExtTableColumn::RunningProductPermArg); + let processor_to_jump_stack = processor_ext_row(JumpStackTablePermArg) + - jump_stack_ext_row(JumpStackExtTableColumn::RunningProductPermArg); + let hash_input = + processor_ext_row(HashInputEvalArg) - hash_ext_row(HashInputRunningEvaluation); + let hash_digest = + hash_ext_row(HashDigestRunningEvaluation) - processor_ext_row(HashDigestEvalArg); + let sponge = processor_ext_row(SpongeEvalArg) - hash_ext_row(SpongeRunningEvaluation); + let hash_to_cascade = cascade_ext_row(CascadeExtTableColumn::HashTableServerLogDerivative) + - hash_ext_row(CascadeState0HighestClientLogDerivative) + - hash_ext_row(CascadeState0MidHighClientLogDerivative) + - hash_ext_row(CascadeState0MidLowClientLogDerivative) + - hash_ext_row(CascadeState0LowestClientLogDerivative) + - hash_ext_row(CascadeState1HighestClientLogDerivative) + - hash_ext_row(CascadeState1MidHighClientLogDerivative) + - hash_ext_row(CascadeState1MidLowClientLogDerivative) + - hash_ext_row(CascadeState1LowestClientLogDerivative) + - hash_ext_row(CascadeState2HighestClientLogDerivative) + - hash_ext_row(CascadeState2MidHighClientLogDerivative) + - hash_ext_row(CascadeState2MidLowClientLogDerivative) + - hash_ext_row(CascadeState2LowestClientLogDerivative) + - hash_ext_row(CascadeState3HighestClientLogDerivative) + - hash_ext_row(CascadeState3MidHighClientLogDerivative) + - hash_ext_row(CascadeState3MidLowClientLogDerivative) + - hash_ext_row(CascadeState3LowestClientLogDerivative); + let cascade_to_lookup = + cascade_ext_row(CascadeExtTableColumn::LookupTableClientLogDerivative) + - lookup_ext_row(CascadeTableServerLogDerivative); + let processor_to_u32 = processor_ext_row(U32LookupClientLogDerivative) + - u32_ext_row(LookupServerLogDerivative); + + // Introduce new variable names to increase readability. Potentially opinionated. + let processor_cjdld = ClockJumpDifferenceLookupServerLogDerivative; + let op_stack_cjdld = OpStackExtTableColumn::ClockJumpDifferenceLookupClientLogDerivative; + let ram_cjdld = RamExtTableColumn::ClockJumpDifferenceLookupClientLogDerivative; + let j_stack_cjdld = JumpStackExtTableColumn::ClockJumpDifferenceLookupClientLogDerivative; + let clock_jump_difference_lookup = processor_ext_row(processor_cjdld) + - op_stack_ext_row(op_stack_cjdld) + - ram_ext_row(ram_cjdld) + - jump_stack_ext_row(j_stack_cjdld); + + let mut constraints = [ + input_to_processor, + processor_to_output, + instruction_lookup, + processor_to_op_stack, + processor_to_ram, + processor_to_jump_stack, + hash_input, + hash_digest, + sponge, + hash_to_cascade, + cascade_to_lookup, + processor_to_u32, + clock_jump_difference_lookup, + ]; + ConstraintCircuitMonad::constant_folding(&mut constraints); + constraints.map(|circuit| circuit.consume()).to_vec() } } From d2d162a6d9337e5e4dfd3f6eddbe38fbf1f38324 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 3 May 2023 14:42:20 +0200 Subject: [PATCH 02/72] remove unused debugging function --- triton-vm/src/table/master_table.rs | 42 ----------------------------- 1 file changed, 42 deletions(-) diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index fc78cc92..93cddcdd 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -1619,48 +1619,6 @@ pub fn derive_domain_generator(domain_length: u64) -> BFieldElement { BFieldElement::primitive_root_of_unity(domain_length).unwrap() } -/// Primarily for debugging purposes. -/// Given the global index of some constraint, returns -/// 1. the type of constraint (initial, consistency, transition, or terminal), -/// 2. the name of the table that the constraint is in, and -/// 3. the index within that table. -pub fn constraint_type_and_index_and_table_name( - constraint_idx: usize, -) -> (&'static str, &'static str, usize) { - let initial_section_start = 0; - let initial_section_end = initial_section_start + num_all_initial_quotients(); - let consistency_section_start = initial_section_end; - let consistency_section_end = consistency_section_start + num_all_consistency_quotients(); - let transition_section_start = consistency_section_end; - let transition_section_end = transition_section_start + num_all_transition_quotients(); - let terminal_section_start = transition_section_end; - let terminal_section_end = terminal_section_start + num_all_terminal_quotients(); - assert_eq!(num_all_table_quotients(), terminal_section_end); - match constraint_idx { - idx if initial_section_start <= idx && idx < initial_section_end => { - let section_idx = idx - initial_section_start; - let (table_idx, table_name) = initial_constraint_table_idx_and_name(section_idx); - ("initial", table_name, table_idx) - } - idx if consistency_section_start <= idx && idx < consistency_section_end => { - let section_idx = idx - consistency_section_start; - let (table_idx, table_name) = consistency_constraint_table_idx_and_name(section_idx); - ("consistency", table_name, table_idx) - } - idx if transition_section_start <= idx && idx < transition_section_end => { - let section_idx = idx - transition_section_start; - let (table_idx, table_name) = transition_constraint_table_idx_and_name(section_idx); - ("transition", table_name, table_idx) - } - idx if terminal_section_start <= idx && idx < terminal_section_end => { - let section_idx = idx - terminal_section_start; - let (table_idx, table_name) = terminal_constraint_table_idx_and_name(section_idx); - ("terminal", table_name, table_idx) - } - _ => ("unknown", "unknown", 0), - } -} - /// Primarily for debugging purposes. /// Given the section index of some initial constraint, returns /// 1. the index within the specific table for that constraint, and From 0876f95f2feb2f3dcc10bfe65ada7c2cea751288 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 4 May 2023 10:07:59 +0200 Subject: [PATCH 03/72] import `CpuParallel` only once, allowing easier switch in the future --- triton-vm/src/proof_stream.rs | 4 ++-- triton-vm/src/stark.rs | 5 ++++- triton-vm/src/table/master_table.rs | 12 ++++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/triton-vm/src/proof_stream.rs b/triton-vm/src/proof_stream.rs index 7d620773..2fd5dcbc 100644 --- a/triton-vm/src/proof_stream.rs +++ b/triton-vm/src/proof_stream.rs @@ -195,12 +195,12 @@ mod proof_stream_typed_tests { use twenty_first::shared_math::other::random_elements; use twenty_first::shared_math::tip5::Tip5; use twenty_first::shared_math::x_field_element::XFieldElement; - use twenty_first::util_types::merkle_tree::CpuParallel; use twenty_first::util_types::merkle_tree::MerkleTree; use twenty_first::util_types::merkle_tree_maker::MerkleTreeMaker; use crate::proof_item::FriResponse; use crate::proof_item::ProofItem; + use crate::stark::MTMaker; use super::*; @@ -360,7 +360,7 @@ mod proof_stream_typed_tests { let num_leaves = 1 << tree_height; let leaf_values: Vec = random_elements(num_leaves); let leaf_digests = leaf_values.iter().map(|&xfe| xfe.into()).collect_vec(); - let merkle_tree: MerkleTree = CpuParallel::from_digests(&leaf_digests); + let merkle_tree: MerkleTree = MTMaker::from_digests(&leaf_digests); let indices_to_check = vec![5, 173, 175, 167, 228, 140, 252, 149, 232, 182, 5, 5, 182]; let authentication_structure = merkle_tree.get_authentication_structure(&indices_to_check); let fri_response_content = authentication_structure diff --git a/triton-vm/src/stark.rs b/triton-vm/src/stark.rs index 4ac3c8b7..b7c6feba 100644 --- a/triton-vm/src/stark.rs +++ b/triton-vm/src/stark.rs @@ -46,9 +46,12 @@ use crate::table::master_table::*; use crate::vm::AlgebraicExecutionTrace; pub type StarkHasher = Tip5; -pub type MTMaker = CpuParallel; pub type StarkProofStream = ProofStream; +/// The Merkle tree maker in use. Keeping this as a type alias should make it easier to switch +/// between different Merkle tree makers. +pub type MTMaker = CpuParallel; + /// All the security-related parameters for the zk-STARK. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)] pub struct StarkParameters { diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index 93cddcdd..8b469ff7 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -32,13 +32,13 @@ use twenty_first::shared_math::traits::ModPowU32; use twenty_first::shared_math::traits::PrimitiveRootOfUnity; use twenty_first::shared_math::x_field_element::XFieldElement; use twenty_first::util_types::algebraic_hasher::AlgebraicHasher; -use twenty_first::util_types::merkle_tree::CpuParallel; use twenty_first::util_types::merkle_tree::MerkleTree; use twenty_first::util_types::merkle_tree_maker::MerkleTreeMaker; use triton_opcodes::instruction::Instruction; use crate::arithmetic_domain::ArithmeticDomain; +use crate::stark::MTMaker; use crate::stark::StarkHasher; use crate::table::cascade_table::CascadeTable; use crate::table::cascade_table::ExtCascadeTable; @@ -488,6 +488,10 @@ impl MasterBaseTable { (master_base_table, interpolation_polynomials) } + /// Compute the Merkle tree of the base table. Every row is one leaf in the tree. + /// Therefore, constructing the tree is a two-step process: + /// 1. Hash each row. + /// 1. Construct the tree from the hashed rows. pub fn merkle_tree( &self, maybe_profiler: &mut Option, @@ -501,10 +505,10 @@ impl MasterBaseTable { .collect::>(); prof_stop!(maybe_profiler, "leafs"); prof_start!(maybe_profiler, "Merkle tree"); - let ret = CpuParallel::from_digests(&hashed_rows); + let merkle_tree = MTMaker::from_digests(&hashed_rows); prof_stop!(maybe_profiler, "Merkle tree"); - ret + merkle_tree } /// Create a `MasterExtTable` from a `MasterBaseTable` by `.extend()`ing each individual base @@ -654,7 +658,7 @@ impl MasterExtTable { .collect::>(); prof_stop!(maybe_profiler, "leafs"); prof_start!(maybe_profiler, "Merkle tree"); - let ret = CpuParallel::from_digests(&hashed_rows); + let ret = MTMaker::from_digests(&hashed_rows); prof_stop!(maybe_profiler, "Merkle tree"); ret From e6efc60a5c8516854c25d0fdf2e42314225080aa Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 4 May 2023 10:26:35 +0200 Subject: [PATCH 04/72] derive trace domain generator in a less convoluted way in STARK prover --- triton-vm/src/stark.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/triton-vm/src/stark.rs b/triton-vm/src/stark.rs index b7c6feba..272804ab 100644 --- a/triton-vm/src/stark.rs +++ b/triton-vm/src/stark.rs @@ -164,7 +164,6 @@ impl Stark { prof_stop!(maybe_profiler, "Merkle tree"); prof_start!(maybe_profiler, "Fiat-Shamir", "hash"); - let padded_height = BFieldElement::new(master_base_table.padded_height as u64); let mut proof_stream = StarkProofStream::new(); proof_stream.enqueue(&ProofItem::MerkleRoot(base_merkle_tree_root), true); let extension_weights = proof_stream.sample_scalars(Challenges::num_challenges_to_sample()); @@ -273,7 +272,7 @@ impl Stark { debug_assert_eq!(fri.domain.length, quot_merkle_tree.get_leaf_count()); prof_start!(maybe_profiler, "out-of-domain rows"); - let trace_domain_generator = derive_domain_generator(padded_height.value()); + let trace_domain_generator = derive_domain_generator(claim.padded_height as u64); let out_of_domain_point_curr_row = proof_stream.sample_scalars(1)[0]; let out_of_domain_point_next_row = trace_domain_generator * out_of_domain_point_curr_row; From 5ab32caa1d63403f30dfe5ee5194c8de291ad8a5 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 4 May 2023 12:41:05 +0200 Subject: [PATCH 05/72] add skeleton degree-lowering table --- triton-vm/src/table.rs | 1 + triton-vm/src/table/degree_lowering_table.rs | 35 ++++ triton-vm/src/table/master_table.rs | 169 +++++++++++++++++-- triton-vm/src/table/table_column.rs | 55 ++++++ 4 files changed, 248 insertions(+), 12 deletions(-) create mode 100644 triton-vm/src/table/degree_lowering_table.rs diff --git a/triton-vm/src/table.rs b/triton-vm/src/table.rs index 9226ab5c..17db6642 100644 --- a/triton-vm/src/table.rs +++ b/triton-vm/src/table.rs @@ -3,6 +3,7 @@ pub mod challenges; pub mod constraint_circuit; pub mod constraints; pub mod cross_table_argument; +pub mod degree_lowering_table; pub mod extension_table; pub mod hash_table; pub mod jump_stack_table; diff --git a/triton-vm/src/table/degree_lowering_table.rs b/triton-vm/src/table/degree_lowering_table.rs new file mode 100644 index 00000000..2176c4c0 --- /dev/null +++ b/triton-vm/src/table/degree_lowering_table.rs @@ -0,0 +1,35 @@ +use strum::EnumCount; +use strum_macros::Display; +use strum_macros::EnumCount as EnumCountMacro; +use strum_macros::EnumIter; +use twenty_first::shared_math::b_field_element::BFieldElement; +use twenty_first::shared_math::x_field_element::XFieldElement; + +use crate::table::extension_table::Evaluable; +use crate::table::extension_table::Quotientable; + +pub const BASE_WIDTH: usize = DegreeLoweringBaseTableColumn::COUNT; +pub const EXT_WIDTH: usize = DegreeLoweringExtTableColumn::COUNT; +pub const FULL_WIDTH: usize = BASE_WIDTH + EXT_WIDTH; + +#[repr(usize)] +#[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] +pub enum DegreeLoweringBaseTableColumn { + Col1, +} + +#[repr(usize)] +#[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] +pub enum DegreeLoweringExtTableColumn { + XCol1, +} + +#[derive(Debug, Clone)] +pub struct DegreeLoweringTable {} + +#[derive(Debug, Clone)] +pub struct ExtDegreeLoweringTable {} + +impl Evaluable for ExtDegreeLoweringTable {} +impl Evaluable for ExtDegreeLoweringTable {} +impl Quotientable for ExtDegreeLoweringTable {} diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index 8b469ff7..1595fef4 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -17,9 +17,6 @@ use strum::EnumCount; use strum_macros::Display; use strum_macros::EnumCount as EnumCountMacro; use strum_macros::EnumIter; -use triton_profiler::prof_start; -use triton_profiler::prof_stop; -use triton_profiler::triton_profiler::TritonProfiler; use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::mpolynomial::Degree; use twenty_first::shared_math::other::is_power_of_two; @@ -36,6 +33,9 @@ use twenty_first::util_types::merkle_tree::MerkleTree; use twenty_first::util_types::merkle_tree_maker::MerkleTreeMaker; use triton_opcodes::instruction::Instruction; +use triton_profiler::prof_start; +use triton_profiler::prof_stop; +use triton_profiler::triton_profiler::TritonProfiler; use crate::arithmetic_domain::ArithmeticDomain; use crate::stark::MTMaker; @@ -44,6 +44,7 @@ use crate::table::cascade_table::CascadeTable; use crate::table::cascade_table::ExtCascadeTable; use crate::table::challenges::Challenges; use crate::table::cross_table_argument::GrandCrossTableArg; +use crate::table::degree_lowering_table::ExtDegreeLoweringTable; use crate::table::extension_table::DegreeWithOrigin; use crate::table::extension_table::Evaluable; use crate::table::extension_table::Quotientable; @@ -76,7 +77,8 @@ pub const NUM_BASE_COLUMNS: usize = program_table::BASE_WIDTH + hash_table::BASE_WIDTH + cascade_table::BASE_WIDTH + lookup_table::BASE_WIDTH - + u32_table::BASE_WIDTH; + + u32_table::BASE_WIDTH + + degree_lowering_table::BASE_WIDTH; pub const NUM_EXT_COLUMNS: usize = program_table::EXT_WIDTH + processor_table::EXT_WIDTH + op_stack_table::EXT_WIDTH @@ -85,7 +87,8 @@ pub const NUM_EXT_COLUMNS: usize = program_table::EXT_WIDTH + hash_table::EXT_WIDTH + cascade_table::EXT_WIDTH + lookup_table::EXT_WIDTH - + u32_table::EXT_WIDTH; + + u32_table::EXT_WIDTH + + degree_lowering_table::EXT_WIDTH; pub const NUM_COLUMNS: usize = NUM_BASE_COLUMNS + NUM_EXT_COLUMNS; pub const PROGRAM_TABLE_START: usize = 0; @@ -106,6 +109,9 @@ pub const LOOKUP_TABLE_START: usize = CASCADE_TABLE_END; pub const LOOKUP_TABLE_END: usize = LOOKUP_TABLE_START + lookup_table::BASE_WIDTH; pub const U32_TABLE_START: usize = LOOKUP_TABLE_END; pub const U32_TABLE_END: usize = U32_TABLE_START + u32_table::BASE_WIDTH; +pub const DEGREE_LOWERING_TABLE_START: usize = U32_TABLE_END; +pub const DEGREE_LOWERING_TABLE_END: usize = + DEGREE_LOWERING_TABLE_START + degree_lowering_table::BASE_WIDTH; pub const EXT_PROGRAM_TABLE_START: usize = 0; pub const EXT_PROGRAM_TABLE_END: usize = EXT_PROGRAM_TABLE_START + program_table::EXT_WIDTH; @@ -126,6 +132,9 @@ pub const EXT_LOOKUP_TABLE_START: usize = EXT_CASCADE_TABLE_END; pub const EXT_LOOKUP_TABLE_END: usize = EXT_LOOKUP_TABLE_START + lookup_table::EXT_WIDTH; pub const EXT_U32_TABLE_START: usize = EXT_LOOKUP_TABLE_END; pub const EXT_U32_TABLE_END: usize = EXT_U32_TABLE_START + u32_table::EXT_WIDTH; +pub const EXT_DEGREE_LOWERING_TABLE_START: usize = EXT_U32_TABLE_END; +pub const EXT_DEGREE_LOWERING_TABLE_END: usize = + EXT_DEGREE_LOWERING_TABLE_START + degree_lowering_table::EXT_WIDTH; /// A `TableId` uniquely determines one of Triton VM's tables. #[derive(Debug, Copy, Clone, Display, EnumCountMacro, EnumIter, PartialEq, Eq, Hash)] @@ -139,6 +148,7 @@ pub enum TableId { CascadeTable, LookupTable, U32Table, + DegreeLoweringTable, } /// A Master Table is, in some sense, a top-level table of Triton VM. It contains all the data @@ -598,6 +608,7 @@ impl MasterBaseTable { CascadeTable => (CASCADE_TABLE_START, CASCADE_TABLE_END), LookupTable => (LOOKUP_TABLE_START, LOOKUP_TABLE_END), U32Table => (U32_TABLE_START, U32_TABLE_END), + DegreeLoweringTable => (DEGREE_LOWERING_TABLE_START, DEGREE_LOWERING_TABLE_END), } } @@ -676,6 +687,10 @@ impl MasterExtTable { CascadeTable => (EXT_CASCADE_TABLE_START, EXT_CASCADE_TABLE_END), LookupTable => (EXT_LOOKUP_TABLE_START, EXT_LOOKUP_TABLE_END), U32Table => (EXT_U32_TABLE_START, EXT_U32_TABLE_END), + DegreeLoweringTable => ( + EXT_DEGREE_LOWERING_TABLE_START, + EXT_DEGREE_LOWERING_TABLE_END, + ), } } @@ -741,6 +756,7 @@ pub fn num_all_initial_quotients() -> usize { + ExtCascadeTable::num_initial_quotients() + ExtLookupTable::num_initial_quotients() + ExtU32Table::num_initial_quotients() + + ExtDegreeLoweringTable::num_initial_quotients() } pub fn num_all_consistency_quotients() -> usize { @@ -753,6 +769,7 @@ pub fn num_all_consistency_quotients() -> usize { + ExtCascadeTable::num_consistency_quotients() + ExtLookupTable::num_consistency_quotients() + ExtU32Table::num_consistency_quotients() + + ExtDegreeLoweringTable::num_consistency_quotients() } pub fn num_all_transition_quotients() -> usize { @@ -765,6 +782,7 @@ pub fn num_all_transition_quotients() -> usize { + ExtCascadeTable::num_transition_quotients() + ExtLookupTable::num_transition_quotients() + ExtU32Table::num_transition_quotients() + + ExtDegreeLoweringTable::num_transition_quotients() } pub fn num_all_terminal_quotients() -> usize { @@ -778,6 +796,7 @@ pub fn num_all_terminal_quotients() -> usize { + ExtLookupTable::num_terminal_quotients() + ExtU32Table::num_terminal_quotients() + GrandCrossTableArg::num_terminal_quotients() + + ExtDegreeLoweringTable::num_terminal_quotients() } pub fn all_initial_quotient_degree_bounds(interpolant_degree: Degree) -> Vec { @@ -791,6 +810,7 @@ pub fn all_initial_quotient_degree_bounds(interpolant_degree: Degree) -> Vec Vec (usize, & let lookup_end = lookup_start + ExtLookupTable::num_initial_quotients(); let u32_start = lookup_end; let u32_end = u32_start + ExtU32Table::num_initial_quotients(); - assert_eq!(num_all_initial_quotients(), u32_end); + let degree_lowering_start = u32_end; + let degree_lowering_end = + degree_lowering_start + ExtDegreeLoweringTable::num_initial_quotients(); + assert_eq!(num_all_initial_quotients(), degree_lowering_end); match constraint_idx { i if program_start <= i && i < program_end => (i - program_start, "Program"), i if processor_start <= i && i < processor_end => (i - processor_start, "Processor"), @@ -1657,6 +1751,9 @@ pub fn initial_constraint_table_idx_and_name(constraint_idx: usize) -> (usize, & i if cascade_start <= i && i < cascade_end => (i - cascade_start, "Cascade"), i if lookup_start <= i && i < lookup_end => (i - lookup_start, "Lookup"), i if u32_start <= i && i < u32_end => (i - u32_start, "U32"), + i if degree_lowering_start <= i && i < degree_lowering_end => { + (i - degree_lowering_start, "DegreeLowering") + } _ => (0, "Unknown"), } } @@ -1684,7 +1781,10 @@ pub fn consistency_constraint_table_idx_and_name(constraint_idx: usize) -> (usiz let lookup_end = lookup_start + ExtLookupTable::num_consistency_quotients(); let u32_start = lookup_end; let u32_end = u32_start + ExtU32Table::num_consistency_quotients(); - assert_eq!(num_all_consistency_quotients(), u32_end); + let degree_lowering_start = u32_end; + let degree_lowering_end = + degree_lowering_start + ExtDegreeLoweringTable::num_consistency_quotients(); + assert_eq!(num_all_consistency_quotients(), degree_lowering_end); match constraint_idx { i if program_start <= i && i < program_end => (i - program_start, "Program"), i if processor_start <= i && i < processor_end => (i - processor_start, "Processor"), @@ -1695,6 +1795,9 @@ pub fn consistency_constraint_table_idx_and_name(constraint_idx: usize) -> (usiz i if cascade_start <= i && i < cascade_end => (i - cascade_start, "Cascade"), i if lookup_start <= i && i < lookup_end => (i - lookup_start, "Lookup"), i if u32_start <= i && i < u32_end => (i - u32_start, "U32"), + i if degree_lowering_start <= i && i < degree_lowering_end => { + (i - degree_lowering_start, "DegreeLowering") + } _ => (0, "Unknown"), } } @@ -1722,7 +1825,10 @@ pub fn transition_constraint_table_idx_and_name(constraint_idx: usize) -> (usize let lookup_end = lookup_start + ExtLookupTable::num_transition_quotients(); let u32_start = lookup_end; let u32_end = u32_start + ExtU32Table::num_transition_quotients(); - assert_eq!(num_all_transition_quotients(), u32_end); + let degree_lowering_start = u32_end; + let degree_lowering_end = + degree_lowering_start + ExtDegreeLoweringTable::num_transition_quotients(); + assert_eq!(num_all_transition_quotients(), degree_lowering_end); match constraint_idx { i if program_start <= i && i < program_end => (i - program_start, "Program"), i if processor_start <= i && i < processor_end => (i - processor_start, "Processor"), @@ -1733,6 +1839,9 @@ pub fn transition_constraint_table_idx_and_name(constraint_idx: usize) -> (usize i if cascade_start <= i && i < cascade_end => (i - cascade_start, "Cascade"), i if lookup_start <= i && i < lookup_end => (i - lookup_start, "Lookup"), i if u32_start <= i && i < u32_end => (i - u32_start, "U32"), + i if degree_lowering_start <= i && i < degree_lowering_end => { + (i - degree_lowering_start, "DegreeLowering") + } _ => (0, "Unknown"), } } @@ -1760,7 +1869,10 @@ pub fn terminal_constraint_table_idx_and_name(constraint_idx: usize) -> (usize, let lookup_end = lookup_start + ExtLookupTable::num_terminal_quotients(); let u32_start = lookup_end; let u32_end = u32_start + ExtU32Table::num_terminal_quotients(); - let cross_table_start = u32_end; + let degree_lowering_start = u32_end; + let degree_lowering_end = + degree_lowering_start + ExtDegreeLoweringTable::num_terminal_quotients(); + let cross_table_start = degree_lowering_end; let cross_table_end = cross_table_start + GrandCrossTableArg::num_terminal_quotients(); assert_eq!(num_all_terminal_quotients(), cross_table_end); match constraint_idx { @@ -1773,6 +1885,9 @@ pub fn terminal_constraint_table_idx_and_name(constraint_idx: usize) -> (usize, i if cascade_start <= i && i < cascade_end => (i - cascade_start, "Cascade"), i if lookup_start <= i && i < lookup_end => (i - lookup_start, "Lookup"), i if u32_start <= i && i < u32_end => (i - u32_start, "U32"), + i if degree_lowering_start <= i && i < degree_lowering_end => { + (i - degree_lowering_start, "DegreeLowering") + } i if cross_table_start <= i && i < cross_table_end => { (i - cross_table_start, "GrandCrossTableArgument") } @@ -1792,6 +1907,9 @@ mod master_table_tests { use crate::stark::triton_stark_tests::parse_simulate_pad; use crate::stark::triton_stark_tests::parse_simulate_pad_extend; use crate::table::cascade_table; + use crate::table::degree_lowering_table; + use crate::table::degree_lowering_table::DegreeLoweringBaseTableColumn; + use crate::table::degree_lowering_table::DegreeLoweringExtTableColumn; use crate::table::hash_table; use crate::table::jump_stack_table; use crate::table::lookup_table; @@ -1800,7 +1918,7 @@ mod master_table_tests { use crate::table::master_table::terminal_quotient_zerofier_inverse; use crate::table::master_table::transition_quotient_zerofier_inverse; use crate::table::master_table::TableId::*; - use crate::table::master_table::EXT_U32_TABLE_END; + use crate::table::master_table::EXT_DEGREE_LOWERING_TABLE_END; use crate::table::master_table::NUM_BASE_COLUMNS; use crate::table::master_table::NUM_COLUMNS; use crate::table::master_table::NUM_EXT_COLUMNS; @@ -1870,6 +1988,10 @@ mod master_table_tests { u32_table::BASE_WIDTH, master_base_table.table(U32Table).ncols() ); + assert_eq!( + degree_lowering_table::BASE_WIDTH, + master_base_table.table(DegreeLoweringTable).ncols() + ); } #[test] @@ -1913,12 +2035,16 @@ mod master_table_tests { u32_table::EXT_WIDTH, master_ext_table.table(U32Table).ncols() ); + assert_eq!( + degree_lowering_table::EXT_WIDTH, + master_ext_table.table(DegreeLoweringTable).ncols() + ); // use some domain-specific knowledge to also check for the randomizer columns assert_eq!( parameters.num_randomizer_polynomials, master_ext_table .master_ext_matrix - .slice(s![.., EXT_U32_TABLE_END..]) + .slice(s![.., EXT_DEGREE_LOWERING_TABLE_END..]) .ncols() ); } @@ -2045,6 +2171,13 @@ mod master_table_tests { u32_table::EXT_WIDTH, u32_table::FULL_WIDTH ); + println!( + "| {:<18} | {:>10} | {:>9} | {:>10} |", + "DegreeLowering", + degree_lowering_table::BASE_WIDTH, + degree_lowering_table::EXT_WIDTH, + degree_lowering_table::FULL_WIDTH, + ); println!("| | | | |"); println!( "| Sum | {NUM_BASE_COLUMNS:>10} \ @@ -2112,6 +2245,12 @@ mod master_table_tests { column.master_base_table_index() ); } + for column in DegreeLoweringBaseTableColumn::iter() { + println!( + "{:>3} | degree low. | {column}", + column.master_base_table_index() + ); + } println!(); println!("idx | table | extension column"); println!("---:|:------------|:----------------"); @@ -2169,5 +2308,11 @@ mod master_table_tests { column.master_ext_table_index() ); } + for column in DegreeLoweringExtTableColumn::iter() { + println!( + "{:>3} | degree low. | {column}", + column.master_ext_table_index() + ); + } } } diff --git a/triton-vm/src/table/table_column.rs b/triton-vm/src/table/table_column.rs index 2c4a0049..85f35e5c 100644 --- a/triton-vm/src/table/table_column.rs +++ b/triton-vm/src/table/table_column.rs @@ -7,8 +7,12 @@ use strum_macros::Display; use strum_macros::EnumCount as EnumCountMacro; use strum_macros::EnumIter; +use crate::table::degree_lowering_table::DegreeLoweringBaseTableColumn; +use crate::table::degree_lowering_table::DegreeLoweringExtTableColumn; use crate::table::master_table::CASCADE_TABLE_START; +use crate::table::master_table::DEGREE_LOWERING_TABLE_START; use crate::table::master_table::EXT_CASCADE_TABLE_START; +use crate::table::master_table::EXT_DEGREE_LOWERING_TABLE_START; use crate::table::master_table::EXT_HASH_TABLE_START; use crate::table::master_table::EXT_JUMP_STACK_TABLE_START; use crate::table::master_table::EXT_LOOKUP_TABLE_START; @@ -534,6 +538,18 @@ impl MasterBaseTableColumn for U32BaseTableColumn { } } +impl MasterBaseTableColumn for DegreeLoweringBaseTableColumn { + #[inline] + fn base_table_index(&self) -> usize { + (*self) as usize + } + + #[inline] + fn master_base_table_index(&self) -> usize { + DEGREE_LOWERING_TABLE_START + self.base_table_index() + } +} + // -------------------------------------------------------------------- /// A trait for the columns in the master extension table. This trait is implemented for all enums @@ -658,6 +674,18 @@ impl MasterExtTableColumn for U32ExtTableColumn { } } +impl MasterExtTableColumn for DegreeLoweringExtTableColumn { + #[inline] + fn ext_table_index(&self) -> usize { + (*self) as usize + } + + #[inline] + fn master_ext_table_index(&self) -> usize { + EXT_DEGREE_LOWERING_TABLE_START + self.ext_table_index() + } +} + // -------------------------------------------------------------------- #[cfg(test)] @@ -665,6 +693,7 @@ mod table_column_tests { use strum::IntoEnumIterator; use crate::table::cascade_table; + use crate::table::degree_lowering_table; use crate::table::hash_table; use crate::table::jump_stack_table; use crate::table::lookup_table; @@ -759,6 +788,15 @@ mod table_column_tests { + 1, "U32Table's BASE_WIDTH is 1 + its max column index", ); + assert_eq!( + degree_lowering_table::BASE_WIDTH, + DegreeLoweringBaseTableColumn::iter() + .last() + .unwrap() + .base_table_index() + + 1, + "DegreeLoweringTable's BASE_WIDTH is 1 + its max column index", + ); assert_eq!( program_table::EXT_WIDTH, @@ -829,6 +867,15 @@ mod table_column_tests { U32ExtTableColumn::iter().last().unwrap().ext_table_index() + 1, "U32Table's EXT_WIDTH is 1 + its max column index", ); + assert_eq!( + degree_lowering_table::EXT_WIDTH, + DegreeLoweringExtTableColumn::iter() + .last() + .unwrap() + .ext_table_index() + + 1, + "DegreeLoweringTable's EXT_WIDTH is 1 + its max column index", + ); } #[test] @@ -870,6 +917,10 @@ mod table_column_tests { assert_eq!(expected_column_index, column.master_base_table_index()); expected_column_index += 1; } + for column in DegreeLoweringBaseTableColumn::iter() { + assert_eq!(expected_column_index, column.master_base_table_index()); + expected_column_index += 1; + } } #[test] @@ -911,5 +962,9 @@ mod table_column_tests { assert_eq!(expected_column_index, column.master_ext_table_index()); expected_column_index += 1; } + for column in DegreeLoweringExtTableColumn::iter() { + assert_eq!(expected_column_index, column.master_ext_table_index()); + expected_column_index += 1; + } } } From 1dd19b4a866541ebc11e3a3e5393ec0935c83428 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 4 May 2023 17:15:05 +0200 Subject: [PATCH 06/72] add mock derivation of deterministic columns' content --- triton-vm/src/table/degree_lowering_table.rs | 54 +++++++++++++++++++ triton-vm/src/table/master_table.rs | 55 ++++++++++++++------ 2 files changed, 92 insertions(+), 17 deletions(-) diff --git a/triton-vm/src/table/degree_lowering_table.rs b/triton-vm/src/table/degree_lowering_table.rs index 2176c4c0..51926dac 100644 --- a/triton-vm/src/table/degree_lowering_table.rs +++ b/triton-vm/src/table/degree_lowering_table.rs @@ -1,3 +1,8 @@ +use ndarray::s; +use ndarray::ArrayView2; +use ndarray::ArrayViewMut2; +use ndarray::Axis; +use ndarray::Zip; use strum::EnumCount; use strum_macros::Display; use strum_macros::EnumCount as EnumCountMacro; @@ -7,6 +12,8 @@ use twenty_first::shared_math::x_field_element::XFieldElement; use crate::table::extension_table::Evaluable; use crate::table::extension_table::Quotientable; +use crate::table::master_table::NUM_BASE_COLUMNS; +use crate::table::master_table::NUM_EXT_COLUMNS; pub const BASE_WIDTH: usize = DegreeLoweringBaseTableColumn::COUNT; pub const EXT_WIDTH: usize = DegreeLoweringExtTableColumn::COUNT; @@ -30,6 +37,53 @@ pub struct DegreeLoweringTable {} #[derive(Debug, Clone)] pub struct ExtDegreeLoweringTable {} +impl DegreeLoweringTable { + pub fn fill_deterministic_base_columns(master_base_table: &mut ArrayViewMut2) { + assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); + + let main_trace_section_start = 0; + let main_trace_section_end = main_trace_section_start + NUM_BASE_COLUMNS - BASE_WIDTH; + let deterministic_section_start = main_trace_section_end; + let deterministic_section_end = deterministic_section_start + BASE_WIDTH; + + let (main_trace_section, mut deterministic_section) = master_base_table.multi_slice_mut(( + s![.., main_trace_section_start..main_trace_section_end], + s![.., deterministic_section_start..deterministic_section_end], + )); + + Zip::from(main_trace_section.axis_iter(Axis(0))) + .and(deterministic_section.axis_iter_mut(Axis(0))) + .par_for_each(|main_trace_row, mut deterministic_row| { + deterministic_row[0] = main_trace_row[0] * main_trace_row[1]; + }); + } + + pub fn fill_deterministic_ext_columns( + master_base_table: ArrayView2, + master_ext_table: &mut ArrayViewMut2, + ) { + assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); + assert_eq!(NUM_EXT_COLUMNS, master_ext_table.ncols()); + + let main_ext_section_start = 0; + let main_ext_section_end = main_ext_section_start + NUM_EXT_COLUMNS - EXT_WIDTH; + let det_ext_section_start = main_ext_section_end; + let det_ext_section_end = det_ext_section_start + EXT_WIDTH; + + let (main_ext_section, mut deterministic_section) = master_ext_table.multi_slice_mut(( + s![.., main_ext_section_start..main_ext_section_end], + s![.., det_ext_section_start..det_ext_section_end], + )); + + Zip::from(master_base_table.axis_iter(Axis(0))) + .and(main_ext_section.axis_iter(Axis(0))) + .and(deterministic_section.axis_iter_mut(Axis(0))) + .par_for_each(|base_row, main_ext_row, mut det_ext_row| { + det_ext_row[0] = base_row[0] * main_ext_row[0]; + }); + } +} + impl Evaluable for ExtDegreeLoweringTable {} impl Evaluable for ExtDegreeLoweringTable {} impl Quotientable for ExtDegreeLoweringTable {} diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index 1595fef4..08ea30bd 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -44,6 +44,7 @@ use crate::table::cascade_table::CascadeTable; use crate::table::cascade_table::ExtCascadeTable; use crate::table::challenges::Challenges; use crate::table::cross_table_argument::GrandCrossTableArg; +use crate::table::degree_lowering_table::DegreeLoweringTable; use crate::table::degree_lowering_table::ExtDegreeLoweringTable; use crate::table::extension_table::DegreeWithOrigin; use crate::table::extension_table::Evaluable; @@ -428,25 +429,17 @@ impl MasterBaseTable { master_base_matrix, }; - let program_table = &mut master_base_table.table_mut(TableId::ProgramTable); - ProgramTable::fill_trace(program_table, aet); - let op_stack_table = &mut master_base_table.table_mut(TableId::OpStackTable); - let clk_jump_diffs_op_stack = OpStackTable::fill_trace(op_stack_table, aet); - let ram_table = &mut master_base_table.table_mut(TableId::RamTable); - let clk_jump_diffs_ram = RamTable::fill_trace(ram_table, aet); - let jump_stack_table = &mut master_base_table.table_mut(TableId::JumpStackTable); - let clk_jump_diffs_jump_stack = JumpStackTable::fill_trace(jump_stack_table, aet); - let hash_table = &mut master_base_table.table_mut(TableId::HashTable); - HashTable::fill_trace(hash_table, aet); - let cascade_table = &mut master_base_table.table_mut(TableId::CascadeTable); - CascadeTable::fill_trace(cascade_table, aet); - let lookup_table = &mut master_base_table.table_mut(TableId::LookupTable); - LookupTable::fill_trace(lookup_table, aet); - let u32_table = &mut master_base_table.table_mut(TableId::U32Table); - U32Table::fill_trace(u32_table, aet); - // memory-like tables must be filled in before clock jump differences are known, hence // the break from the usual order + let clk_jump_diffs_op_stack = + OpStackTable::fill_trace(&mut master_base_table.table_mut(TableId::OpStackTable), aet); + let clk_jump_diffs_ram = + RamTable::fill_trace(&mut master_base_table.table_mut(TableId::RamTable), aet); + let clk_jump_diffs_jump_stack = JumpStackTable::fill_trace( + &mut master_base_table.table_mut(TableId::JumpStackTable), + aet, + ); + let processor_table = &mut master_base_table.table_mut(TableId::ProcessorTable); ProcessorTable::fill_trace( processor_table, @@ -456,6 +449,15 @@ impl MasterBaseTable { &clk_jump_diffs_jump_stack, ); + ProgramTable::fill_trace(&mut master_base_table.table_mut(TableId::ProgramTable), aet); + HashTable::fill_trace(&mut master_base_table.table_mut(TableId::HashTable), aet); + CascadeTable::fill_trace(&mut master_base_table.table_mut(TableId::CascadeTable), aet); + LookupTable::fill_trace(&mut master_base_table.table_mut(TableId::LookupTable), aet); + U32Table::fill_trace(&mut master_base_table.table_mut(TableId::U32Table), aet); + + // Filling the degree-lowering table only makes sense after padding has happened. + // Hence, this table is omitted here. + master_base_table } @@ -468,22 +470,34 @@ impl MasterBaseTable { let program_table = &mut self.table_mut(TableId::ProgramTable); ProgramTable::pad_trace(program_table, program_len); + let processor_table = &mut self.table_mut(TableId::ProcessorTable); ProcessorTable::pad_trace(processor_table, main_execution_len); + let op_stack_table = &mut self.table_mut(TableId::OpStackTable); OpStackTable::pad_trace(op_stack_table, main_execution_len); + let ram_table = &mut self.table_mut(TableId::RamTable); RamTable::pad_trace(ram_table, main_execution_len); + let jump_stack_table = &mut self.table_mut(TableId::JumpStackTable); JumpStackTable::pad_trace(jump_stack_table, main_execution_len); + let hash_table = &mut self.table_mut(TableId::HashTable); HashTable::pad_trace(hash_table, hash_coprocessor_execution_len); + let cascade_table = &mut self.table_mut(TableId::CascadeTable); CascadeTable::pad_trace(cascade_table, cascade_table_len); + let lookup_table = &mut self.table_mut(TableId::LookupTable); LookupTable::pad_trace(lookup_table); + let u32_table = &mut self.table_mut(TableId::U32Table); U32Table::pad_trace(u32_table, u32_table_len); + + DegreeLoweringTable::fill_deterministic_base_columns( + &mut self.master_base_matrix.view_mut(), + ); } /// Returns the low-degree extended columns as well as the columns' interpolation polynomials. @@ -593,6 +607,13 @@ impl MasterBaseTable { challenges, ); + DegreeLoweringTable::fill_deterministic_ext_columns( + self.master_base_matrix.view(), + &mut master_ext_table + .master_ext_matrix + .slice_mut(s![.., 0..NUM_EXT_COLUMNS]), + ); + master_ext_table } From 1e75587947e840ed00c080e8efd5d451f30a4682 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 4 May 2023 18:32:31 +0200 Subject: [PATCH 07/72] add constraints for mock deterministic columns --- triton-vm/src/table/degree_lowering_table.rs | 147 ++++++++++++++++++- 1 file changed, 144 insertions(+), 3 deletions(-) diff --git a/triton-vm/src/table/degree_lowering_table.rs b/triton-vm/src/table/degree_lowering_table.rs index 51926dac..e3e8956b 100644 --- a/triton-vm/src/table/degree_lowering_table.rs +++ b/triton-vm/src/table/degree_lowering_table.rs @@ -1,4 +1,5 @@ use ndarray::s; +use ndarray::ArrayView1; use ndarray::ArrayView2; use ndarray::ArrayViewMut2; use ndarray::Axis; @@ -8,8 +9,10 @@ use strum_macros::Display; use strum_macros::EnumCount as EnumCountMacro; use strum_macros::EnumIter; use twenty_first::shared_math::b_field_element::BFieldElement; +use twenty_first::shared_math::mpolynomial::Degree; use twenty_first::shared_math::x_field_element::XFieldElement; +use crate::table::challenges::Challenges; use crate::table::extension_table::Evaluable; use crate::table::extension_table::Quotientable; use crate::table::master_table::NUM_BASE_COLUMNS; @@ -84,6 +87,144 @@ impl DegreeLoweringTable { } } -impl Evaluable for ExtDegreeLoweringTable {} -impl Evaluable for ExtDegreeLoweringTable {} -impl Quotientable for ExtDegreeLoweringTable {} +impl Evaluable for ExtDegreeLoweringTable { + fn evaluate_initial_constraints( + _base_row: ArrayView1, + _ext_row: ArrayView1, + _challenges: &Challenges, + ) -> Vec { + vec![] + } + + fn evaluate_consistency_constraints( + base_row: ArrayView1, + ext_row: ArrayView1, + _challenges: &Challenges, + ) -> Vec { + let deterministic_base_section_start = NUM_BASE_COLUMNS - BASE_WIDTH; + let deterministic_ext_section_start = NUM_EXT_COLUMNS - EXT_WIDTH; + + let b_dummy_0 = base_row[deterministic_base_section_start] - base_row[0] * base_row[1]; + let x_dummy_0 = ext_row[deterministic_ext_section_start] - base_row[0] * ext_row[0]; + + let base_constraints = [b_dummy_0]; + let ext_constraints = [x_dummy_0]; + + base_constraints + .map(|b| b.lift()) + .into_iter() + .chain(ext_constraints) + .collect() + } + + fn evaluate_transition_constraints( + _current_base_row: ArrayView1, + _current_ext_row: ArrayView1, + _next_base_row: ArrayView1, + _next_ext_row: ArrayView1, + _challenges: &Challenges, + ) -> Vec { + vec![] + } + + fn evaluate_terminal_constraints( + _base_row: ArrayView1, + _ext_row: ArrayView1, + _challenges: &Challenges, + ) -> Vec { + vec![] + } +} + +impl Evaluable for ExtDegreeLoweringTable { + fn evaluate_initial_constraints( + _base_row: ArrayView1, + _ext_row: ArrayView1, + _challenges: &Challenges, + ) -> Vec { + vec![] + } + + fn evaluate_consistency_constraints( + base_row: ArrayView1, + ext_row: ArrayView1, + _challenges: &Challenges, + ) -> Vec { + let deterministic_base_section_start = NUM_BASE_COLUMNS - BASE_WIDTH; + let deterministic_ext_section_start = NUM_EXT_COLUMNS - EXT_WIDTH; + + let b_dummy_0 = base_row[deterministic_base_section_start] - base_row[0] * base_row[1]; + let x_dummy_0 = ext_row[deterministic_ext_section_start] - base_row[0] * ext_row[0]; + + let base_constraints = [b_dummy_0]; + let ext_constraints = [x_dummy_0]; + + base_constraints + .into_iter() + .chain(ext_constraints) + .collect() + } + + fn evaluate_transition_constraints( + _current_base_row: ArrayView1, + _current_ext_row: ArrayView1, + _next_base_row: ArrayView1, + _next_ext_row: ArrayView1, + _challenges: &Challenges, + ) -> Vec { + vec![] + } + + fn evaluate_terminal_constraints( + _base_row: ArrayView1, + _ext_row: ArrayView1, + _challenges: &Challenges, + ) -> Vec { + vec![] + } +} + +impl Quotientable for ExtDegreeLoweringTable { + fn num_initial_quotients() -> usize { + 0 + } + + fn num_consistency_quotients() -> usize { + 2 + } + + fn num_transition_quotients() -> usize { + 0 + } + + fn num_terminal_quotients() -> usize { + 0 + } + + fn initial_quotient_degree_bounds(_interpolant_degree: Degree) -> Vec { + vec![] + } + + fn consistency_quotient_degree_bounds( + interpolant_degree: Degree, + padded_height: usize, + ) -> Vec { + let zerofier_degree = padded_height as Degree; + [ + interpolant_degree * 2 - zerofier_degree, + interpolant_degree * 2 - zerofier_degree, + ] + .to_vec() + } + + fn transition_quotient_degree_bounds( + _interpolant_degree: Degree, + _padded_height: usize, + ) -> Vec { + vec![] + } + + fn terminal_quotient_degree_bounds(_interpolant_degree: Degree) -> Vec { + vec![] + } +} From d8fa5f94804efd1619531a1854c679bd02d348a8 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Fri, 5 May 2023 10:01:44 +0200 Subject: [PATCH 08/72] constraints: most tables return monads, not the finished circuit This change will allow global optimizations, like constant folding. The one table not handled in this commit is the Processor Table. --- constraint-evaluation-generator/src/main.rs | 88 +++++----- triton-vm/src/table/cascade_table.rs | 39 +++-- triton-vm/src/table/constraint_circuit.rs | 169 +++++++++++--------- triton-vm/src/table/cross_table_argument.rs | 24 +-- triton-vm/src/table/hash_table.rs | 79 ++++----- triton-vm/src/table/jump_stack_table.rs | 36 ++--- triton-vm/src/table/lookup_table.rs | 42 +++-- triton-vm/src/table/op_stack_table.rs | 33 ++-- triton-vm/src/table/program_table.rs | 38 ++--- triton-vm/src/table/ram_table.rs | 38 ++--- triton-vm/src/table/u32_table.rs | 46 +++--- 11 files changed, 314 insertions(+), 318 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 9b245c82..e6dde85c 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -8,6 +8,8 @@ use twenty_first::shared_math::x_field_element::XFieldElement; use triton_vm::table::cascade_table::ExtCascadeTable; use triton_vm::table::constraint_circuit::CircuitExpression; use triton_vm::table::constraint_circuit::ConstraintCircuit; +use triton_vm::table::constraint_circuit::ConstraintCircuitBuilder; +use triton_vm::table::constraint_circuit::ConstraintCircuitMonad; use triton_vm::table::constraint_circuit::InputIndicator; use triton_vm::table::cross_table_argument::GrandCrossTableArg; use triton_vm::table::hash_table::ExtHashTable; @@ -24,10 +26,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut ExtProgramTable::ext_initial_constraints_as_circuits(), - &mut ExtProgramTable::ext_consistency_constraints_as_circuits(), - &mut ExtProgramTable::ext_transition_constraints_as_circuits(), - &mut ExtProgramTable::ext_terminal_constraints_as_circuits(), + &mut build_fold_circuitify(&ExtProgramTable::ext_initial_constraints_as_circuits), + &mut build_fold_circuitify(&ExtProgramTable::ext_consistency_constraints_as_circuits), + &mut build_fold_circuitify(&ExtProgramTable::ext_transition_constraints_as_circuits), + &mut build_fold_circuitify(&ExtProgramTable::ext_terminal_constraints_as_circuits), ); write(&table_name_snake, source_code); @@ -46,10 +48,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut ExtOpStackTable::ext_initial_constraints_as_circuits(), - &mut ExtOpStackTable::ext_consistency_constraints_as_circuits(), - &mut ExtOpStackTable::ext_transition_constraints_as_circuits(), - &mut ExtOpStackTable::ext_terminal_constraints_as_circuits(), + &mut build_fold_circuitify(&ExtOpStackTable::ext_initial_constraints_as_circuits), + &mut build_fold_circuitify(&ExtOpStackTable::ext_consistency_constraints_as_circuits), + &mut build_fold_circuitify(&ExtOpStackTable::ext_transition_constraints_as_circuits), + &mut build_fold_circuitify(&ExtOpStackTable::ext_terminal_constraints_as_circuits), ); write(&table_name_snake, source_code); @@ -57,10 +59,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut ExtRamTable::ext_initial_constraints_as_circuits(), - &mut ExtRamTable::ext_consistency_constraints_as_circuits(), - &mut ExtRamTable::ext_transition_constraints_as_circuits(), - &mut ExtRamTable::ext_terminal_constraints_as_circuits(), + &mut build_fold_circuitify(&ExtRamTable::ext_initial_constraints_as_circuits), + &mut build_fold_circuitify(&ExtRamTable::ext_consistency_constraints_as_circuits), + &mut build_fold_circuitify(&ExtRamTable::ext_transition_constraints_as_circuits), + &mut build_fold_circuitify(&ExtRamTable::ext_terminal_constraints_as_circuits), ); write(&table_name_snake, source_code); @@ -69,10 +71,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut ExtJumpStackTable::ext_initial_constraints_as_circuits(), - &mut ExtJumpStackTable::ext_consistency_constraints_as_circuits(), - &mut ExtJumpStackTable::ext_transition_constraints_as_circuits(), - &mut ExtJumpStackTable::ext_terminal_constraints_as_circuits(), + &mut build_fold_circuitify(&ExtJumpStackTable::ext_initial_constraints_as_circuits), + &mut build_fold_circuitify(&ExtJumpStackTable::ext_consistency_constraints_as_circuits), + &mut build_fold_circuitify(&ExtJumpStackTable::ext_transition_constraints_as_circuits), + &mut build_fold_circuitify(&ExtJumpStackTable::ext_terminal_constraints_as_circuits), ); write(&table_name_snake, source_code); @@ -80,10 +82,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut ExtHashTable::ext_initial_constraints_as_circuits(), - &mut ExtHashTable::ext_consistency_constraints_as_circuits(), - &mut ExtHashTable::ext_transition_constraints_as_circuits(), - &mut ExtHashTable::ext_terminal_constraints_as_circuits(), + &mut build_fold_circuitify(&ExtHashTable::ext_initial_constraints_as_circuits), + &mut build_fold_circuitify(&ExtHashTable::ext_consistency_constraints_as_circuits), + &mut build_fold_circuitify(&ExtHashTable::ext_transition_constraints_as_circuits), + &mut build_fold_circuitify(&ExtHashTable::ext_terminal_constraints_as_circuits), ); write(&table_name_snake, source_code); @@ -91,10 +93,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut ExtCascadeTable::ext_initial_constraints_as_circuits(), - &mut ExtCascadeTable::ext_consistency_constraints_as_circuits(), - &mut ExtCascadeTable::ext_transition_constraints_as_circuits(), - &mut ExtCascadeTable::ext_terminal_constraints_as_circuits(), + &mut build_fold_circuitify(&ExtCascadeTable::ext_initial_constraints_as_circuits), + &mut build_fold_circuitify(&ExtCascadeTable::ext_consistency_constraints_as_circuits), + &mut build_fold_circuitify(&ExtCascadeTable::ext_transition_constraints_as_circuits), + &mut build_fold_circuitify(&ExtCascadeTable::ext_terminal_constraints_as_circuits), ); write(&table_name_snake, source_code); @@ -102,10 +104,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut ExtLookupTable::ext_initial_constraints_as_circuits(), - &mut ExtLookupTable::ext_consistency_constraints_as_circuits(), - &mut ExtLookupTable::ext_transition_constraints_as_circuits(), - &mut ExtLookupTable::ext_terminal_constraints_as_circuits(), + &mut build_fold_circuitify(&ExtLookupTable::ext_initial_constraints_as_circuits), + &mut build_fold_circuitify(&ExtLookupTable::ext_consistency_constraints_as_circuits), + &mut build_fold_circuitify(&ExtLookupTable::ext_transition_constraints_as_circuits), + &mut build_fold_circuitify(&ExtLookupTable::ext_terminal_constraints_as_circuits), ); write(&table_name_snake, source_code); @@ -113,10 +115,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut ExtU32Table::ext_initial_constraints_as_circuits(), - &mut ExtU32Table::ext_consistency_constraints_as_circuits(), - &mut ExtU32Table::ext_transition_constraints_as_circuits(), - &mut ExtU32Table::ext_terminal_constraints_as_circuits(), + &mut build_fold_circuitify(&ExtU32Table::ext_initial_constraints_as_circuits), + &mut build_fold_circuitify(&ExtU32Table::ext_consistency_constraints_as_circuits), + &mut build_fold_circuitify(&ExtU32Table::ext_transition_constraints_as_circuits), + &mut build_fold_circuitify(&ExtU32Table::ext_terminal_constraints_as_circuits), ); write(&table_name_snake, source_code); @@ -125,10 +127,10 @@ fn main() { let source_code = gen( table_name_snake, table_name_camel, - &mut GrandCrossTableArg::ext_initial_constraints_as_circuits(), - &mut GrandCrossTableArg::ext_consistency_constraints_as_circuits(), - &mut GrandCrossTableArg::ext_transition_constraints_as_circuits(), - &mut GrandCrossTableArg::ext_terminal_constraints_as_circuits(), + &mut build_fold_circuitify(&GrandCrossTableArg::ext_initial_constraints_as_circuits), + &mut build_fold_circuitify(&GrandCrossTableArg::ext_consistency_constraints_as_circuits), + &mut build_fold_circuitify(&GrandCrossTableArg::ext_transition_constraints_as_circuits), + &mut build_fold_circuitify(&GrandCrossTableArg::ext_terminal_constraints_as_circuits), ); write(table_name_snake, source_code); @@ -137,6 +139,20 @@ fn main() { } } +fn build_fold_circuitify( + circuit_monad_function: &dyn Fn( + &ConstraintCircuitBuilder, + ) -> Vec>, +) -> Vec> { + let circuit_builder = ConstraintCircuitBuilder::new(); + let mut constraints = circuit_monad_function(&circuit_builder); + ConstraintCircuitMonad::constant_folding(&mut constraints); + constraints + .into_iter() + .map(|circuit| circuit.consume()) + .collect() +} + fn construct_needed_table_identifiers(table_name_constituents: &[&str]) -> (String, String) { let table_name_snake = format!("{}_table", table_name_constituents.join("_")); let title_case = table_name_constituents diff --git a/triton-vm/src/table/cascade_table.rs b/triton-vm/src/table/cascade_table.rs index 4be28f3b..f1263538 100644 --- a/triton-vm/src/table/cascade_table.rs +++ b/triton-vm/src/table/cascade_table.rs @@ -12,7 +12,6 @@ use twenty_first::shared_math::x_field_element::XFieldElement; use crate::table::challenges::ChallengeId; use crate::table::challenges::ChallengeId::*; use crate::table::challenges::Challenges; -use crate::table::constraint_circuit::ConstraintCircuit; use crate::table::constraint_circuit::ConstraintCircuitBuilder; use crate::table::constraint_circuit::ConstraintCircuitMonad; use crate::table::constraint_circuit::DualRowIndicator; @@ -135,9 +134,9 @@ impl CascadeTable { } impl ExtCascadeTable { - pub fn ext_initial_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); - + pub fn ext_initial_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let base_row = |col_id: CascadeBaseTableColumn| { circuit_builder.input(BaseRow(col_id.master_base_table_index())) }; @@ -200,17 +199,15 @@ impl ExtCascadeTable { * lookup_table_log_derivative_has_accumulated_first_row + is_padding * lookup_table_log_derivative_is_default_initial; - let mut constraints = [ + vec![ hash_table_log_derivative_is_initialized_correctly, lookup_table_log_derivative_is_initialized_correctly, - ]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_consistency_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); - + pub fn ext_consistency_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let base_row = |col_id: CascadeBaseTableColumn| { circuit_builder.input(BaseRow(col_id.master_base_table_index())) }; @@ -219,13 +216,12 @@ impl ExtCascadeTable { let is_padding = base_row(IsPadding); let is_padding_is_0_or_1 = is_padding.clone() * (one - is_padding); - let mut constraints = [is_padding_is_0_or_1]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + vec![is_padding_is_0_or_1] } - pub fn ext_transition_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_transition_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let challenge = |c| circuit_builder.challenge(c); let constant = |c: u64| circuit_builder.b_constant(c.into()); @@ -304,16 +300,17 @@ impl ExtCascadeTable { * lookup_table_log_derivative_accumulates_next_row + is_padding_next * lookup_table_log_derivative_remains; - let mut constraints = [ + vec![ if_current_row_is_padding_row_then_next_row_is_padding_row, hash_table_log_derivative_updates_correctly, lookup_table_log_derivative_updates_correctly, - ]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_terminal_constraints_as_circuits() -> Vec> { + pub fn ext_terminal_constraints_as_circuits( + _circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + // no further constraints vec![] } } diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 50718df7..499dfe91 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -1641,6 +1641,24 @@ mod constraint_circuit_tests { } } + /// Get the constraints defined in the given function, perform constant folding, and return + /// them as a vector of `ConstraintCircuit`s. + fn build_fold_circuitify( + circuit_monad_function: &dyn Fn( + &ConstraintCircuitBuilder, + ) -> Vec>, + ) -> Vec> { + let circuit_builder = ConstraintCircuitBuilder::new(); + let mut constraints = circuit_monad_function(&circuit_builder); + ConstraintCircuitMonad::constant_folding(&mut constraints); + constraints + .into_iter() + .map(|circuit| circuit.consume()) + .collect() + } + + /// Verify that all nodes evaluate to a unique value when given a randomized input. + /// If this is not the case two nodes that are not equal evaluate to the same value. fn table_constraints_prop( mut constraints: Vec>, challenges: &Challenges, @@ -1648,11 +1666,6 @@ mod constraint_circuit_tests { ) { ConstraintCircuit::assert_has_unique_ids(&mut constraints); - // Verify that all nodes evaluate to a unique value when given a randomized input. - // If this is not the case two nodes that are not equal evaluate to the same value. - // let input: Vec = random_elements(constraints[0].var_count); - // let base_input: Vec = random_elements(master_table::NUM_BASE_COLUMNS); - // let ext_input: Vec = random_elements(master_table::NUM_EXT_COLUMNS); let base_table = Array2::from_shape_simple_fn( [2, master_table::NUM_BASE_COLUMNS], random::, @@ -1676,117 +1689,119 @@ mod constraint_circuit_tests { #[test] fn constant_folding_processor_table_test() { let challenges = Challenges::placeholder(&[], &[]); - let constraint_circuits = ExtProcessorTable::ext_initial_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "processor initial"); - let constraint_circuits = ExtProcessorTable::ext_consistency_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "processor consistency"); - let constraint_circuits = ExtProcessorTable::ext_transition_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "processor transition"); - let constraint_circuits = ExtProcessorTable::ext_terminal_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "processor terminal"); + let init = ExtProcessorTable::ext_initial_constraints_as_circuits(); + let cons = ExtProcessorTable::ext_consistency_constraints_as_circuits(); + let tran = ExtProcessorTable::ext_transition_constraints_as_circuits(); + let term = ExtProcessorTable::ext_terminal_constraints_as_circuits(); + table_constraints_prop(init, &challenges, "processor initial"); + table_constraints_prop(cons, &challenges, "processor consistency"); + table_constraints_prop(tran, &challenges, "processor transition"); + table_constraints_prop(term, &challenges, "processor terminal"); } #[test] fn constant_folding_program_table_test() { let challenges = Challenges::placeholder(&[], &[]); - let constraint_circuits = ExtProgramTable::ext_initial_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "program initial"); - let constraint_circuits = ExtProgramTable::ext_consistency_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "program consistency"); - let constraint_circuits = ExtProgramTable::ext_transition_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "program transition"); - let constraint_circuits = ExtProgramTable::ext_terminal_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "program terminal"); + let init = build_fold_circuitify(&ExtProgramTable::ext_initial_constraints_as_circuits); + let cons = build_fold_circuitify(&ExtProgramTable::ext_consistency_constraints_as_circuits); + let tran = build_fold_circuitify(&ExtProgramTable::ext_transition_constraints_as_circuits); + let term = build_fold_circuitify(&ExtProgramTable::ext_terminal_constraints_as_circuits); + table_constraints_prop(init, &challenges, "program initial"); + table_constraints_prop(cons, &challenges, "program consistency"); + table_constraints_prop(tran, &challenges, "program transition"); + table_constraints_prop(term, &challenges, "program terminal"); } #[test] fn constant_folding_jump_stack_table_test() { let challenges = Challenges::placeholder(&[], &[]); - let constraint_circuits = ExtJumpStackTable::ext_initial_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "jump stack initial"); - let constraint_circuits = ExtJumpStackTable::ext_consistency_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "jump stack consistency"); - let constraint_circuits = ExtJumpStackTable::ext_transition_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "jump stack transition"); - let constraint_circuits = ExtJumpStackTable::ext_terminal_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "jump stack terminal"); + let init = build_fold_circuitify(&ExtJumpStackTable::ext_initial_constraints_as_circuits); + let cons = + build_fold_circuitify(&ExtJumpStackTable::ext_consistency_constraints_as_circuits); + let tran = + build_fold_circuitify(&ExtJumpStackTable::ext_transition_constraints_as_circuits); + let term = build_fold_circuitify(&ExtJumpStackTable::ext_terminal_constraints_as_circuits); + table_constraints_prop(init, &challenges, "jump stack initial"); + table_constraints_prop(cons, &challenges, "jump stack consistency"); + table_constraints_prop(tran, &challenges, "jump stack transition"); + table_constraints_prop(term, &challenges, "jump stack terminal"); } #[test] fn constant_folding_op_stack_table_test() { let challenges = Challenges::placeholder(&[], &[]); - let constraint_circuits = ExtOpStackTable::ext_initial_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "op stack initial"); - let constraint_circuits = ExtOpStackTable::ext_consistency_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "op stack consistency"); - let constraint_circuits = ExtOpStackTable::ext_transition_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "op stack transition"); - let constraint_circuits = ExtOpStackTable::ext_terminal_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "op stack terminal"); + let init = build_fold_circuitify(&ExtOpStackTable::ext_initial_constraints_as_circuits); + let cons = build_fold_circuitify(&ExtOpStackTable::ext_consistency_constraints_as_circuits); + let tran = build_fold_circuitify(&ExtOpStackTable::ext_transition_constraints_as_circuits); + let term = build_fold_circuitify(&ExtOpStackTable::ext_terminal_constraints_as_circuits); + table_constraints_prop(init, &challenges, "op stack initial"); + table_constraints_prop(cons, &challenges, "op stack consistency"); + table_constraints_prop(tran, &challenges, "op stack transition"); + table_constraints_prop(term, &challenges, "op stack terminal"); } #[test] fn constant_folding_ram_table_test() { let challenges = Challenges::placeholder(&[], &[]); - let constraint_circuits = ExtRamTable::ext_initial_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "ram initial"); - let constraint_circuits = ExtRamTable::ext_consistency_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "ram consistency"); - let constraint_circuits = ExtRamTable::ext_transition_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "ram transition"); - let constraint_circuits = ExtRamTable::ext_terminal_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "ram terminal"); + let init = build_fold_circuitify(&ExtRamTable::ext_initial_constraints_as_circuits); + let cons = build_fold_circuitify(&ExtRamTable::ext_consistency_constraints_as_circuits); + let tran = build_fold_circuitify(&ExtRamTable::ext_transition_constraints_as_circuits); + let term = build_fold_circuitify(&ExtRamTable::ext_terminal_constraints_as_circuits); + table_constraints_prop(init, &challenges, "ram initial"); + table_constraints_prop(cons, &challenges, "ram consistency"); + table_constraints_prop(tran, &challenges, "ram transition"); + table_constraints_prop(term, &challenges, "ram terminal"); } #[test] fn constant_folding_hash_table_test() { let challenges = Challenges::placeholder(&[], &[]); - let constraint_circuits = ExtHashTable::ext_initial_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "hash initial"); - let constraint_circuits = ExtHashTable::ext_consistency_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "hash consistency"); - let constraint_circuits = ExtHashTable::ext_transition_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "hash transition"); - let constraint_circuits = ExtHashTable::ext_terminal_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "hash terminal"); + let init = build_fold_circuitify(&ExtHashTable::ext_initial_constraints_as_circuits); + let cons = build_fold_circuitify(&ExtHashTable::ext_consistency_constraints_as_circuits); + let tran = build_fold_circuitify(&ExtHashTable::ext_transition_constraints_as_circuits); + let term = build_fold_circuitify(&ExtHashTable::ext_terminal_constraints_as_circuits); + table_constraints_prop(init, &challenges, "hash initial"); + table_constraints_prop(cons, &challenges, "hash consistency"); + table_constraints_prop(tran, &challenges, "hash transition"); + table_constraints_prop(term, &challenges, "hash terminal"); } #[test] fn constant_folding_u32_table_test() { let challenges = Challenges::placeholder(&[], &[]); - let constraint_circuits = ExtU32Table::ext_initial_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "u32 initial"); - let constraint_circuits = ExtU32Table::ext_consistency_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "u32 consistency"); - let constraint_circuits = ExtU32Table::ext_transition_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "u32 transition"); - let constraint_circuits = ExtU32Table::ext_terminal_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "u32 terminal"); + let init = build_fold_circuitify(&ExtU32Table::ext_initial_constraints_as_circuits); + let cons = build_fold_circuitify(&ExtU32Table::ext_consistency_constraints_as_circuits); + let tran = build_fold_circuitify(&ExtU32Table::ext_transition_constraints_as_circuits); + let term = build_fold_circuitify(&ExtU32Table::ext_terminal_constraints_as_circuits); + table_constraints_prop(init, &challenges, "u32 initial"); + table_constraints_prop(cons, &challenges, "u32 consistency"); + table_constraints_prop(tran, &challenges, "u32 transition"); + table_constraints_prop(term, &challenges, "u32 terminal"); } #[test] fn constant_folding_cascade_table_test() { let challenges = Challenges::placeholder(&[], &[]); - let constraint_circuits = ExtCascadeTable::ext_initial_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "cascade initial"); - let constraint_circuits = ExtCascadeTable::ext_consistency_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "cascade consistency"); - let constraint_circuits = ExtCascadeTable::ext_transition_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "cascade transition"); - let constraint_circuits = ExtCascadeTable::ext_terminal_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "cascade terminal"); + let init = build_fold_circuitify(&ExtCascadeTable::ext_initial_constraints_as_circuits); + let cons = build_fold_circuitify(&ExtCascadeTable::ext_consistency_constraints_as_circuits); + let tran = build_fold_circuitify(&ExtCascadeTable::ext_transition_constraints_as_circuits); + let term = build_fold_circuitify(&ExtCascadeTable::ext_terminal_constraints_as_circuits); + table_constraints_prop(init, &challenges, "cascade initial"); + table_constraints_prop(cons, &challenges, "cascade consistency"); + table_constraints_prop(tran, &challenges, "cascade transition"); + table_constraints_prop(term, &challenges, "cascade terminal"); } #[test] fn constant_folding_lookup_table_test() { let challenges = Challenges::placeholder(&[], &[]); - let constraint_circuits = ExtLookupTable::ext_initial_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "lookup initial"); - let constraint_circuits = ExtLookupTable::ext_consistency_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "lookup consistency"); - let constraint_circuits = ExtLookupTable::ext_transition_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "lookup transition"); - let constraint_circuits = ExtLookupTable::ext_terminal_constraints_as_circuits(); - table_constraints_prop(constraint_circuits, &challenges, "lookup terminal"); + let init = build_fold_circuitify(&ExtLookupTable::ext_initial_constraints_as_circuits); + let cons = build_fold_circuitify(&ExtLookupTable::ext_consistency_constraints_as_circuits); + let tran = build_fold_circuitify(&ExtLookupTable::ext_transition_constraints_as_circuits); + let term = build_fold_circuitify(&ExtLookupTable::ext_terminal_constraints_as_circuits); + table_constraints_prop(init, &challenges, "lookup initial"); + table_constraints_prop(cons, &challenges, "lookup consistency"); + table_constraints_prop(tran, &challenges, "lookup transition"); + table_constraints_prop(term, &challenges, "lookup terminal"); } } diff --git a/triton-vm/src/table/cross_table_argument.rs b/triton-vm/src/table/cross_table_argument.rs index 332d27ac..3d77c877 100644 --- a/triton-vm/src/table/cross_table_argument.rs +++ b/triton-vm/src/table/cross_table_argument.rs @@ -9,7 +9,6 @@ use twenty_first::shared_math::traits::Inverse; use twenty_first::shared_math::x_field_element::XFieldElement; use crate::table::challenges::ChallengeId::*; -use crate::table::constraint_circuit::ConstraintCircuit; use crate::table::constraint_circuit::ConstraintCircuitBuilder; use crate::table::constraint_circuit::ConstraintCircuitMonad; use crate::table::constraint_circuit::DualRowIndicator; @@ -133,23 +132,30 @@ impl LookupArg { pub struct GrandCrossTableArg {} impl GrandCrossTableArg { - pub fn ext_initial_constraints_as_circuits() -> Vec> { + pub fn ext_initial_constraints_as_circuits( + _circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { // no further constraints vec![] } - pub fn ext_consistency_constraints_as_circuits() -> Vec> { + pub fn ext_consistency_constraints_as_circuits( + _circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { // no further constraints vec![] } - pub fn ext_transition_constraints_as_circuits() -> Vec> { + pub fn ext_transition_constraints_as_circuits( + _circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { // no further constraints vec![] } - pub fn ext_terminal_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_terminal_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let challenge = |c| circuit_builder.challenge(c); let ext_row = |col_index| circuit_builder.input(ExtRow(col_index)); @@ -220,7 +226,7 @@ impl GrandCrossTableArg { - ram_ext_row(ram_cjdld) - jump_stack_ext_row(j_stack_cjdld); - let mut constraints = [ + vec![ input_to_processor, processor_to_output, instruction_lookup, @@ -234,8 +240,6 @@ impl GrandCrossTableArg { cascade_to_lookup, processor_to_u32, clock_jump_difference_lookup, - ]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } } diff --git a/triton-vm/src/table/hash_table.rs b/triton-vm/src/table/hash_table.rs index 5c26272d..fe1a407a 100644 --- a/triton-vm/src/table/hash_table.rs +++ b/triton-vm/src/table/hash_table.rs @@ -24,7 +24,6 @@ use triton_opcodes::instruction::Instruction; use crate::table::cascade_table::CascadeTable; use crate::table::challenges::ChallengeId::*; use crate::table::challenges::Challenges; -use crate::table::constraint_circuit::ConstraintCircuit; use crate::table::constraint_circuit::ConstraintCircuitBuilder; use crate::table::constraint_circuit::ConstraintCircuitMonad; use crate::table::constraint_circuit::DualRowIndicator; @@ -81,8 +80,9 @@ impl ExtHashTable { * capital_r_inv } - pub fn ext_initial_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_initial_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let challenge = |c| circuit_builder.challenge(c); let constant = |c: u64| circuit_builder.b_constant(c.into()); let b_constant = |c| circuit_builder.b_constant(c); @@ -105,28 +105,28 @@ impl ExtHashTable { let one = constant(1); let state_0 = Self::re_compose_16_bit_limbs( - &circuit_builder, + circuit_builder, base_row(State0HighestLkIn), base_row(State0MidHighLkIn), base_row(State0MidLowLkIn), base_row(State0LowestLkIn), ); let state_1 = Self::re_compose_16_bit_limbs( - &circuit_builder, + circuit_builder, base_row(State1HighestLkIn), base_row(State1MidHighLkIn), base_row(State1MidLowLkIn), base_row(State1LowestLkIn), ); let state_2 = Self::re_compose_16_bit_limbs( - &circuit_builder, + circuit_builder, base_row(State2HighestLkIn), base_row(State2MidHighLkIn), base_row(State2MidLowLkIn), base_row(State2LowestLkIn), ); let state_3 = Self::re_compose_16_bit_limbs( - &circuit_builder, + circuit_builder, base_row(State3HighestLkIn), base_row(State3MidHighLkIn), base_row(State3MidLowLkIn), @@ -212,12 +212,8 @@ impl ExtHashTable { running_evaluation_sponge_absorb_is_initialized_correctly, ]; constraints - .append(&mut Self::all_cascade_log_derivative_init_circuits(&circuit_builder).to_vec()); - ConstraintCircuitMonad::constant_folding(&mut constraints); + .append(&mut Self::all_cascade_log_derivative_init_circuits(circuit_builder).to_vec()); constraints - .into_iter() - .map(|circuit| circuit.consume()) - .collect() } fn round_number_deselector( @@ -384,8 +380,9 @@ impl ExtHashTable { ] } - pub fn ext_consistency_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_consistency_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let constant = |c: u64| circuit_builder.b_constant(c.into()); let base_row = |column_id: HashBaseTableColumn| { circuit_builder.input(BaseRow(column_id.master_base_table_index())) @@ -406,9 +403,9 @@ impl ExtHashTable { let ci_is_squeeze = ci - constant(Instruction::Squeeze.opcode() as u64); let round_number_is_not_neg_1 = - Self::round_number_deselector(&circuit_builder, &round_number, -1); + Self::round_number_deselector(circuit_builder, &round_number, -1); let round_number_is_not_0 = - Self::round_number_deselector(&circuit_builder, &round_number, 0); + Self::round_number_deselector(circuit_builder, &round_number, 0); let if_padding_row_then_ci_is_hash = round_number_is_not_neg_1 * ci_is_hash.clone(); @@ -554,7 +551,7 @@ impl ExtHashTable { let round_constant_for_current_row = circuit_builder.b_constant(ROUND_CONSTANTS[round_constant_idx_for_current_row]); let round_deselector_circuit = Self::round_number_deselector( - &circuit_builder, + circuit_builder, &round_number, round_idx as isize, ); @@ -565,11 +562,7 @@ impl ExtHashTable { constraints.push(round_constant_constraint_circuit); } - ConstraintCircuitMonad::constant_folding(&mut constraints); constraints - .into_iter() - .map(|circuit| circuit.consume()) - .collect() } fn round_constant_column_by_index(index: usize) -> HashBaseTableColumn { @@ -594,8 +587,9 @@ impl ExtHashTable { } } - pub fn ext_transition_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_transition_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let challenge = |c| circuit_builder.challenge(c); let constant = |c: u64| circuit_builder.b_constant(c.into()); let b_constant = |c| circuit_builder.b_constant(c); @@ -635,28 +629,28 @@ impl ExtHashTable { let running_evaluation_sponge_next = next_ext_row(SpongeRunningEvaluation); let state_0 = Self::re_compose_16_bit_limbs( - &circuit_builder, + circuit_builder, current_base_row(State0HighestLkIn), current_base_row(State0MidHighLkIn), current_base_row(State0MidLowLkIn), current_base_row(State0LowestLkIn), ); let state_1 = Self::re_compose_16_bit_limbs( - &circuit_builder, + circuit_builder, current_base_row(State1HighestLkIn), current_base_row(State1MidHighLkIn), current_base_row(State1MidLowLkIn), current_base_row(State1LowestLkIn), ); let state_2 = Self::re_compose_16_bit_limbs( - &circuit_builder, + circuit_builder, current_base_row(State2HighestLkIn), current_base_row(State2MidHighLkIn), current_base_row(State2MidLowLkIn), current_base_row(State2LowestLkIn), ); let state_3 = Self::re_compose_16_bit_limbs( - &circuit_builder, + circuit_builder, current_base_row(State3HighestLkIn), current_base_row(State3MidHighLkIn), current_base_row(State3MidLowLkIn), @@ -683,7 +677,7 @@ impl ExtHashTable { ]; let (state_next, hash_function_round_correctly_performs_update) = - Self::tip5_constraints_as_circuits(&circuit_builder); + Self::tip5_constraints_as_circuits(circuit_builder); let state_weights = [ HashStateWeight0, @@ -711,9 +705,9 @@ impl ExtHashTable { // -1 -> -1 let round_number_is_not_neg_1 = - Self::round_number_deselector(&circuit_builder, &round_number, -1); + Self::round_number_deselector(circuit_builder, &round_number, -1); let round_number_is_not_5 = - Self::round_number_deselector(&circuit_builder, &round_number, 5); + Self::round_number_deselector(circuit_builder, &round_number, 5); let round_number_is_0_through_5_or_round_number_next_is_neg_1 = round_number_is_not_neg_1 * (round_number_next.clone() + constant(1)); @@ -737,7 +731,7 @@ impl ExtHashTable { // copy capacity between rounds with index 5 and 0 if instruction is “absorb” let round_number_next_is_not_0 = - Self::round_number_deselector(&circuit_builder, &round_number_next, 0); + Self::round_number_deselector(circuit_builder, &round_number_next, 0); let round_number_next_is_0 = round_number_next.clone(); let difference_of_capacity_registers = state_current[RATE..] @@ -803,11 +797,8 @@ impl ExtHashTable { // If (and only if) the row number in the next row is 5 and the current instruction in // the next row corresponds to `hash`, update running evaluation “hash digest.” let round_number_next_is_5 = round_number_next.clone() - constant(NUM_ROUNDS as u64); - let round_number_next_is_not_5 = Self::round_number_deselector( - &circuit_builder, - &round_number_next, - NUM_ROUNDS as isize, - ); + let round_number_next_is_not_5 = + Self::round_number_deselector(circuit_builder, &round_number_next, NUM_ROUNDS as isize); let running_evaluation_hash_digest_remains = running_evaluation_hash_digest_next.clone() - running_evaluation_hash_digest.clone(); let hash_digest = state_next[..DIGEST_LENGTH].to_owned(); @@ -855,7 +846,7 @@ impl ExtHashTable { + if_round_no_next_is_not_0_then_running_evaluation_sponge_absorb_remains + if_ci_next_is_not_spongy_then_running_evaluation_sponge_absorb_remains; - let mut constraints = [ + [ vec![ round_number_is_0_through_5_or_round_number_next_is_neg_1, round_number_is_neg_1_through_4_or_round_number_next_is_0_or_neg_1, @@ -871,15 +862,9 @@ impl ExtHashTable { running_evaluation_hash_digest_is_updated_correctly, running_evaluation_sponge_is_updated_correctly, ], - Self::all_cascade_log_derivative_update_circuits(&circuit_builder).to_vec(), + Self::all_cascade_log_derivative_update_circuits(circuit_builder).to_vec(), ] - .concat(); - - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints - .into_iter() - .map(|circuit| circuit.consume()) - .collect() + .concat() } fn tip5_constraints_as_circuits( @@ -1207,7 +1192,9 @@ impl ExtHashTable { ] } - pub fn ext_terminal_constraints_as_circuits() -> Vec> { + pub fn ext_terminal_constraints_as_circuits( + _circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { // no more constraints vec![] } diff --git a/triton-vm/src/table/jump_stack_table.rs b/triton-vm/src/table/jump_stack_table.rs index c365ca8e..2c7b83f7 100644 --- a/triton-vm/src/table/jump_stack_table.rs +++ b/triton-vm/src/table/jump_stack_table.rs @@ -17,7 +17,6 @@ use twenty_first::shared_math::x_field_element::XFieldElement; use crate::table::challenges::ChallengeId::*; use crate::table::challenges::Challenges; -use crate::table::constraint_circuit::ConstraintCircuit; use crate::table::constraint_circuit::ConstraintCircuitBuilder; use crate::table::constraint_circuit::DualRowIndicator; use crate::table::constraint_circuit::DualRowIndicator::*; @@ -48,9 +47,9 @@ pub struct JumpStackTable {} pub struct ExtJumpStackTable {} impl ExtJumpStackTable { - pub fn ext_initial_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); - + pub fn ext_initial_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let clk = circuit_builder.input(BaseRow(CLK.master_base_table_index())); let jsp = circuit_builder.input(BaseRow(JSP.master_base_table_index())); let jso = circuit_builder.input(BaseRow(JSO.master_base_table_index())); @@ -70,27 +69,26 @@ impl ExtJumpStackTable { let clock_jump_diff_log_derivative_starts_correctly = clock_jump_diff_log_derivative - circuit_builder.x_constant(LookupArg::default_initial()); - let mut constraints = [ + vec![ clk, jsp, jso, jsd, rppa_starts_correctly, clock_jump_diff_log_derivative_starts_correctly, - ]; - - ConstraintCircuitMonad::constant_folding(&mut constraints); - - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_consistency_constraints_as_circuits() -> Vec> { + pub fn ext_consistency_constraints_as_circuits( + _circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { // no further constraints vec![] } - pub fn ext_transition_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_transition_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let one = circuit_builder.b_constant(1u32.into()); let call_opcode = circuit_builder.b_constant(Instruction::Call(Default::default()).opcode_b()); @@ -175,20 +173,20 @@ impl ExtJumpStackTable { * log_derivative_accumulates + (jsp_next - jsp) * log_derivative_remains; - let mut constraints = [ + vec![ jsp_inc_or_stays, jsp_inc_or_jso_stays_or_ci_is_ret, jsp_inc_or_jsd_stays_or_ci_ret, jsp_inc_or_clk_inc_or_ci_call_or_ci_ret, rppa_updates_correctly, log_derivative_updates_correctly, - ]; - - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_terminal_constraints_as_circuits() -> Vec> { + pub fn ext_terminal_constraints_as_circuits( + _circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + // no further constraints vec![] } } diff --git a/triton-vm/src/table/lookup_table.rs b/triton-vm/src/table/lookup_table.rs index a3c8cfce..ded8d8cf 100644 --- a/triton-vm/src/table/lookup_table.rs +++ b/triton-vm/src/table/lookup_table.rs @@ -13,7 +13,6 @@ use twenty_first::shared_math::x_field_element::XFieldElement; use crate::table::challenges::ChallengeId; use crate::table::challenges::ChallengeId::*; use crate::table::challenges::Challenges; -use crate::table::constraint_circuit::ConstraintCircuit; use crate::table::constraint_circuit::ConstraintCircuitBuilder; use crate::table::constraint_circuit::ConstraintCircuitMonad; use crate::table::constraint_circuit::DualRowIndicator; @@ -122,9 +121,9 @@ impl LookupTable { } impl ExtLookupTable { - pub fn ext_initial_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); - + pub fn ext_initial_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let base_row = |col_id: LookupBaseTableColumn| { circuit_builder.input(BaseRow(col_id.master_base_table_index())) }; @@ -159,17 +158,16 @@ impl ExtLookupTable { - eval_argument_default_initial * public_indeterminate - lookup_output; - let mut constraints = [ + vec![ lookup_input_is_0, cascade_table_log_derivative_is_initialized_correctly, public_evaluation_argument_is_initialized_correctly, - ]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_consistency_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_consistency_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let constant = |c: u64| circuit_builder.b_constant(c.into()); let base_row = |col_id: LookupBaseTableColumn| { circuit_builder.input(BaseRow(col_id.master_base_table_index())) @@ -177,13 +175,12 @@ impl ExtLookupTable { let padding_is_0_or_1 = base_row(IsPadding) * (constant(1) - base_row(IsPadding)); - let mut constraints = [padding_is_0_or_1]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + vec![padding_is_0_or_1] } - pub fn ext_transition_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_transition_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let one = circuit_builder.b_constant(BFIELD_ONE); let current_base_row = |col_id: LookupBaseTableColumn| { @@ -253,18 +250,17 @@ impl ExtLookupTable { (one - is_padding_next.clone()) * public_evaluation_argument_updates + is_padding_next * public_evaluation_argument_remains; - let mut constraints = [ + vec![ if_current_row_is_padding_row_then_next_row_is_padding_row, lookup_input_increments_if_and_only_if_next_row_is_not_padding_row, cascade_table_log_derivative_updates_if_and_only_if_next_row_is_not_padding_row, public_evaluation_argument_updates_if_and_only_if_next_row_is_not_padding_row, - ]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_terminal_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_terminal_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let challenge = |challenge_id: ChallengeId| circuit_builder.challenge(challenge_id); let ext_row = |col_id: LookupExtTableColumn| { circuit_builder.input(ExtRow(col_id.master_ext_table_index())) @@ -273,8 +269,6 @@ impl ExtLookupTable { let narrow_table_terminal_matches_user_supplied_terminal = ext_row(PublicEvaluationArgument) - challenge(LookupTablePublicTerminal); - let mut constraints = [narrow_table_terminal_matches_user_supplied_terminal]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + vec![narrow_table_terminal_matches_user_supplied_terminal] } } diff --git a/triton-vm/src/table/op_stack_table.rs b/triton-vm/src/table/op_stack_table.rs index 520b17a2..d6e1dfcc 100644 --- a/triton-vm/src/table/op_stack_table.rs +++ b/triton-vm/src/table/op_stack_table.rs @@ -15,7 +15,6 @@ use twenty_first::shared_math::x_field_element::XFieldElement; use crate::op_stack::OP_STACK_REG_COUNT; use crate::table::challenges::ChallengeId::*; use crate::table::challenges::Challenges; -use crate::table::constraint_circuit::ConstraintCircuit; use crate::table::constraint_circuit::ConstraintCircuitBuilder; use crate::table::constraint_circuit::DualRowIndicator; use crate::table::constraint_circuit::DualRowIndicator::*; @@ -46,9 +45,9 @@ pub struct OpStackTable {} pub struct ExtOpStackTable {} impl ExtOpStackTable { - pub fn ext_initial_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); - + pub fn ext_initial_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let clk = circuit_builder.input(BaseRow(CLK.master_base_table_index())); let ib1 = circuit_builder.input(BaseRow(IB1ShrinkStack.master_base_table_index())); let osp = circuit_builder.input(BaseRow(OSP.master_base_table_index())); @@ -74,24 +73,25 @@ impl ExtOpStackTable { let clock_jump_diff_log_derivative_is_initialized_correctly = clock_jump_diff_log_derivative - circuit_builder.x_constant(LookupArg::default_initial()); - let mut constraints = [ + vec![ clk_is_0, osv_is_0, osp_is_16, rppa_starts_correctly, clock_jump_diff_log_derivative_is_initialized_correctly, - ]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_consistency_constraints_as_circuits() -> Vec> { + pub fn ext_consistency_constraints_as_circuits( + _circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { // no further constraints vec![] } - pub fn ext_transition_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::::new(); + pub fn ext_transition_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let one = circuit_builder.b_constant(1u32.into()); let clk = circuit_builder.input(CurrentBaseRow(CLK.master_base_table_index())); @@ -156,18 +156,17 @@ impl ExtOpStackTable { * log_derivative_accumulates + (osp_next - osp) * log_derivative_remains; - let mut constraints = [ + vec![ osp_increases_by_1_or_does_not_change, osp_increases_by_1_or_osv_does_not_change_or_shrink_stack, rppa_updates_correctly, log_derivative_updates_correctly, - ]; - - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_terminal_constraints_as_circuits() -> Vec> { + pub fn ext_terminal_constraints_as_circuits( + _circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { // no further constraints vec![] } diff --git a/triton-vm/src/table/program_table.rs b/triton-vm/src/table/program_table.rs index 3b79f382..28f91e68 100644 --- a/triton-vm/src/table/program_table.rs +++ b/triton-vm/src/table/program_table.rs @@ -11,7 +11,6 @@ use twenty_first::shared_math::x_field_element::XFieldElement; use crate::table::challenges::ChallengeId::*; use crate::table::challenges::Challenges; -use crate::table::constraint_circuit::ConstraintCircuit; use crate::table::constraint_circuit::ConstraintCircuitBuilder; use crate::table::constraint_circuit::DualRowIndicator; use crate::table::constraint_circuit::DualRowIndicator::*; @@ -40,9 +39,9 @@ pub struct ProgramTable {} pub struct ExtProgramTable {} impl ExtProgramTable { - pub fn ext_initial_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); - + pub fn ext_initial_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let address = circuit_builder.input(BaseRow(Address.master_base_table_index())); let instruction_lookup_log_derivative = circuit_builder.input(ExtRow( InstructionLookupServerLogDerivative.master_ext_table_index(), @@ -54,29 +53,26 @@ impl ExtProgramTable { instruction_lookup_log_derivative - circuit_builder.x_constant(LookupArg::default_initial()); - let mut constraints = [ + vec![ first_address_is_zero, instruction_lookup_log_derivative_is_initialized_correctly, - ]; - - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_consistency_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_consistency_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let one = circuit_builder.b_constant(1_u32.into()); let is_padding = circuit_builder.input(BaseRow(IsPadding.master_base_table_index())); let is_padding_is_bit = is_padding.clone() * (is_padding - one); - let mut constraints = [is_padding_is_bit]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + vec![is_padding_is_bit] } - pub fn ext_transition_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_transition_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let one = circuit_builder.b_constant(1u32.into()); let address = circuit_builder.input(CurrentBaseRow(Address.master_base_table_index())); let instruction = @@ -114,16 +110,16 @@ impl ExtProgramTable { * log_derivative_updates + is_padding * log_derivative_remains; - let mut constraints = [ + vec![ address_increases_by_one, is_padding_is_0_or_remains_unchanged, log_derivative_updates_if_and_only_if_not_a_padding_row, - ]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_terminal_constraints_as_circuits() -> Vec> { + pub fn ext_terminal_constraints_as_circuits( + _circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { // no further constraints vec![] } diff --git a/triton-vm/src/table/ram_table.rs b/triton-vm/src/table/ram_table.rs index 8c534f1e..338ade9c 100644 --- a/triton-vm/src/table/ram_table.rs +++ b/triton-vm/src/table/ram_table.rs @@ -18,7 +18,6 @@ use twenty_first::shared_math::x_field_element::XFieldElement; use crate::table::challenges::ChallengeId::*; use crate::table::challenges::Challenges; -use crate::table::constraint_circuit::ConstraintCircuit; use crate::table::constraint_circuit::ConstraintCircuitBuilder; use crate::table::constraint_circuit::DualRowIndicator; use crate::table::constraint_circuit::DualRowIndicator::*; @@ -313,8 +312,9 @@ impl RamTable { } impl ExtRamTable { - pub fn ext_initial_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_initial_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let one = circuit_builder.b_constant(1_u32.into()); let bezout_challenge = circuit_builder.challenge(RamTableBezoutRelationIndeterminate); @@ -364,7 +364,7 @@ impl ExtRamTable { let running_product_permutation_argument_is_initialized_correctly = rppa - (rppa_challenge - compressed_row_for_permutation_argument); - let mut constraints = [ + vec![ ramv_is_0_or_was_written_to, bezout_coefficient_polynomial_coefficient_0_is_0, bezout_coefficient_0_is_0, @@ -373,19 +373,19 @@ impl ExtRamTable { formal_derivative_is_1, running_product_permutation_argument_is_initialized_correctly, clock_jump_diff_log_derivative_is_initialized_correctly, - ]; - - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_consistency_constraints_as_circuits() -> Vec> { + pub fn ext_consistency_constraints_as_circuits( + _circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { // no further constraints vec![] } - pub fn ext_transition_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_transition_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let one = circuit_builder.b_constant(1u32.into()); let bezout_challenge = circuit_builder.challenge(RamTableBezoutRelationIndeterminate); @@ -510,7 +510,7 @@ impl ExtRamTable { let log_derivative_updates_correctly = (one - ramp_changes) * log_derivative_accumulates + ramp_diff * log_derivative_remains; - let mut constraints = [ + vec![ iord_is_0_or_iord_is_inverse_of_ramp_diff, ramp_diff_is_0_or_iord_is_inverse_of_ramp_diff, ramp_changes_or_write_mem_or_ramv_stays, @@ -523,14 +523,12 @@ impl ExtRamTable { bezout_coefficient_1_is_constructed_correctly, rppa_updates_correctly, log_derivative_updates_correctly, - ]; - - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_terminal_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_terminal_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let one = circuit_builder.b_constant(1_u32.into()); let rp = circuit_builder.input(ExtRow(RunningProductOfRAMP.master_ext_table_index())); @@ -540,8 +538,6 @@ impl ExtRamTable { let bezout_relation_holds = bc0 * rp + bc1 * fd - one; - let mut constraints = [bezout_relation_holds]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + vec![bezout_relation_holds] } } diff --git a/triton-vm/src/table/u32_table.rs b/triton-vm/src/table/u32_table.rs index 57e3a976..0aa678d2 100644 --- a/triton-vm/src/table/u32_table.rs +++ b/triton-vm/src/table/u32_table.rs @@ -19,7 +19,6 @@ use triton_opcodes::instruction::Instruction; use crate::table::challenges::ChallengeId::*; use crate::table::challenges::Challenges; -use crate::table::constraint_circuit::ConstraintCircuit; use crate::table::constraint_circuit::ConstraintCircuitBuilder; use crate::table::constraint_circuit::ConstraintCircuitMonad; use crate::table::constraint_circuit::DualRowIndicator; @@ -70,8 +69,9 @@ impl ExtU32Table { ) } - pub fn ext_initial_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_initial_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let challenge = |c| circuit_builder.challenge(c); let one = circuit_builder.b_constant(1_u32.into()); @@ -102,13 +102,12 @@ impl ExtU32Table { if_copy_flag_is_0_then_log_derivative_is_default_initial + if_copy_flag_is_1_then_log_derivative_has_accumulated_first_row; - let mut constraints = [running_sum_log_derivative_starts_correctly]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + vec![running_sum_log_derivative_starts_correctly] } - pub fn ext_consistency_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_consistency_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let one = circuit_builder.b_constant(1_u32.into()); let two = circuit_builder.b_constant(2_u32.into()); @@ -126,7 +125,7 @@ impl ExtU32Table { circuit_builder.input(BaseRow(LookupMultiplicity.master_base_table_index())); let instruction_deselector = |instruction_to_select| { - Self::instruction_deselector(instruction_to_select, &circuit_builder, &ci) + Self::instruction_deselector(instruction_to_select, circuit_builder, &ci) }; let copy_flag_is_bit = copy_flag.clone() * (one.clone() - copy_flag.clone()); @@ -175,7 +174,7 @@ impl ExtU32Table { let if_copy_flag_is_0_then_lookup_multiplicity_is_0 = (copy_flag - one) * lookup_multiplicity; - let mut constraints = [ + vec![ copy_flag_is_bit, copy_flag_is_0_or_bits_is_0, bits_minus_33_inv_is_inverse_of_bits_minus_33, @@ -191,13 +190,12 @@ impl ExtU32Table { result_is_initialized_correctly_for_pop_count, if_log_2_floor_on_0_then_vm_crashes, if_copy_flag_is_0_then_lookup_multiplicity_is_0, - ]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_transition_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); + pub fn ext_transition_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let challenge = |c| circuit_builder.challenge(c); let one = circuit_builder.b_constant(1_u32.into()); let two = circuit_builder.b_constant(2_u32.into()); @@ -226,7 +224,7 @@ impl ExtU32Table { )); let instruction_deselector = |instruction_to_select: Instruction| { - Self::instruction_deselector(instruction_to_select, &circuit_builder, &ci_next) + Self::instruction_deselector(instruction_to_select, circuit_builder, &ci_next) }; // helpful aliases @@ -361,7 +359,7 @@ impl ExtU32Table { * (challenge(U32Indeterminate) - compressed_row_next) - lookup_multiplicity_next); - let mut constraints = [ + vec![ if_copy_flag_next_is_1_then_lhs_is_0_or_ci_is_pow, if_copy_flag_next_is_1_then_rhs_is_0, if_copy_flag_next_is_0_then_ci_stays, @@ -384,14 +382,12 @@ impl ExtU32Table { if_copy_flag_next_is_0_and_ci_is_pop_count_then_result_increases_by_lhs_lsb, if_copy_flag_next_is_0_then_running_sum_log_derivative_stays, if_copy_flag_next_is_1_then_running_sum_log_derivative_accumulates_next_row, - ]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_terminal_constraints_as_circuits() -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); - + pub fn ext_terminal_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { let ci = circuit_builder.input(BaseRow(CI.master_base_table_index())); let lhs = circuit_builder.input(BaseRow(LHS.master_base_table_index())); let rhs = circuit_builder.input(BaseRow(RHS.master_base_table_index())); @@ -400,9 +396,7 @@ impl ExtU32Table { lhs * (ci - circuit_builder.b_constant(Instruction::Pow.opcode_b())); let rhs_is_0 = rhs; - let mut constraints = [lhs_is_0_or_ci_is_pow, rhs_is_0]; - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + vec![lhs_is_0_or_ci_is_pow, rhs_is_0] } } From 52db9457cbbbc9a9fe0e752021b8cdf32ec81c6f Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Sun, 7 May 2023 19:15:40 +0200 Subject: [PATCH 09/72] remove factory helper structs from the Processor Table --- constraint-evaluation-generator/src/main.rs | 8 +- triton-vm/src/table/constraint_circuit.rs | 10 +- triton-vm/src/table/processor_table.rs | 3874 +++++++++---------- 3 files changed, 1799 insertions(+), 2093 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index e6dde85c..2f4239a8 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -37,10 +37,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut ExtProcessorTable::ext_initial_constraints_as_circuits(), - &mut ExtProcessorTable::ext_consistency_constraints_as_circuits(), - &mut ExtProcessorTable::ext_transition_constraints_as_circuits(), - &mut ExtProcessorTable::ext_terminal_constraints_as_circuits(), + &mut build_fold_circuitify(&ExtProcessorTable::ext_initial_constraints_as_circuits), + &mut build_fold_circuitify(&ExtProcessorTable::ext_consistency_constraints_as_circuits), + &mut build_fold_circuitify(&ExtProcessorTable::ext_transition_constraints_as_circuits), + &mut build_fold_circuitify(&ExtProcessorTable::ext_terminal_constraints_as_circuits), ); write(&table_name_snake, source_code); diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 499dfe91..a0152b59 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -1689,10 +1689,12 @@ mod constraint_circuit_tests { #[test] fn constant_folding_processor_table_test() { let challenges = Challenges::placeholder(&[], &[]); - let init = ExtProcessorTable::ext_initial_constraints_as_circuits(); - let cons = ExtProcessorTable::ext_consistency_constraints_as_circuits(); - let tran = ExtProcessorTable::ext_transition_constraints_as_circuits(); - let term = ExtProcessorTable::ext_terminal_constraints_as_circuits(); + let init = build_fold_circuitify(&ExtProcessorTable::ext_initial_constraints_as_circuits); + let cons = + build_fold_circuitify(&ExtProcessorTable::ext_consistency_constraints_as_circuits); + let tran = + build_fold_circuitify(&ExtProcessorTable::ext_transition_constraints_as_circuits); + let term = build_fold_circuitify(&ExtProcessorTable::ext_terminal_constraints_as_circuits); table_constraints_prop(init, &challenges, "processor initial"); table_constraints_prop(cons, &challenges, "processor consistency"); table_constraints_prop(tran, &challenges, "processor transition"); diff --git a/triton-vm/src/table/processor_table.rs b/triton-vm/src/table/processor_table.rs index 0a408ff0..68c90c8a 100644 --- a/triton-vm/src/table/processor_table.rs +++ b/triton-vm/src/table/processor_table.rs @@ -1,5 +1,4 @@ use std::cmp::max; -use std::collections::HashMap; use std::fmt::Display; use std::ops::Mul; @@ -14,29 +13,30 @@ use ndarray::Axis; use num_traits::One; use num_traits::Zero; use strum::EnumCount; -use triton_opcodes::instruction::AnInstruction::*; -use triton_opcodes::instruction::Instruction; -use triton_opcodes::instruction::ALL_INSTRUCTIONS; -use triton_opcodes::ord_n::Ord8; use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::b_field_element::BFIELD_ONE; use twenty_first::shared_math::traits::Inverse; use twenty_first::shared_math::x_field_element::XFieldElement; +use triton_opcodes::instruction::AnInstruction::*; +use triton_opcodes::instruction::Instruction; +use triton_opcodes::instruction::ALL_INSTRUCTIONS; +use triton_opcodes::ord_n::Ord8; + +use crate::table::challenges::ChallengeId; use crate::table::challenges::ChallengeId::*; use crate::table::challenges::Challenges; -use crate::table::constraint_circuit::ConstraintCircuit; use crate::table::constraint_circuit::ConstraintCircuitBuilder; use crate::table::constraint_circuit::ConstraintCircuitMonad; use crate::table::constraint_circuit::DualRowIndicator; +use crate::table::constraint_circuit::DualRowIndicator::*; use crate::table::constraint_circuit::InputIndicator; use crate::table::constraint_circuit::SingleRowIndicator; +use crate::table::constraint_circuit::SingleRowIndicator::*; use crate::table::cross_table_argument::CrossTableArg; use crate::table::cross_table_argument::EvalArg; use crate::table::cross_table_argument::LookupArg; use crate::table::cross_table_argument::PermArg; -use crate::table::master_table::NUM_BASE_COLUMNS; -use crate::table::master_table::NUM_EXT_COLUMNS; use crate::table::table_column::MasterBaseTableColumn; use crate::table::table_column::MasterExtTableColumn; use crate::table::table_column::ProcessorBaseTableColumn; @@ -389,165 +389,71 @@ impl ProcessorTable { } } -impl ExtProcessorTable { - /// Instruction-specific transition constraints are combined with deselectors in such a way - /// that arbitrary sets of mutually exclusive combinations are summed, i.e., - /// - /// ```py - /// [ deselector_pop * tc_pop_0 + deselector_push * tc_push_0 + ..., - /// deselector_pop * tc_pop_1 + deselector_push * tc_push_1 + ..., - /// ..., - /// deselector_pop * tc_pop_i + deselector_push * tc_push_i + ..., - /// deselector_pop * 0 + deselector_push * tc_push_{i+1} + ..., - /// ..., - /// ] - /// ``` - /// For instructions that have fewer transition constraints than the maximal number of - /// transition constraints among all instructions, the deselector is multiplied with a zero, - /// causing no additional terms in the final sets of combined transition constraint polynomials. - fn combine_instruction_constraints_with_deselectors( - factory: &mut DualRowConstraints, - instr_tc_polys_tuples: [(Instruction, Vec>); - Instruction::COUNT], - ) -> Vec> { - let (all_instructions, all_tc_polys_for_all_instructions): (Vec<_>, Vec>) = - instr_tc_polys_tuples.into_iter().unzip(); - - let instruction_deselectors = InstructionDeselectors::new(factory); - - let all_instruction_deselectors = all_instructions - .into_iter() - .map(|instr| instruction_deselectors.get(instr)) - .collect_vec(); - - let max_number_of_constraints = all_tc_polys_for_all_instructions - .iter() - .map(|tc_polys_for_instr| tc_polys_for_instr.len()) - .max() - .unwrap(); - - let zero_poly = factory.zero(); - let all_tc_polys_for_all_instructions_transposed = (0..max_number_of_constraints) - .map(|idx| { - all_tc_polys_for_all_instructions - .iter() - .map(|tc_polys_for_instr| tc_polys_for_instr.get(idx).unwrap_or(&zero_poly)) - .collect_vec() - }) - .collect_vec(); - - all_tc_polys_for_all_instructions_transposed - .into_iter() - .map(|row| { - all_instruction_deselectors - .clone() - .into_iter() - .zip(row) - .map(|(deselector, instruction_tc)| deselector * instruction_tc.to_owned()) - .sum() - }) - .collect_vec() - } - - fn combine_transition_constraints_with_padding_constraints( - factory: &DualRowConstraints, - instruction_transition_constraints: Vec>, - ) -> Vec> { - let ip_remains = factory.ip_next() - factory.ip(); - let ci_remains = factory.ci_next() - factory.ci(); - let nia_remains = factory.nia_next() - factory.nia(); - let padding_row_transition_constraints = [ - vec![ip_remains, ci_remains, nia_remains], - factory.keep_jump_stack(), - factory.keep_stack(), - factory.keep_ram(), - ] - .concat(); - - let padding_row_deselector = factory.one() - factory.is_padding_next(); - let padding_row_selector = factory.is_padding_next(); - - let max_number_of_constraints = max( - instruction_transition_constraints.len(), - padding_row_transition_constraints.len(), - ); - - (0..max_number_of_constraints) - .map(|idx| { - let instruction_constraint = instruction_transition_constraints - .get(idx) - .unwrap_or(&factory.zero()) - .clone(); - let padding_constraint = padding_row_transition_constraints - .get(idx) - .unwrap_or(&factory.zero()) - .clone(); - - instruction_constraint * padding_row_deselector.clone() - + padding_constraint * padding_row_selector.clone() - }) - .collect_vec() - } -} - #[derive(Debug, Clone)] pub struct ExtProcessorTable {} impl ExtProcessorTable { - pub fn ext_initial_constraints_as_circuits() -> Vec> { - let factory = SingleRowConstraints::default(); - let constant = |c| factory.constant_from_i32(c); - let constant_x = |x| factory.constant_from_xfe(x); - let challenge = |c| factory.circuit_builder.challenge(c); - - let clk_is_0 = factory.clk(); - let ip_is_0 = factory.ip(); - let jsp_is_0 = factory.jsp(); - let jso_is_0 = factory.jso(); - let jsd_is_0 = factory.jsd(); - let st0_is_0 = factory.st0(); - let st1_is_0 = factory.st1(); - let st2_is_0 = factory.st2(); - let st3_is_0 = factory.st3(); - let st4_is_0 = factory.st4(); - let st5_is_0 = factory.st5(); - let st6_is_0 = factory.st6(); - let st7_is_0 = factory.st7(); - let st8_is_0 = factory.st8(); - let st9_is_0 = factory.st9(); - let st10_is_0 = factory.st10(); - let st11_is_0 = factory.st11(); - let st12_is_0 = factory.st12(); - let st13_is_0 = factory.st13(); - let st14_is_0 = factory.st14(); - let st15_is_0 = factory.st15(); - let osp_is_16 = factory.osp() - constant(16); - let osv_is_0 = factory.osv(); - let ramv_is_0 = factory.ramv(); - let ramp_is_0 = factory.ramp(); - let previous_instruction_is_0 = factory.previous_instruction(); + pub fn ext_initial_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let x_constant = |x| circuit_builder.x_constant(x); + let challenge = |c| circuit_builder.challenge(c); + let base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(BaseRow(col.master_base_table_index())) + }; + let ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(ExtRow(col.master_ext_table_index())) + }; + + let clk_is_0 = base_row(CLK); + let ip_is_0 = base_row(IP); + let jsp_is_0 = base_row(JSP); + let jso_is_0 = base_row(JSO); + let jsd_is_0 = base_row(JSD); + let st0_is_0 = base_row(ST0); + let st1_is_0 = base_row(ST1); + let st2_is_0 = base_row(ST2); + let st3_is_0 = base_row(ST3); + let st4_is_0 = base_row(ST4); + let st5_is_0 = base_row(ST5); + let st6_is_0 = base_row(ST6); + let st7_is_0 = base_row(ST7); + let st8_is_0 = base_row(ST8); + let st9_is_0 = base_row(ST9); + let st10_is_0 = base_row(ST10); + let st11_is_0 = base_row(ST11); + let st12_is_0 = base_row(ST12); + let st13_is_0 = base_row(ST13); + let st14_is_0 = base_row(ST14); + let st15_is_0 = base_row(ST15); + let osp_is_16 = base_row(OSP) - constant(16); + let osv_is_0 = base_row(OSV); + let ramv_is_0 = base_row(RAMV); + let ramp_is_0 = base_row(RAMP); + let previous_instruction_is_0 = base_row(PreviousInstruction); // Permutation and Evaluation Arguments with all tables the Processor Table relates to // standard input let running_evaluation_for_standard_input_is_initialized_correctly = - factory.running_evaluation_standard_input() - constant_x(EvalArg::default_initial()); + ext_row(InputTableEvalArg) - x_constant(EvalArg::default_initial()); // program table let instruction_lookup_indeterminate = challenge(InstructionLookupIndeterminate); let instruction_ci_weight = challenge(ProgramInstructionWeight); let instruction_nia_weight = challenge(ProgramNextInstructionWeight); let compressed_row_for_instruction_lookup = - instruction_ci_weight * factory.ci() + instruction_nia_weight * factory.nia(); - let instruction_lookup_log_derivative_is_initialized_correctly = (factory - .instruction_lookup_log_derivative() - - constant_x(LookupArg::default_initial())) - * (instruction_lookup_indeterminate - compressed_row_for_instruction_lookup) - - factory.one(); + instruction_ci_weight * base_row(CI) + instruction_nia_weight * base_row(NIA); + let instruction_lookup_log_derivative_is_initialized_correctly = + (ext_row(InstructionLookupClientLogDerivative) + - x_constant(LookupArg::default_initial())) + * (instruction_lookup_indeterminate - compressed_row_for_instruction_lookup) + - constant(1); // standard output let running_evaluation_for_standard_output_is_initialized_correctly = - factory.running_evaluation_standard_output() - constant_x(EvalArg::default_initial()); + ext_row(OutputTableEvalArg) - x_constant(EvalArg::default_initial()); // op-stack table let op_stack_indeterminate = challenge(OpStackIndeterminate); @@ -555,68 +461,65 @@ impl ExtProcessorTable { let op_stack_osp_weight = challenge(OpStackOspWeight); // note: `clk` and `osv` are already constrained to be 0, `osp` to be 16 let compressed_row_for_op_stack_table = - op_stack_ib1_weight * factory.ib1() + op_stack_osp_weight * constant(16); - let running_product_for_op_stack_table_is_initialized_correctly = factory - .running_product_op_stack_table() - - constant_x(PermArg::default_initial()) - * (op_stack_indeterminate - compressed_row_for_op_stack_table); + op_stack_ib1_weight * base_row(IB1) + op_stack_osp_weight * constant(16); + let running_product_for_op_stack_table_is_initialized_correctly = + ext_row(OpStackTablePermArg) + - x_constant(PermArg::default_initial()) + * (op_stack_indeterminate - compressed_row_for_op_stack_table); // ram table let ram_indeterminate = challenge(RamIndeterminate); // note: `clk`, `ramp`, and `ramv` are already constrained to be 0. let compressed_row_for_ram_table = constant(0); - let running_product_for_ram_table_is_initialized_correctly = factory - .running_product_ram_table() - - constant_x(PermArg::default_initial()) + let running_product_for_ram_table_is_initialized_correctly = ext_row(RamTablePermArg) + - x_constant(PermArg::default_initial()) * (ram_indeterminate - compressed_row_for_ram_table); // jump-stack table let jump_stack_indeterminate = challenge(JumpStackIndeterminate); let jump_stack_ci_weight = challenge(JumpStackCiWeight); // note: `clk`, `jsp`, `jso`, and `jsd` are already constrained to be 0. - let compressed_row_for_jump_stack_table = jump_stack_ci_weight * factory.ci(); - let running_product_for_jump_stack_table_is_initialized_correctly = factory - .running_product_jump_stack_table() - - constant_x(PermArg::default_initial()) - * (jump_stack_indeterminate - compressed_row_for_jump_stack_table); + let compressed_row_for_jump_stack_table = jump_stack_ci_weight * base_row(CI); + let running_product_for_jump_stack_table_is_initialized_correctly = + ext_row(JumpStackTablePermArg) + - x_constant(PermArg::default_initial()) + * (jump_stack_indeterminate - compressed_row_for_jump_stack_table); // clock jump difference lookup argument // A clock jump difference of 0 is illegal. Hence, the initial is recorded. - let clock_jump_diff_lookup_log_derivative_is_initialized_correctly = factory - .clock_jump_difference_lookup_server_log_derivative() - - constant_x(LookupArg::default_initial()); + let clock_jump_diff_lookup_log_derivative_is_initialized_correctly = + ext_row(ClockJumpDifferenceLookupServerLogDerivative) + - x_constant(LookupArg::default_initial()); // from processor to hash table - let hash_selector = factory.ci() - constant(Instruction::Hash.opcode() as i32); + let hash_selector = base_row(CI) - constant(Instruction::Hash.opcode()); let hash_deselector = - InstructionDeselectors::instruction_deselector_single_row(&factory, Instruction::Hash); + Self::instruction_deselector_single_row(circuit_builder, Instruction::Hash); let hash_input_indeterminate = challenge(HashInputIndeterminate); // the opStack is guaranteed to be initialized to 0 by virtue of other initial constraints let compressed_row = constant(0); - let running_evaluation_hash_input_has_absorbed_first_row = factory - .running_evaluation_hash_input() - - hash_input_indeterminate * constant_x(EvalArg::default_initial()) + let running_evaluation_hash_input_has_absorbed_first_row = ext_row(HashInputEvalArg) + - hash_input_indeterminate * x_constant(EvalArg::default_initial()) - compressed_row; let running_evaluation_hash_input_is_default_initial = - factory.running_evaluation_hash_input() - constant_x(EvalArg::default_initial()); + ext_row(HashInputEvalArg) - x_constant(EvalArg::default_initial()); let running_evaluation_hash_input_is_initialized_correctly = hash_selector * running_evaluation_hash_input_is_default_initial + hash_deselector * running_evaluation_hash_input_has_absorbed_first_row; // from hash table to processor let running_evaluation_hash_digest_is_initialized_correctly = - factory.running_evaluation_hash_digest() - constant_x(EvalArg::default_initial()); + ext_row(HashDigestEvalArg) - x_constant(EvalArg::default_initial()); // Hash Table – Sponge let running_evaluation_sponge_absorb_is_initialized_correctly = - factory.running_evaluation_sponge() - constant_x(EvalArg::default_initial()); + ext_row(SpongeEvalArg) - x_constant(EvalArg::default_initial()); // u32 table - let running_sum_log_derivative_for_u32_table_is_initialized_correctly = factory - .u32_table_running_sum_log_derivative() - - constant_x(LookupArg::default_initial()); + let running_sum_log_derivative_for_u32_table_is_initialized_correctly = + ext_row(U32LookupClientLogDerivative) - x_constant(LookupArg::default_initial()); - let mut constraints = [ + vec![ clk_is_0, ip_is_0, jsp_is_0, @@ -654,47 +557,47 @@ impl ExtProcessorTable { running_evaluation_hash_digest_is_initialized_correctly, running_evaluation_sponge_absorb_is_initialized_correctly, running_sum_log_derivative_for_u32_table_is_initialized_correctly, - ]; - - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_consistency_constraints_as_circuits() -> Vec> { - let factory = SingleRowConstraints::default(); - let one = factory.one(); - let constant = |c| factory.constant_from_i32(c); + pub fn ext_consistency_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(BaseRow(col.master_base_table_index())) + }; // The composition of instruction buckets ib0-ib7 corresponds the current instruction ci. - let ib_composition = one.clone() * factory.ib0() - + constant(1 << 1) * factory.ib1() - + constant(1 << 2) * factory.ib2() - + constant(1 << 3) * factory.ib3() - + constant(1 << 4) * factory.ib4() - + constant(1 << 5) * factory.ib5() - + constant(1 << 6) * factory.ib6() - + constant(1 << 7) * factory.ib7(); - let ci_corresponds_to_ib0_thru_ib7 = factory.ci() - ib_composition; - - let ib0_is_bit = factory.ib0() * (factory.ib0() - one.clone()); - let ib1_is_bit = factory.ib1() * (factory.ib1() - one.clone()); - let ib2_is_bit = factory.ib2() * (factory.ib2() - one.clone()); - let ib3_is_bit = factory.ib3() * (factory.ib3() - one.clone()); - let ib4_is_bit = factory.ib4() * (factory.ib4() - one.clone()); - let ib5_is_bit = factory.ib5() * (factory.ib5() - one.clone()); - let ib6_is_bit = factory.ib6() * (factory.ib6() - one.clone()); - let ib7_is_bit = factory.ib7() * (factory.ib7() - one.clone()); - let is_padding_is_bit = factory.is_padding() * (factory.is_padding() - one); + let ib_composition = base_row(IB0) + + constant(1 << 1) * base_row(IB1) + + constant(1 << 2) * base_row(IB2) + + constant(1 << 3) * base_row(IB3) + + constant(1 << 4) * base_row(IB4) + + constant(1 << 5) * base_row(IB5) + + constant(1 << 6) * base_row(IB6) + + constant(1 << 7) * base_row(IB7); + let ci_corresponds_to_ib0_thru_ib7 = base_row(CI) - ib_composition; + + let ib0_is_bit = base_row(IB0) * (base_row(IB0) - constant(1)); + let ib1_is_bit = base_row(IB1) * (base_row(IB1) - constant(1)); + let ib2_is_bit = base_row(IB2) * (base_row(IB2) - constant(1)); + let ib3_is_bit = base_row(IB3) * (base_row(IB3) - constant(1)); + let ib4_is_bit = base_row(IB4) * (base_row(IB4) - constant(1)); + let ib5_is_bit = base_row(IB5) * (base_row(IB5) - constant(1)); + let ib6_is_bit = base_row(IB6) * (base_row(IB6) - constant(1)); + let ib7_is_bit = base_row(IB7) * (base_row(IB7) - constant(1)); + let is_padding_is_bit = base_row(IsPadding) * (base_row(IsPadding) - constant(1)); // In padding rows, the clock jump difference lookup multiplicity is 0. The one row // exempt from this rule is the row wth CLK == 1: since the memory-like tables don't have // an “awareness” of padding rows, they keep looking up clock jump differences of // magnitude 1. - let clock_jump_diff_lookup_multiplicity_is_0_in_padding_rows = factory.is_padding() - * (factory.clk() - factory.one()) - * factory.clock_jump_difference_lookup_multiplicity(); + let clock_jump_diff_lookup_multiplicity_is_0_in_padding_rows = base_row(IsPadding) + * (base_row(CLK) - constant(1)) + * base_row(ClockJumpDifferenceLookupMultiplicity); - let mut constraints = [ + vec![ ib0_is_bit, ib1_is_bit, ib2_is_bit, @@ -706,588 +609,784 @@ impl ExtProcessorTable { is_padding_is_bit, ci_corresponds_to_ib0_thru_ib7, clock_jump_diff_lookup_multiplicity_is_0_in_padding_rows, - ]; - - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() + ] } - pub fn ext_transition_constraints_as_circuits() -> Vec> { - let mut factory = DualRowConstraints::default(); - - // instruction-specific constraints - let all_instruction_transition_constraints: [_; Instruction::COUNT] = [ - (Pop, factory.instruction_pop()), - (Push(Default::default()), factory.instruction_push()), - (Divine(Default::default()), factory.instruction_divine()), - (Dup(Default::default()), factory.instruction_dup()), - (Swap(Default::default()), factory.instruction_swap()), - (Nop, factory.instruction_nop()), - (Skiz, factory.instruction_skiz()), - (Call(Default::default()), factory.instruction_call()), - (Return, factory.instruction_return()), - (Recurse, factory.instruction_recurse()), - (Assert, factory.instruction_assert()), - (Halt, factory.instruction_halt()), - (ReadMem, factory.instruction_read_mem()), - (WriteMem, factory.instruction_write_mem()), - (Hash, factory.instruction_hash()), - (DivineSibling, factory.instruction_divine_sibling()), - (AssertVector, factory.instruction_assert_vector()), - (AbsorbInit, factory.instruction_absorb_init()), - (Absorb, factory.instruction_absorb()), - (Squeeze, factory.instruction_squeeze()), - (Add, factory.instruction_add()), - (Mul, factory.instruction_mul()), - (Invert, factory.instruction_invert()), - (Eq, factory.instruction_eq()), - (Split, factory.instruction_split()), - (Lt, factory.instruction_lt()), - (And, factory.instruction_and()), - (Xor, factory.instruction_xor()), - (Log2Floor, factory.instruction_log_2_floor()), - (Pow, factory.instruction_pow()), - (Div, factory.instruction_div()), - (PopCount, factory.instruction_pop_count()), - (XxAdd, factory.instruction_xxadd()), - (XxMul, factory.instruction_xxmul()), - (XInvert, factory.instruction_xinv()), - (XbMul, factory.instruction_xbmul()), - (ReadIo, factory.instruction_read_io()), - (WriteIo, factory.instruction_write_io()), - ]; + fn indicator_polynomial( + circuit_builder: &ConstraintCircuitBuilder, + i: usize, + ) -> ConstraintCircuitMonad { + let one = || circuit_builder.b_constant(1_u32.into()); + let hv = |idx| match idx { + 0 => circuit_builder.input(CurrentBaseRow(HV0.master_base_table_index())), + 1 => circuit_builder.input(CurrentBaseRow(HV1.master_base_table_index())), + 2 => circuit_builder.input(CurrentBaseRow(HV2.master_base_table_index())), + 3 => circuit_builder.input(CurrentBaseRow(HV3.master_base_table_index())), + j => panic!("Index for helper variable must be in range 0..4, got {j}."), + }; - let mut transition_constraints = Self::combine_instruction_constraints_with_deselectors( - &mut factory, - all_instruction_transition_constraints, - ); + match i { + 0 => (one() - hv(3)) * (one() - hv(2)) * (one() - hv(1)) * (one() - hv(0)), + 1 => (one() - hv(3)) * (one() - hv(2)) * (one() - hv(1)) * hv(0), + 2 => (one() - hv(3)) * (one() - hv(2)) * hv(1) * (one() - hv(0)), + 3 => (one() - hv(3)) * (one() - hv(2)) * hv(1) * hv(0), + 4 => (one() - hv(3)) * hv(2) * (one() - hv(1)) * (one() - hv(0)), + 5 => (one() - hv(3)) * hv(2) * (one() - hv(1)) * hv(0), + 6 => (one() - hv(3)) * hv(2) * hv(1) * (one() - hv(0)), + 7 => (one() - hv(3)) * hv(2) * hv(1) * hv(0), + 8 => hv(3) * (one() - hv(2)) * (one() - hv(1)) * (one() - hv(0)), + 9 => hv(3) * (one() - hv(2)) * (one() - hv(1)) * hv(0), + 10 => hv(3) * (one() - hv(2)) * hv(1) * (one() - hv(0)), + 11 => hv(3) * (one() - hv(2)) * hv(1) * hv(0), + 12 => hv(3) * hv(2) * (one() - hv(1)) * (one() - hv(0)), + 13 => hv(3) * hv(2) * (one() - hv(1)) * hv(0), + 14 => hv(3) * hv(2) * hv(1) * (one() - hv(0)), + 15 => hv(3) * hv(2) * hv(1) * hv(0), + _ => panic!("No indicator polynomial with index {i} exists: there are only 16."), + } + } - // if next row is padding row: disable transition constraints, enable padding constraints - transition_constraints = Self::combine_transition_constraints_with_padding_constraints( - &factory, - transition_constraints, - ); + /// Instruction-specific transition constraints are combined with deselectors in such a way + /// that arbitrary sets of mutually exclusive combinations are summed, i.e., + /// + /// ```py + /// [ deselector_pop * tc_pop_0 + deselector_push * tc_push_0 + ..., + /// deselector_pop * tc_pop_1 + deselector_push * tc_push_1 + ..., + /// ..., + /// deselector_pop * tc_pop_i + deselector_push * tc_push_i + ..., + /// deselector_pop * 0 + deselector_push * tc_push_{i+1} + ..., + /// ..., + /// ] + /// ``` + /// For instructions that have fewer transition constraints than the maximal number of + /// transition constraints among all instructions, the deselector is multiplied with a zero, + /// causing no additional terms in the final sets of combined transition constraint polynomials. + fn combine_instruction_constraints_with_deselectors( + circuit_builder: &ConstraintCircuitBuilder, + instr_tc_polys_tuples: [(Instruction, Vec>); + Instruction::COUNT], + ) -> Vec> { + let (all_instructions, all_tc_polys_for_all_instructions): (Vec<_>, Vec<_>) = + instr_tc_polys_tuples.into_iter().unzip(); - // constraints common to all instructions - transition_constraints.insert(0, factory.clk_always_increases_by_one()); - transition_constraints.insert(1, factory.is_padding_is_zero_or_does_not_change()); - transition_constraints.insert(2, factory.previous_instruction_is_copied_correctly()); - - // constraints related to clock jump difference Lookup Argument - let clock_jump_difference_lookup_indeterminate = factory - .circuit_builder - .challenge(ClockJumpDifferenceLookupIndeterminate); - let log_derivative_accumulates_clk_next = (factory - .clock_jump_difference_lookup_server_log_derivative_next() - - factory.clock_jump_difference_lookup_server_log_derivative()) - * (clock_jump_difference_lookup_indeterminate - factory.clk_next()) - - factory.clock_jump_difference_lookup_multiplicity_next(); - - transition_constraints.push(log_derivative_accumulates_clk_next); + let all_instruction_deselectors = all_instructions + .into_iter() + .map(|instr| Self::instruction_deselector_current_row(circuit_builder, instr)) + .collect_vec(); - // constraints related to evaluation and permutation arguments + let max_number_of_constraints = all_tc_polys_for_all_instructions + .iter() + .map(|tc_polys_for_instr| tc_polys_for_instr.len()) + .max() + .unwrap(); - transition_constraints - .push(factory.running_evaluation_for_standard_input_updates_correctly()); - transition_constraints - .push(factory.log_derivative_for_instruction_lookup_updates_correctly()); - transition_constraints - .push(factory.running_evaluation_for_standard_output_updates_correctly()); - transition_constraints.push(factory.running_product_for_op_stack_table_updates_correctly()); - transition_constraints.push(factory.running_product_for_ram_table_updates_correctly()); - transition_constraints - .push(factory.running_product_for_jump_stack_table_updates_correctly()); - transition_constraints.push(factory.running_evaluation_hash_input_updates_correctly()); - transition_constraints.push(factory.running_evaluation_hash_digest_updates_correctly()); - transition_constraints.push(factory.running_evaluation_sponge_updates_correctly()); - transition_constraints.push(factory.log_derivative_with_u32_table_updates_correctly()); + let zero_poly = circuit_builder.b_constant(0_u32.into()); + let all_tc_polys_for_all_instructions_transposed = (0..max_number_of_constraints) + .map(|idx| { + all_tc_polys_for_all_instructions + .iter() + .map(|tc_polys_for_instr| tc_polys_for_instr.get(idx).unwrap_or(&zero_poly)) + .collect_vec() + }) + .collect_vec(); - ConstraintCircuitMonad::constant_folding(&mut transition_constraints); - transition_constraints + all_tc_polys_for_all_instructions_transposed .into_iter() - .map(|tc_ref| tc_ref.consume()) + .map(|row| { + all_instruction_deselectors + .clone() + .into_iter() + .zip(row) + .map(|(deselector, instruction_tc)| deselector * instruction_tc.to_owned()) + .sum() + }) .collect_vec() } - pub fn ext_terminal_constraints_as_circuits() -> Vec> { - let factory = SingleRowConstraints::default(); + fn combine_transition_constraints_with_padding_constraints( + circuit_builder: &ConstraintCircuitBuilder, + instruction_transition_constraints: Vec>, + ) -> Vec> { + let constant = |c: u64| circuit_builder.b_constant(c.into()); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; - // In the last row, current instruction register ci is 0, corresponding to instruction halt. - let last_ci_is_halt = factory.ci(); + let padding_row_transition_constraints = [ + vec![ + next_base_row(IP) - curr_base_row(IP), + next_base_row(CI) - curr_base_row(CI), + next_base_row(NIA) - curr_base_row(NIA), + ], + Self::instruction_group_keep_jump_stack(circuit_builder), + Self::instruction_group_keep_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), + ] + .concat(); - let mut constraints = [last_ci_is_halt]; + let padding_row_deselector = constant(1) - next_base_row(IsPadding); + let padding_row_selector = next_base_row(IsPadding); - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints.map(|circuit| circuit.consume()).to_vec() - } -} + let max_number_of_constraints = max( + instruction_transition_constraints.len(), + padding_row_transition_constraints.len(), + ); -#[derive(Debug, Clone)] -pub struct SingleRowConstraints { - base_row_variables: [ConstraintCircuitMonad; NUM_BASE_COLUMNS], - ext_row_variables: [ConstraintCircuitMonad; NUM_EXT_COLUMNS], - circuit_builder: ConstraintCircuitBuilder, - one: ConstraintCircuitMonad, - two: ConstraintCircuitMonad, -} + (0..max_number_of_constraints) + .map(|idx| { + let instruction_constraint = instruction_transition_constraints + .get(idx) + .unwrap_or(&constant(0)) + .to_owned(); + let padding_constraint = padding_row_transition_constraints + .get(idx) + .unwrap_or(&constant(0)) + .to_owned(); -impl Default for SingleRowConstraints { - fn default() -> Self { - let circuit_builder = ConstraintCircuitBuilder::new(); - let base_row_variables = (0..NUM_BASE_COLUMNS) - .map(|i| circuit_builder.input(SingleRowIndicator::BaseRow(i))) - .collect_vec() - .try_into() - .expect("Create variables for single base row constraints"); - let ext_row_variables = (0..NUM_EXT_COLUMNS) - .map(|i| circuit_builder.input(SingleRowIndicator::ExtRow(i))) + instruction_constraint * padding_row_deselector.clone() + + padding_constraint * padding_row_selector.clone() + }) .collect_vec() - .try_into() - .expect("Create variables for single ext row constraints"); + } - let one = circuit_builder.b_constant(1u32.into()); - let two = circuit_builder.b_constant(2u32.into()); + fn instruction_group_decompose_arg( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; - Self { - base_row_variables, - ext_row_variables, - circuit_builder, - one, - two, - } + let hv0_is_a_bit = curr_base_row(HV0) * (curr_base_row(HV0) - constant(1)); + let hv1_is_a_bit = curr_base_row(HV1) * (curr_base_row(HV1) - constant(1)); + let hv2_is_a_bit = curr_base_row(HV2) * (curr_base_row(HV2) - constant(1)); + let hv3_is_a_bit = curr_base_row(HV3) * (curr_base_row(HV3) - constant(1)); + + let helper_variables_are_binary_decomposition_of_nia = curr_base_row(NIA) + - constant(8) * curr_base_row(HV3) + - constant(4) * curr_base_row(HV2) + - constant(2) * curr_base_row(HV1) + - curr_base_row(HV0); + + vec![ + hv0_is_a_bit, + hv1_is_a_bit, + hv2_is_a_bit, + hv3_is_a_bit, + helper_variables_are_binary_decomposition_of_nia, + ] } -} -impl SingleRowConstraints { - pub fn constant_from_xfe( - &self, - constant: XFieldElement, - ) -> ConstraintCircuitMonad { - self.circuit_builder.x_constant(constant) + fn instruction_group_keep_ram( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + vec![ + next_base_row(RAMV) - curr_base_row(RAMV), + next_base_row(RAMP) - curr_base_row(RAMP), + ] } - pub fn constant_from_i32(&self, constant: i32) -> ConstraintCircuitMonad { - let bfe = if constant < 0 { - BFieldElement::new(BFieldElement::P - ((-constant) as u64)) - } else { - BFieldElement::new(constant as u64) + + fn instruction_group_op_stack_remains_and_top_eleven_elements_unconstrained( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) }; - self.circuit_builder.b_constant(bfe) + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + vec![ + next_base_row(ST11) - curr_base_row(ST11), + next_base_row(ST12) - curr_base_row(ST12), + next_base_row(ST13) - curr_base_row(ST13), + next_base_row(ST14) - curr_base_row(ST14), + next_base_row(ST15) - curr_base_row(ST15), + // The top of the OpStack underflow, i.e., osv, does not change. + next_base_row(OSV) - curr_base_row(OSV), + // The OpStack pointer, osp, does not change. + next_base_row(OSP) - curr_base_row(OSP), + ] } - pub fn one(&self) -> ConstraintCircuitMonad { - self.one.clone() + fn instruction_group_op_stack_remains_and_top_ten_elements_unconstrained( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let specific_constraints = vec![next_base_row(ST10) - curr_base_row(ST10)]; + let inherited_constraints = + Self::instruction_group_op_stack_remains_and_top_eleven_elements_unconstrained( + circuit_builder, + ); + + [specific_constraints, inherited_constraints].concat() } - pub fn two(&self) -> ConstraintCircuitMonad { - self.two.clone() + + fn instruction_group_op_stack_remains_and_top_three_elements_unconstrained( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let specific_constraints = vec![ + next_base_row(ST3) - curr_base_row(ST3), + next_base_row(ST4) - curr_base_row(ST4), + next_base_row(ST5) - curr_base_row(ST5), + next_base_row(ST6) - curr_base_row(ST6), + next_base_row(ST7) - curr_base_row(ST7), + next_base_row(ST8) - curr_base_row(ST8), + next_base_row(ST9) - curr_base_row(ST9), + ]; + let inherited_constraints = + Self::instruction_group_op_stack_remains_and_top_ten_elements_unconstrained( + circuit_builder, + ); + + [specific_constraints, inherited_constraints].concat() } - pub fn clk(&self) -> ConstraintCircuitMonad { - self.base_row_variables[CLK.master_base_table_index()].clone() + + fn instruction_group_unop( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let specific_constraints = vec![ + next_base_row(ST1) - curr_base_row(ST1), + next_base_row(ST2) - curr_base_row(ST2), + ]; + let inherited_constraints = + Self::instruction_group_op_stack_remains_and_top_three_elements_unconstrained( + circuit_builder, + ); + + [specific_constraints, inherited_constraints].concat() } - pub fn is_padding(&self) -> ConstraintCircuitMonad { - self.base_row_variables[IsPadding.master_base_table_index()].clone() - } - pub fn ip(&self) -> ConstraintCircuitMonad { - self.base_row_variables[IP.master_base_table_index()].clone() - } - pub fn ci(&self) -> ConstraintCircuitMonad { - self.base_row_variables[CI.master_base_table_index()].clone() - } - pub fn nia(&self) -> ConstraintCircuitMonad { - self.base_row_variables[NIA.master_base_table_index()].clone() - } - pub fn ib0(&self) -> ConstraintCircuitMonad { - self.base_row_variables[IB0.master_base_table_index()].clone() - } - pub fn ib1(&self) -> ConstraintCircuitMonad { - self.base_row_variables[IB1.master_base_table_index()].clone() - } - pub fn ib2(&self) -> ConstraintCircuitMonad { - self.base_row_variables[IB2.master_base_table_index()].clone() - } - pub fn ib3(&self) -> ConstraintCircuitMonad { - self.base_row_variables[IB3.master_base_table_index()].clone() - } - pub fn ib4(&self) -> ConstraintCircuitMonad { - self.base_row_variables[IB4.master_base_table_index()].clone() - } - pub fn ib5(&self) -> ConstraintCircuitMonad { - self.base_row_variables[IB5.master_base_table_index()].clone() - } - pub fn ib6(&self) -> ConstraintCircuitMonad { - self.base_row_variables[IB6.master_base_table_index()].clone() - } - pub fn ib7(&self) -> ConstraintCircuitMonad { - self.base_row_variables[IB7.master_base_table_index()].clone() - } - pub fn jsp(&self) -> ConstraintCircuitMonad { - self.base_row_variables[JSP.master_base_table_index()].clone() - } - pub fn jsd(&self) -> ConstraintCircuitMonad { - self.base_row_variables[JSD.master_base_table_index()].clone() - } - pub fn jso(&self) -> ConstraintCircuitMonad { - self.base_row_variables[JSO.master_base_table_index()].clone() - } - pub fn st0(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST0.master_base_table_index()].clone() - } - pub fn st1(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST1.master_base_table_index()].clone() - } - pub fn st2(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST2.master_base_table_index()].clone() - } - pub fn st3(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST3.master_base_table_index()].clone() - } - pub fn st4(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST4.master_base_table_index()].clone() - } - pub fn st5(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST5.master_base_table_index()].clone() - } - pub fn st6(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST6.master_base_table_index()].clone() - } - pub fn st7(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST7.master_base_table_index()].clone() - } - pub fn st8(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST8.master_base_table_index()].clone() - } - pub fn st9(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST9.master_base_table_index()].clone() - } - pub fn st10(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST10.master_base_table_index()].clone() - } - pub fn st11(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST11.master_base_table_index()].clone() - } - pub fn st12(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST12.master_base_table_index()].clone() - } - pub fn st13(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST13.master_base_table_index()].clone() - } - pub fn st14(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST14.master_base_table_index()].clone() - } - pub fn st15(&self) -> ConstraintCircuitMonad { - self.base_row_variables[ST15.master_base_table_index()].clone() - } - pub fn osp(&self) -> ConstraintCircuitMonad { - self.base_row_variables[OSP.master_base_table_index()].clone() - } - pub fn osv(&self) -> ConstraintCircuitMonad { - self.base_row_variables[OSV.master_base_table_index()].clone() - } - pub fn hv0(&self) -> ConstraintCircuitMonad { - self.base_row_variables[HV0.master_base_table_index()].clone() - } - pub fn hv1(&self) -> ConstraintCircuitMonad { - self.base_row_variables[HV1.master_base_table_index()].clone() - } - pub fn hv2(&self) -> ConstraintCircuitMonad { - self.base_row_variables[HV2.master_base_table_index()].clone() - } - pub fn hv3(&self) -> ConstraintCircuitMonad { - self.base_row_variables[HV3.master_base_table_index()].clone() - } - pub fn ramv(&self) -> ConstraintCircuitMonad { - self.base_row_variables[RAMV.master_base_table_index()].clone() - } - pub fn ramp(&self) -> ConstraintCircuitMonad { - self.base_row_variables[RAMP.master_base_table_index()].clone() - } - pub fn clock_jump_difference_lookup_multiplicity( - &self, - ) -> ConstraintCircuitMonad { - self.base_row_variables[ClockJumpDifferenceLookupMultiplicity.master_base_table_index()] - .clone() - } - pub fn previous_instruction(&self) -> ConstraintCircuitMonad { - self.base_row_variables[PreviousInstruction.master_base_table_index()].clone() + + fn instruction_group_keep_op_stack( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let specific_constraints = vec![next_base_row(ST0) - curr_base_row(ST0)]; + let inherited_constraints = Self::instruction_group_unop(circuit_builder); + + [specific_constraints, inherited_constraints].concat() } - pub fn running_evaluation_standard_input(&self) -> ConstraintCircuitMonad { - self.ext_row_variables[InputTableEvalArg.master_ext_table_index()].clone() - } - pub fn running_evaluation_standard_output(&self) -> ConstraintCircuitMonad { - self.ext_row_variables[OutputTableEvalArg.master_ext_table_index()].clone() - } - pub fn instruction_lookup_log_derivative(&self) -> ConstraintCircuitMonad { - self.ext_row_variables[InstructionLookupClientLogDerivative.master_ext_table_index()] - .clone() - } - pub fn running_product_op_stack_table(&self) -> ConstraintCircuitMonad { - self.ext_row_variables[OpStackTablePermArg.master_ext_table_index()].clone() - } - pub fn running_product_ram_table(&self) -> ConstraintCircuitMonad { - self.ext_row_variables[RamTablePermArg.master_ext_table_index()].clone() - } - pub fn running_product_jump_stack_table(&self) -> ConstraintCircuitMonad { - self.ext_row_variables[JumpStackTablePermArg.master_ext_table_index()].clone() + fn instruction_group_grow_op_stack_and_top_two_elements_unconstrained( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + vec![ + // The stack element in st1 is moved into st2. + next_base_row(ST2) - curr_base_row(ST1), + // And so on... + next_base_row(ST3) - curr_base_row(ST2), + next_base_row(ST4) - curr_base_row(ST3), + next_base_row(ST5) - curr_base_row(ST4), + next_base_row(ST6) - curr_base_row(ST5), + next_base_row(ST7) - curr_base_row(ST6), + next_base_row(ST8) - curr_base_row(ST7), + next_base_row(ST9) - curr_base_row(ST8), + next_base_row(ST10) - curr_base_row(ST9), + next_base_row(ST11) - curr_base_row(ST10), + next_base_row(ST12) - curr_base_row(ST11), + next_base_row(ST13) - curr_base_row(ST12), + next_base_row(ST14) - curr_base_row(ST13), + next_base_row(ST15) - curr_base_row(ST14), + // The stack element in st15 is moved to the top of OpStack underflow, i.e., osv. + next_base_row(OSV) - curr_base_row(ST15), + // The OpStack pointer is incremented by 1. + next_base_row(OSP) - (curr_base_row(OSP) + constant(1)), + ] } - pub fn clock_jump_difference_lookup_server_log_derivative( - &self, - ) -> ConstraintCircuitMonad { - self.ext_row_variables - [ClockJumpDifferenceLookupServerLogDerivative.master_ext_table_index()] - .clone() + + fn instruction_group_grow_op_stack( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let specific_constraints = vec![next_base_row(ST1) - curr_base_row(ST0)]; + let inherited_constraints = + Self::instruction_group_grow_op_stack_and_top_two_elements_unconstrained( + circuit_builder, + ); + + [specific_constraints, inherited_constraints].concat() } - pub fn running_evaluation_hash_input(&self) -> ConstraintCircuitMonad { - self.ext_row_variables[HashInputEvalArg.master_ext_table_index()].clone() + + fn instruction_group_op_stack_shrinks_and_top_three_elements_unconstrained( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + vec![ + // The stack element in st4 is moved into st3. + next_base_row(ST3) - curr_base_row(ST4), + // The stack element in st5 is moved into st4. + next_base_row(ST4) - curr_base_row(ST5), + // And so on... + next_base_row(ST5) - curr_base_row(ST6), + next_base_row(ST6) - curr_base_row(ST7), + next_base_row(ST7) - curr_base_row(ST8), + next_base_row(ST8) - curr_base_row(ST9), + next_base_row(ST9) - curr_base_row(ST10), + next_base_row(ST10) - curr_base_row(ST11), + next_base_row(ST11) - curr_base_row(ST12), + next_base_row(ST12) - curr_base_row(ST13), + next_base_row(ST13) - curr_base_row(ST14), + next_base_row(ST14) - curr_base_row(ST15), + // The stack element at the top of OpStack underflow, i.e., osv, is moved into st15. + next_base_row(ST15) - curr_base_row(OSV), + // The OpStack pointer, osp, is decremented by 1. + next_base_row(OSP) - (curr_base_row(OSP) - constant(1)), + // The helper variable register hv3 holds the inverse of (osp - 16). + (curr_base_row(OSP) - constant(16)) * curr_base_row(HV3) - constant(1), + ] } - pub fn running_evaluation_hash_digest(&self) -> ConstraintCircuitMonad { - self.ext_row_variables[HashDigestEvalArg.master_ext_table_index()].clone() + + fn instruction_group_binop( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let specific_constraints = vec![ + next_base_row(ST1) - curr_base_row(ST2), + next_base_row(ST2) - curr_base_row(ST3), + ]; + let inherited_constraints = + Self::instruction_group_op_stack_shrinks_and_top_three_elements_unconstrained( + circuit_builder, + ); + + [specific_constraints, inherited_constraints].concat() } - pub fn running_evaluation_sponge(&self) -> ConstraintCircuitMonad { - self.ext_row_variables[SpongeEvalArg.master_ext_table_index()].clone() + + fn instruction_group_shrink_op_stack( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let specific_constraints = vec![next_base_row(ST0) - curr_base_row(ST1)]; + let inherited_constraints = Self::instruction_group_binop(circuit_builder); + + [specific_constraints, inherited_constraints].concat() } - pub fn u32_table_running_sum_log_derivative( - &self, - ) -> ConstraintCircuitMonad { - self.ext_row_variables[U32LookupClientLogDerivative.master_ext_table_index()].clone() + + fn instruction_group_keep_jump_stack( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let jsp_does_not_change = next_base_row(JSP) - curr_base_row(JSP); + let jso_does_not_change = next_base_row(JSO) - curr_base_row(JSO); + let jsd_does_not_change = next_base_row(JSD) - curr_base_row(JSD); + + vec![ + jsp_does_not_change, + jso_does_not_change, + jsd_does_not_change, + ] } -} -#[derive(Debug, Clone)] -pub struct DualRowConstraints { - current_base_row_variables: [ConstraintCircuitMonad; NUM_BASE_COLUMNS], - current_ext_row_variables: [ConstraintCircuitMonad; NUM_EXT_COLUMNS], - next_base_row_variables: [ConstraintCircuitMonad; NUM_BASE_COLUMNS], - next_ext_row_variables: [ConstraintCircuitMonad; NUM_EXT_COLUMNS], - circuit_builder: ConstraintCircuitBuilder, - zero: ConstraintCircuitMonad, - one: ConstraintCircuitMonad, - two: ConstraintCircuitMonad, -} + fn instruction_group_step_1( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; -impl Default for DualRowConstraints { - fn default() -> Self { - let circuit_builder = ConstraintCircuitBuilder::new(); - let current_base_row_variables = (0..NUM_BASE_COLUMNS) - .map(|i| circuit_builder.input(DualRowIndicator::CurrentBaseRow(i))) - .collect_vec() - .try_into() - .expect("Create variables for dual rows – current base row"); - let current_ext_row_variables = (0..NUM_EXT_COLUMNS) - .map(|i| circuit_builder.input(DualRowIndicator::CurrentExtRow(i))) - .collect_vec() - .try_into() - .expect("Create variables for dual rows – current ext row"); - let next_base_row_variables = (0..NUM_BASE_COLUMNS) - .map(|i| circuit_builder.input(DualRowIndicator::NextBaseRow(i))) - .collect_vec() - .try_into() - .expect("Create variables for dual rows – next base row"); - let next_ext_row_variables = (0..NUM_EXT_COLUMNS) - .map(|i| circuit_builder.input(DualRowIndicator::NextExtRow(i))) - .collect_vec() - .try_into() - .expect("Create variables for dual rows – next ext row"); + let instruction_pointer_increases_by_one = + next_base_row(IP) - curr_base_row(IP) - constant(1); + [ + Self::instruction_group_keep_jump_stack(circuit_builder), + vec![instruction_pointer_increases_by_one], + ] + .concat() + } - let zero = circuit_builder.b_constant(0u32.into()); - let one = circuit_builder.b_constant(1u32.into()); - let two = circuit_builder.b_constant(2u32.into()); + fn instruction_group_step_2( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; - Self { - current_base_row_variables, - current_ext_row_variables, - next_base_row_variables, - next_ext_row_variables, - circuit_builder, - zero, - one, - two, - } + let instruction_pointer_increases_by_two = + next_base_row(IP) - curr_base_row(IP) - constant(2); + [ + Self::instruction_group_keep_jump_stack(circuit_builder), + vec![instruction_pointer_increases_by_two], + ] + .concat() } -} -impl DualRowConstraints { - /// ## The cycle counter (`clk`) always increases by one - /// - /// $$ - /// p(..., clk, clk_next, ...) = clk_next - clk - 1 - /// $$ - /// - /// In general, for all $clk = a$, and $clk_next = a + 1$, - /// - /// $$ - /// p(..., a, a+1, ...) = (a+1) - a - 1 = a + 1 - a - 1 = a - a + 1 - 1 = 0 - /// $$ - /// - /// So the `clk_increase_by_one` base transition constraint polynomial holds exactly - /// when every `clk` register $a$ is one less than `clk` register $a + 1$. - pub fn clk_always_increases_by_one(&self) -> ConstraintCircuitMonad { - let one = self.one(); - let clk = self.clk(); - let clk_next = self.clk_next(); + /// Internal helper function to de-duplicate functionality common between the similar (but + /// different on a type level) functions for construction deselectors. + fn instruction_deselector_common_functionality( + circuit_builder: &ConstraintCircuitBuilder, + instruction: Instruction, + instruction_bucket_polynomials: [ConstraintCircuitMonad; Ord8::COUNT], + ) -> ConstraintCircuitMonad { + let one = circuit_builder.b_constant(1_u32.into()); + + let selector_bits: [_; Ord8::COUNT] = [ + instruction.ib(Ord8::IB0), + instruction.ib(Ord8::IB1), + instruction.ib(Ord8::IB2), + instruction.ib(Ord8::IB3), + instruction.ib(Ord8::IB4), + instruction.ib(Ord8::IB5), + instruction.ib(Ord8::IB6), + instruction.ib(Ord8::IB7), + ]; + let deselector_polynomials = + selector_bits.map(|b| one.clone() - circuit_builder.b_constant(b)); - clk_next - clk - one + instruction_bucket_polynomials + .into_iter() + .zip_eq(deselector_polynomials.into_iter()) + .map(|(bucket_poly, deselector_poly)| bucket_poly - deselector_poly) + .fold(one, ConstraintCircuitMonad::mul) } - pub fn is_padding_is_zero_or_does_not_change( - &self, + /// A polynomial that has no solutions when `ci` is `instruction`. + /// The number of variables in the polynomial corresponds to two rows. + fn instruction_deselector_current_row( + circuit_builder: &ConstraintCircuitBuilder, + instruction: Instruction, ) -> ConstraintCircuitMonad { - self.is_padding() * (self.is_padding_next() - self.is_padding()) + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + + let instruction_bucket_polynomials = [ + curr_base_row(IB0), + curr_base_row(IB1), + curr_base_row(IB2), + curr_base_row(IB3), + curr_base_row(IB4), + curr_base_row(IB5), + curr_base_row(IB6), + curr_base_row(IB7), + ]; + + Self::instruction_deselector_common_functionality( + circuit_builder, + instruction, + instruction_bucket_polynomials, + ) } - pub fn previous_instruction_is_copied_correctly( - &self, + /// A polynomial that has no solutions when `ci_next` is `instruction`. + /// The number of variables in the polynomial corresponds to two rows. + fn instruction_deselector_next_row( + circuit_builder: &ConstraintCircuitBuilder, + instruction: Instruction, ) -> ConstraintCircuitMonad { - (self.previous_instruction_next() - self.ci()) * (self.one() - self.is_padding_next()) + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let instruction_bucket_polynomials = [ + next_base_row(IB0), + next_base_row(IB1), + next_base_row(IB2), + next_base_row(IB3), + next_base_row(IB4), + next_base_row(IB5), + next_base_row(IB6), + next_base_row(IB7), + ]; + + Self::instruction_deselector_common_functionality( + circuit_builder, + instruction, + instruction_bucket_polynomials, + ) } - pub fn indicator_polynomial(&self, i: usize) -> ConstraintCircuitMonad { - let hv0 = self.hv0(); - let hv1 = self.hv1(); - let hv2 = self.hv2(); - let hv3 = self.hv3(); + /// A polynomial that has no solutions when `ci` is `instruction`. + /// The number of variables in the polynomial corresponds to a single row. + fn instruction_deselector_single_row( + circuit_builder: &ConstraintCircuitBuilder, + instruction: Instruction, + ) -> ConstraintCircuitMonad { + let base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(BaseRow(col.master_base_table_index())) + }; + + let instruction_bucket_polynomials = [ + base_row(IB0), + base_row(IB1), + base_row(IB2), + base_row(IB3), + base_row(IB4), + base_row(IB5), + base_row(IB6), + base_row(IB7), + ]; - match i { - 0 => (self.one() - hv3) * (self.one() - hv2) * (self.one() - hv1) * (self.one() - hv0), - 1 => (self.one() - hv3) * (self.one() - hv2) * (self.one() - hv1) * hv0, - 2 => (self.one() - hv3) * (self.one() - hv2) * hv1 * (self.one() - hv0), - 3 => (self.one() - hv3) * (self.one() - hv2) * hv1 * hv0, - 4 => (self.one() - hv3) * hv2 * (self.one() - hv1) * (self.one() - hv0), - 5 => (self.one() - hv3) * hv2 * (self.one() - hv1) * hv0, - 6 => (self.one() - hv3) * hv2 * hv1 * (self.one() - hv0), - 7 => (self.one() - hv3) * hv2 * hv1 * hv0, - 8 => hv3 * (self.one() - hv2) * (self.one() - hv1) * (self.one() - hv0), - 9 => hv3 * (self.one() - hv2) * (self.one() - hv1) * hv0, - 10 => hv3 * (self.one() - hv2) * hv1 * (self.one() - hv0), - 11 => hv3 * (self.one() - hv2) * hv1 * hv0, - 12 => hv3 * hv2 * (self.one() - hv1) * (self.one() - hv0), - 13 => hv3 * hv2 * (self.one() - hv1) * hv0, - 14 => hv3 * hv2 * hv1 * (self.one() - hv0), - 15 => hv3 * hv2 * hv1 * hv0, - _ => panic!("No indicator polynomial with index {i} exists: there are only 16."), - } + Self::instruction_deselector_common_functionality( + circuit_builder, + instruction, + instruction_bucket_polynomials, + ) } - pub fn instruction_pop(&self) -> Vec> { - [self.step_1(), self.shrink_stack(), self.keep_ram()].concat() + fn instruction_pop( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + [ + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_shrink_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), + ] + .concat() } - /// push'es argument should be on the stack after execution - /// $st0_next == nia => st0_next - nia == 0$ - pub fn instruction_push(&self) -> Vec> { - let specific_constraints = vec![self.st0_next() - self.nia()]; + fn instruction_push( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let specific_constraints = vec![next_base_row(ST0) - curr_base_row(NIA)]; [ specific_constraints, - self.grow_stack(), - self.step_2(), - self.keep_ram(), + Self::instruction_group_grow_op_stack(circuit_builder), + Self::instruction_group_step_2(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_divine(&self) -> Vec> { - [self.step_1(), self.grow_stack(), self.keep_ram()].concat() + fn instruction_divine( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + [ + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_grow_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), + ] + .concat() } - pub fn instruction_dup(&self) -> Vec> { + fn instruction_dup( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let indicator_poly = |idx| Self::indicator_polynomial(circuit_builder, idx); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let specific_constraints = vec![ - self.indicator_polynomial(0) * (self.st0_next() - self.st0()), - self.indicator_polynomial(1) * (self.st0_next() - self.st1()), - self.indicator_polynomial(2) * (self.st0_next() - self.st2()), - self.indicator_polynomial(3) * (self.st0_next() - self.st3()), - self.indicator_polynomial(4) * (self.st0_next() - self.st4()), - self.indicator_polynomial(5) * (self.st0_next() - self.st5()), - self.indicator_polynomial(6) * (self.st0_next() - self.st6()), - self.indicator_polynomial(7) * (self.st0_next() - self.st7()), - self.indicator_polynomial(8) * (self.st0_next() - self.st8()), - self.indicator_polynomial(9) * (self.st0_next() - self.st9()), - self.indicator_polynomial(10) * (self.st0_next() - self.st10()), - self.indicator_polynomial(11) * (self.st0_next() - self.st11()), - self.indicator_polynomial(12) * (self.st0_next() - self.st12()), - self.indicator_polynomial(13) * (self.st0_next() - self.st13()), - self.indicator_polynomial(14) * (self.st0_next() - self.st14()), - self.indicator_polynomial(15) * (self.st0_next() - self.st15()), + indicator_poly(0) * (next_base_row(ST0) - curr_base_row(ST0)), + indicator_poly(1) * (next_base_row(ST0) - curr_base_row(ST1)), + indicator_poly(2) * (next_base_row(ST0) - curr_base_row(ST2)), + indicator_poly(3) * (next_base_row(ST0) - curr_base_row(ST3)), + indicator_poly(4) * (next_base_row(ST0) - curr_base_row(ST4)), + indicator_poly(5) * (next_base_row(ST0) - curr_base_row(ST5)), + indicator_poly(6) * (next_base_row(ST0) - curr_base_row(ST6)), + indicator_poly(7) * (next_base_row(ST0) - curr_base_row(ST7)), + indicator_poly(8) * (next_base_row(ST0) - curr_base_row(ST8)), + indicator_poly(9) * (next_base_row(ST0) - curr_base_row(ST9)), + indicator_poly(10) * (next_base_row(ST0) - curr_base_row(ST10)), + indicator_poly(11) * (next_base_row(ST0) - curr_base_row(ST11)), + indicator_poly(12) * (next_base_row(ST0) - curr_base_row(ST12)), + indicator_poly(13) * (next_base_row(ST0) - curr_base_row(ST13)), + indicator_poly(14) * (next_base_row(ST0) - curr_base_row(ST14)), + indicator_poly(15) * (next_base_row(ST0) - curr_base_row(ST15)), ]; [ specific_constraints, - self.decompose_arg(), - self.step_2(), - self.grow_stack(), - self.keep_ram(), + Self::instruction_group_decompose_arg(circuit_builder), + Self::instruction_group_step_2(circuit_builder), + Self::instruction_group_grow_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_swap(&self) -> Vec> { + fn instruction_swap( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let one = || circuit_builder.b_constant(1_u32.into()); + let indicator_poly = |idx| Self::indicator_polynomial(circuit_builder, idx); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let specific_constraints = vec![ - self.indicator_polynomial(0), - self.indicator_polynomial(1) * (self.st1_next() - self.st0()), - self.indicator_polynomial(2) * (self.st2_next() - self.st0()), - self.indicator_polynomial(3) * (self.st3_next() - self.st0()), - self.indicator_polynomial(4) * (self.st4_next() - self.st0()), - self.indicator_polynomial(5) * (self.st5_next() - self.st0()), - self.indicator_polynomial(6) * (self.st6_next() - self.st0()), - self.indicator_polynomial(7) * (self.st7_next() - self.st0()), - self.indicator_polynomial(8) * (self.st8_next() - self.st0()), - self.indicator_polynomial(9) * (self.st9_next() - self.st0()), - self.indicator_polynomial(10) * (self.st10_next() - self.st0()), - self.indicator_polynomial(11) * (self.st11_next() - self.st0()), - self.indicator_polynomial(12) * (self.st12_next() - self.st0()), - self.indicator_polynomial(13) * (self.st13_next() - self.st0()), - self.indicator_polynomial(14) * (self.st14_next() - self.st0()), - self.indicator_polynomial(15) * (self.st15_next() - self.st0()), - self.indicator_polynomial(1) * (self.st0_next() - self.st1()), - self.indicator_polynomial(2) * (self.st0_next() - self.st2()), - self.indicator_polynomial(3) * (self.st0_next() - self.st3()), - self.indicator_polynomial(4) * (self.st0_next() - self.st4()), - self.indicator_polynomial(5) * (self.st0_next() - self.st5()), - self.indicator_polynomial(6) * (self.st0_next() - self.st6()), - self.indicator_polynomial(7) * (self.st0_next() - self.st7()), - self.indicator_polynomial(8) * (self.st0_next() - self.st8()), - self.indicator_polynomial(9) * (self.st0_next() - self.st9()), - self.indicator_polynomial(10) * (self.st0_next() - self.st10()), - self.indicator_polynomial(11) * (self.st0_next() - self.st11()), - self.indicator_polynomial(12) * (self.st0_next() - self.st12()), - self.indicator_polynomial(13) * (self.st0_next() - self.st13()), - self.indicator_polynomial(14) * (self.st0_next() - self.st14()), - self.indicator_polynomial(15) * (self.st0_next() - self.st15()), - (self.one() - self.indicator_polynomial(1)) * (self.st1_next() - self.st1()), - (self.one() - self.indicator_polynomial(2)) * (self.st2_next() - self.st2()), - (self.one() - self.indicator_polynomial(3)) * (self.st3_next() - self.st3()), - (self.one() - self.indicator_polynomial(4)) * (self.st4_next() - self.st4()), - (self.one() - self.indicator_polynomial(5)) * (self.st5_next() - self.st5()), - (self.one() - self.indicator_polynomial(6)) * (self.st6_next() - self.st6()), - (self.one() - self.indicator_polynomial(7)) * (self.st7_next() - self.st7()), - (self.one() - self.indicator_polynomial(8)) * (self.st8_next() - self.st8()), - (self.one() - self.indicator_polynomial(9)) * (self.st9_next() - self.st9()), - (self.one() - self.indicator_polynomial(10)) * (self.st10_next() - self.st10()), - (self.one() - self.indicator_polynomial(11)) * (self.st11_next() - self.st11()), - (self.one() - self.indicator_polynomial(12)) * (self.st12_next() - self.st12()), - (self.one() - self.indicator_polynomial(13)) * (self.st13_next() - self.st13()), - (self.one() - self.indicator_polynomial(14)) * (self.st14_next() - self.st14()), - (self.one() - self.indicator_polynomial(15)) * (self.st15_next() - self.st15()), - self.osv_next() - self.osv(), - self.osp_next() - self.osp(), + indicator_poly(0), + indicator_poly(1) * (next_base_row(ST1) - curr_base_row(ST0)), + indicator_poly(2) * (next_base_row(ST2) - curr_base_row(ST0)), + indicator_poly(3) * (next_base_row(ST3) - curr_base_row(ST0)), + indicator_poly(4) * (next_base_row(ST4) - curr_base_row(ST0)), + indicator_poly(5) * (next_base_row(ST5) - curr_base_row(ST0)), + indicator_poly(6) * (next_base_row(ST6) - curr_base_row(ST0)), + indicator_poly(7) * (next_base_row(ST7) - curr_base_row(ST0)), + indicator_poly(8) * (next_base_row(ST8) - curr_base_row(ST0)), + indicator_poly(9) * (next_base_row(ST9) - curr_base_row(ST0)), + indicator_poly(10) * (next_base_row(ST10) - curr_base_row(ST0)), + indicator_poly(11) * (next_base_row(ST11) - curr_base_row(ST0)), + indicator_poly(12) * (next_base_row(ST12) - curr_base_row(ST0)), + indicator_poly(13) * (next_base_row(ST13) - curr_base_row(ST0)), + indicator_poly(14) * (next_base_row(ST14) - curr_base_row(ST0)), + indicator_poly(15) * (next_base_row(ST15) - curr_base_row(ST0)), + indicator_poly(1) * (next_base_row(ST0) - curr_base_row(ST1)), + indicator_poly(2) * (next_base_row(ST0) - curr_base_row(ST2)), + indicator_poly(3) * (next_base_row(ST0) - curr_base_row(ST3)), + indicator_poly(4) * (next_base_row(ST0) - curr_base_row(ST4)), + indicator_poly(5) * (next_base_row(ST0) - curr_base_row(ST5)), + indicator_poly(6) * (next_base_row(ST0) - curr_base_row(ST6)), + indicator_poly(7) * (next_base_row(ST0) - curr_base_row(ST7)), + indicator_poly(8) * (next_base_row(ST0) - curr_base_row(ST8)), + indicator_poly(9) * (next_base_row(ST0) - curr_base_row(ST9)), + indicator_poly(10) * (next_base_row(ST0) - curr_base_row(ST10)), + indicator_poly(11) * (next_base_row(ST0) - curr_base_row(ST11)), + indicator_poly(12) * (next_base_row(ST0) - curr_base_row(ST12)), + indicator_poly(13) * (next_base_row(ST0) - curr_base_row(ST13)), + indicator_poly(14) * (next_base_row(ST0) - curr_base_row(ST14)), + indicator_poly(15) * (next_base_row(ST0) - curr_base_row(ST15)), + (one() - indicator_poly(1)) * (next_base_row(ST1) - curr_base_row(ST1)), + (one() - indicator_poly(2)) * (next_base_row(ST2) - curr_base_row(ST2)), + (one() - indicator_poly(3)) * (next_base_row(ST3) - curr_base_row(ST3)), + (one() - indicator_poly(4)) * (next_base_row(ST4) - curr_base_row(ST4)), + (one() - indicator_poly(5)) * (next_base_row(ST5) - curr_base_row(ST5)), + (one() - indicator_poly(6)) * (next_base_row(ST6) - curr_base_row(ST6)), + (one() - indicator_poly(7)) * (next_base_row(ST7) - curr_base_row(ST7)), + (one() - indicator_poly(8)) * (next_base_row(ST8) - curr_base_row(ST8)), + (one() - indicator_poly(9)) * (next_base_row(ST9) - curr_base_row(ST9)), + (one() - indicator_poly(10)) * (next_base_row(ST10) - curr_base_row(ST10)), + (one() - indicator_poly(11)) * (next_base_row(ST11) - curr_base_row(ST11)), + (one() - indicator_poly(12)) * (next_base_row(ST12) - curr_base_row(ST12)), + (one() - indicator_poly(13)) * (next_base_row(ST13) - curr_base_row(ST13)), + (one() - indicator_poly(14)) * (next_base_row(ST14) - curr_base_row(ST14)), + (one() - indicator_poly(15)) * (next_base_row(ST15) - curr_base_row(ST15)), + next_base_row(OSV) - curr_base_row(OSV), + next_base_row(OSP) - curr_base_row(OSP), ]; [ specific_constraints, - self.decompose_arg(), - self.step_2(), - self.keep_ram(), + Self::instruction_group_decompose_arg(circuit_builder), + Self::instruction_group_step_2(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_nop(&self) -> Vec> { - [self.step_1(), self.keep_stack(), self.keep_ram()].concat() + fn instruction_nop( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + [ + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_keep_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), + ] + .concat() } - pub fn instruction_skiz(&self) -> Vec> { + fn instruction_skiz( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let one = || constant(1); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + // The next instruction nia is decomposed into helper variables hv. - let nia_decomposes_to_hvs = self.nia() - (self.hv0() + self.two() * self.hv1()); + let nia_decomposes_to_hvs = + curr_base_row(NIA) - (curr_base_row(HV0) + constant(2) * curr_base_row(HV1)); // The relevant helper variable hv1 is either 0 or 1. // Here, hv0 == 1 means that nia takes an argument. - let hv0_is_0_or_1 = self.hv0() * (self.hv0() - self.one()); + let hv0_is_0_or_1 = curr_base_row(HV0) * (curr_base_row(HV0) - one()); // If `st0` is non-zero, register `ip` is incremented by 1. // If `st0` is 0 and `nia` takes no argument, register `ip` is incremented by 2. @@ -1297,38 +1396,48 @@ impl DualRowConstraints { // 6. (Register `st0` is 0 or `ip` is incremented by 1), and // (`st0` has a multiplicative inverse or `hv` is 1 or `ip` is incremented by 2), and // (`st0` has a multiplicative inverse or `hv0` is 0 or `ip` is incremented by 3). - let ip_case_1 = (self.ip_next() - self.ip() - self.one()) * self.st0(); - let ip_case_2 = (self.ip_next() - self.ip() - self.two()) - * (self.st0() * self.hv2() - self.one()) - * (self.hv0() - self.one()); - let ip_case_3 = (self.ip_next() - self.ip() - self.constant(3)) - * (self.st0() * self.hv2() - self.one()) - * self.hv0(); + let ip_case_1 = (next_base_row(IP) - curr_base_row(IP) - constant(1)) * curr_base_row(ST0); + let ip_case_2 = (next_base_row(IP) - curr_base_row(IP) - constant(2)) + * (curr_base_row(ST0) * curr_base_row(HV2) - one()) + * (curr_base_row(HV0) - one()); + let ip_case_3 = (next_base_row(IP) - curr_base_row(IP) - constant(3)) + * (curr_base_row(ST0) * curr_base_row(HV2) - one()) + * curr_base_row(HV0); let ip_incr_by_1_or_2_or_3 = ip_case_1 + ip_case_2 + ip_case_3; let specific_constraints = vec![nia_decomposes_to_hvs, hv0_is_0_or_1, ip_incr_by_1_or_2_or_3]; [ specific_constraints, - self.keep_jump_stack(), - self.shrink_stack(), - self.keep_ram(), + Self::instruction_group_keep_jump_stack(circuit_builder), + Self::instruction_group_shrink_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_call(&self) -> Vec> { + fn instruction_call( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + // The jump stack pointer jsp is incremented by 1. - let jsp_incr_1 = self.jsp_next() - (self.jsp() + self.one()); + let jsp_incr_1 = next_base_row(JSP) - curr_base_row(JSP) - constant(1); // The jump's origin jso is set to the current instruction pointer ip plus 2. - let jso_becomes_ip_plus_2 = self.jso_next() - self.ip() - self.two(); + let jso_becomes_ip_plus_2 = next_base_row(JSO) - curr_base_row(IP) - constant(2); // The jump's destination jsd is set to the instruction's argument. - let jsd_becomes_nia = self.jsd_next() - self.nia(); + let jsd_becomes_nia = next_base_row(JSD) - curr_base_row(NIA); // The instruction pointer ip is set to the instruction's argument. - let ip_becomes_nia = self.ip_next() - self.nia(); + let ip_becomes_nia = next_base_row(IP) - curr_base_row(NIA); let specific_constraints = vec![ jsp_incr_1, @@ -1336,89 +1445,166 @@ impl DualRowConstraints { jsd_becomes_nia, ip_becomes_nia, ]; - [specific_constraints, self.keep_stack(), self.keep_ram()].concat() + [ + specific_constraints, + Self::instruction_group_keep_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), + ] + .concat() } - pub fn instruction_return(&self) -> Vec> { + fn instruction_return( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + // The jump stack pointer jsp is decremented by 1. - let jsp_incr_1 = self.jsp_next() - (self.jsp() - self.one()); + let jsp_incr_1 = next_base_row(JSP) - (curr_base_row(JSP) - constant(1)); // The instruction pointer ip is set to the last call's origin jso. - let ip_becomes_jso = self.ip_next() - self.jso(); + let ip_becomes_jso = next_base_row(IP) - curr_base_row(JSO); let specific_constraints = vec![jsp_incr_1, ip_becomes_jso]; - [specific_constraints, self.keep_stack(), self.keep_ram()].concat() + [ + specific_constraints, + Self::instruction_group_keep_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), + ] + .concat() } - pub fn instruction_recurse(&self) -> Vec> { + fn instruction_recurse( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + // The instruction pointer ip is set to the last jump's destination jsd. - let ip_becomes_jsd = self.ip_next() - self.jsd(); + let ip_becomes_jsd = next_base_row(IP) - curr_base_row(JSD); let specific_constraints = vec![ip_becomes_jsd]; [ specific_constraints, - self.keep_jump_stack(), - self.keep_stack(), - self.keep_ram(), + Self::instruction_group_keep_jump_stack(circuit_builder), + Self::instruction_group_keep_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_assert(&self) -> Vec> { + fn instruction_assert( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + // The current top of the stack st0 is 1. - let st_0_is_1 = self.st0() - self.one(); + let st_0_is_1 = curr_base_row(ST0) - constant(1); let specific_constraints = vec![st_0_is_1]; [ specific_constraints, - self.step_1(), - self.shrink_stack(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_shrink_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_halt(&self) -> Vec> { + fn instruction_halt( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + // The instruction executed in the following step is instruction halt. - let halt_is_followed_by_halt = self.ci_next() - self.ci(); + let halt_is_followed_by_halt = next_base_row(CI) - curr_base_row(CI); let specific_constraints = vec![halt_is_followed_by_halt]; [ specific_constraints, - self.step_1(), - self.keep_stack(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_keep_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_read_mem(&self) -> Vec> { + fn instruction_read_mem( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + // the RAM pointer is overwritten with st0 - let update_ramp = self.ramp_next() - self.st0(); + let update_ramp = next_base_row(RAMP) - curr_base_row(ST0); // The top of the stack is overwritten with the RAM value. - let st0_becomes_ramv = self.st0_next() - self.ramv_next(); + let st0_becomes_ramv = next_base_row(ST0) - next_base_row(RAMV); let specific_constraints = vec![update_ramp, st0_becomes_ramv]; - [specific_constraints, self.step_1(), self.grow_stack()].concat() + [ + specific_constraints, + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_grow_op_stack(circuit_builder), + ] + .concat() } - pub fn instruction_write_mem(&self) -> Vec> { + fn instruction_write_mem( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + // the RAM pointer is overwritten with st1 - let update_ramp = self.ramp_next() - self.st1(); + let update_ramp = next_base_row(RAMP) - curr_base_row(ST1); // The RAM value is overwritten with the top of the stack. - let ramv_becomes_st0 = self.ramv_next() - self.st0(); + let ramv_becomes_st0 = next_base_row(RAMV) - curr_base_row(ST0); let specific_constraints = vec![update_ramp, ramv_becomes_st0]; - [specific_constraints, self.step_1(), self.shrink_stack()].concat() + [ + specific_constraints, + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_shrink_op_stack(circuit_builder), + ] + .concat() } /// Two Evaluation Arguments with the Hash Table guarantee correct transition. - pub fn instruction_hash(&self) -> Vec> { + fn instruction_hash( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { [ - self.step_1(), - self.stack_remains_and_top_ten_elements_unconstrained(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_op_stack_remains_and_top_ten_elements_unconstrained( + circuit_builder, + ), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } @@ -1427,24 +1613,41 @@ impl DualRowConstraints { /// leafs have 0 (respectively 1) as their least significant bit. The first /// two polynomials achieve that helper variable hv0 holds the result of /// st10 mod 2. The second polynomial sets the new value of st10 to st10 div 2. - pub fn instruction_divine_sibling(&self) -> Vec> { + fn instruction_divine_sibling( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let one = || constant(1); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + // Helper variable hv0 is either 0 or 1. - let hv0_is_0_or_1 = self.hv0() * (self.hv0() - self.one()); + let hv0_is_0_or_1 = curr_base_row(HV0) * (curr_base_row(HV0) - one()); // The 11th stack register is shifted by 1 bit to the right. - let st10_is_shifted_1_bit_right = self.st10_next() * self.two() + self.hv0() - self.st10(); + let st10_is_shifted_1_bit_right = + next_base_row(ST10) * constant(2) + curr_base_row(HV0) - curr_base_row(ST10); // The second pentuplet either stays where it is, or is moved to the top - let maybe_move_st5 = (self.one() - self.hv0()) * (self.st5() - self.st0_next()) - + self.hv0() * (self.st5() - self.st5_next()); - let maybe_move_st6 = (self.one() - self.hv0()) * (self.st6() - self.st1_next()) - + self.hv0() * (self.st6() - self.st6_next()); - let maybe_move_st7 = (self.one() - self.hv0()) * (self.st7() - self.st2_next()) - + self.hv0() * (self.st7() - self.st7_next()); - let maybe_move_st8 = (self.one() - self.hv0()) * (self.st8() - self.st3_next()) - + self.hv0() * (self.st8() - self.st8_next()); - let maybe_move_st9 = (self.one() - self.hv0()) * (self.st9() - self.st4_next()) - + self.hv0() * (self.st9() - self.st9_next()); + let maybe_move_st5 = (one() - curr_base_row(HV0)) + * (curr_base_row(ST5) - next_base_row(ST0)) + + curr_base_row(HV0) * (curr_base_row(ST5) - next_base_row(ST5)); + let maybe_move_st6 = (one() - curr_base_row(HV0)) + * (curr_base_row(ST6) - next_base_row(ST1)) + + curr_base_row(HV0) * (curr_base_row(ST6) - next_base_row(ST6)); + let maybe_move_st7 = (one() - curr_base_row(HV0)) + * (curr_base_row(ST7) - next_base_row(ST2)) + + curr_base_row(HV0) * (curr_base_row(ST7) - next_base_row(ST7)); + let maybe_move_st8 = (one() - curr_base_row(HV0)) + * (curr_base_row(ST8) - next_base_row(ST3)) + + curr_base_row(HV0) * (curr_base_row(ST8) - next_base_row(ST8)); + let maybe_move_st9 = (one() - curr_base_row(HV0)) + * (curr_base_row(ST9) - next_base_row(ST4)) + + curr_base_row(HV0) * (curr_base_row(ST9) - next_base_row(ST9)); let specific_constraints = vec![ hv0_is_0_or_1, @@ -1457,129 +1660,163 @@ impl DualRowConstraints { ]; [ specific_constraints, - self.stack_remains_and_top_eleven_elements_unconstrained(), - self.step_1(), - self.keep_ram(), + Self::instruction_group_op_stack_remains_and_top_eleven_elements_unconstrained( + circuit_builder, + ), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_assert_vector(&self) -> Vec> { + fn instruction_assert_vector( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let specific_constraints = vec![ - // Register st0 is equal to st5. - self.st5() - self.st0(), - // Register st1 is equal to st6. - self.st6() - self.st1(), - // and so on - self.st7() - self.st2(), - self.st8() - self.st3(), - self.st9() - self.st4(), + curr_base_row(ST5) - curr_base_row(ST0), + curr_base_row(ST6) - curr_base_row(ST1), + curr_base_row(ST7) - curr_base_row(ST2), + curr_base_row(ST8) - curr_base_row(ST3), + curr_base_row(ST9) - curr_base_row(ST4), ]; [ specific_constraints, - self.step_1(), - self.keep_stack(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_keep_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_absorb_init(&self) -> Vec> { - // no further constraints - let specific_constraints = vec![]; + fn instruction_absorb_init( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { [ - specific_constraints, - self.step_1(), - self.keep_stack(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_keep_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_absorb(&self) -> Vec> { - // no further constraints - let specific_constraints = vec![]; + fn instruction_absorb( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { [ - specific_constraints, - self.step_1(), - self.keep_stack(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_keep_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_squeeze(&self) -> Vec> { - // no further constraints - let specific_constraints = vec![]; + fn instruction_squeeze( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { [ - specific_constraints, - self.step_1(), - self.stack_remains_and_top_ten_elements_unconstrained(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_op_stack_remains_and_top_ten_elements_unconstrained( + circuit_builder, + ), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - /// The sum of the top two stack elements is moved into the top of the stack. - /// - /// $st0' - (st0 + st1) = 0$ - pub fn instruction_add(&self) -> Vec> { - let specific_constraints = vec![self.st0_next() - (self.st0() + self.st1())]; + fn instruction_add( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let specific_constraints = + vec![next_base_row(ST0) - curr_base_row(ST0) - curr_base_row(ST1)]; [ specific_constraints, - self.step_1(), - self.binop(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_binop(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - /// The product of the top two stack elements is moved into the top of the stack. - /// - /// $st0' - (st0 * st1) = 0$ - pub fn instruction_mul(&self) -> Vec> { - let specific_constraints = vec![self.st0_next() - (self.st0() * self.st1())]; + fn instruction_mul( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let specific_constraints = + vec![next_base_row(ST0) - curr_base_row(ST0) * curr_base_row(ST1)]; [ specific_constraints, - self.step_1(), - self.binop(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_binop(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - /// The top of the stack's inverse is moved into the top of the stack. - /// - /// $st0'·st0 - 1 = 0$ - pub fn instruction_invert(&self) -> Vec> { - let specific_constraints = vec![self.st0_next() * self.st0() - self.one()]; + fn instruction_invert( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let one = || constant(1); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let specific_constraints = vec![next_base_row(ST0) * curr_base_row(ST0) - one()]; [ specific_constraints, - self.step_1(), - self.unop(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_unop(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_eq(&self) -> Vec> { - // Helper variable hv0 is the inverse of the difference of the stack's two top-most elements or 0. - // - // $ hv0·(hv0·(st1 - st0) - 1) = 0 $ - let hv0_is_inverse_of_diff_or_hv0_is_0 = - self.hv0() * (self.hv0() * (self.st1() - self.st0()) - self.one()); + fn instruction_eq( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let one = || constant(1); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; - // Helper variable hv0 is the inverse of the difference of the stack's two top-most elements or the difference is 0. - // - // $ (st1 - st0)·(hv0·(st1 - st0) - 1) = 0 $ - let hv0_is_inverse_of_diff_or_diff_is_0 = - (self.st1() - self.st0()) * (self.hv0() * (self.st1() - self.st0()) - self.one()); + // Helper variable hv0 is the inverse-or-zero of the difference of the stack's two top-most + // elements: `hv0·(hv0·(st1 - st0) - 1)` + let hv0_is_inverse_of_diff_or_hv0_is_0 = curr_base_row(HV0) + * (curr_base_row(HV0) * (curr_base_row(ST1) - curr_base_row(ST0)) - one()); - // The new top of the stack is 1 if the difference between the stack's two top-most elements is not invertible, 0 otherwise. - // - // $ st0' - (1 - hv0·(st1 - st0)) = 0 $ - let st0_becomes_1_if_diff_is_not_invertible = - self.st0_next() - (self.one() - self.hv0() * (self.st1() - self.st0())); + // Helper variable hv0 is the inverse-or-zero of the difference of the stack's two + // top-most elements: `(st1 - st0)·(hv0·(st1 - st0) - 1)` + let hv0_is_inverse_of_diff_or_diff_is_0 = (curr_base_row(ST1) - curr_base_row(ST0)) + * (curr_base_row(HV0) * (curr_base_row(ST1) - curr_base_row(ST0)) - one()); + + // The new top of the stack is 1 if the difference between the stack's two top-most + // elements is not invertible, 0 otherwise: `st0' - (1 - hv0·(st1 - st0))` + let st0_becomes_1_if_diff_is_not_invertible = next_base_row(ST0) + - (one() - curr_base_row(HV0) * (curr_base_row(ST1) - curr_base_row(ST0))); let specific_constraints = vec![ hv0_is_inverse_of_diff_or_hv0_is_0, @@ -1588,21 +1825,29 @@ impl DualRowConstraints { ]; [ specific_constraints, - self.step_1(), - self.binop(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_binop(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_split(&self) -> Vec> { - let two_pow_32 = self.constant_b(BFieldElement::new(1_u64 << 32)); + fn instruction_split( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u64| circuit_builder.b_constant(c.into()); + let one = || constant(1); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; - // The top of the stack is decomposed as 32-bit chunks into the stack's top-most elements. - // - // $st0 - (2^32·st0' + st1') = 0$ + // The top of the stack is decomposed as 32-bit chunks into the stack's top-most elements: + // st0 - (2^32·st0' + st1') = 0$ let st0_decomposes_to_two_32_bit_chunks = - self.st0() - (two_pow_32.clone() * self.st1_next() + self.st0_next()); + curr_base_row(ST0) - (constant(1 << 32) * next_base_row(ST1) + next_base_row(ST0)); // Helper variable `hv0` = 0 if either // 1. `hv0` is the difference between (2^32 - 1) and the high 32 bits (`st0'`), or @@ -1611,12 +1856,12 @@ impl DualRowConstraints { // st1'·(hv0·(st0' - (2^32 - 1)) - 1) // lo·(hv0·(hi - 0xffff_ffff)) - 1) let hv0_holds_inverse_of_chunk_difference_or_low_bits_are_0 = { - let hv0 = self.hv0(); - let hi = self.st1_next(); - let lo = self.st0_next(); - let ffff_ffff = two_pow_32 - self.one(); + let hv0 = curr_base_row(HV0); + let hi = next_base_row(ST1); + let lo = next_base_row(ST0); + let ffff_ffff = constant(0xffff_ffff); - lo * (hv0 * (hi - ffff_ffff) - self.one()) + lo * (hv0 * (hi - ffff_ffff) - one()) }; let specific_constraints = vec![ @@ -1625,996 +1870,531 @@ impl DualRowConstraints { ]; [ specific_constraints, - self.grow_stack_and_top_two_elements_unconstrained(), - self.step_1(), - self.keep_ram(), - ] - .concat() - } - - pub fn instruction_lt(&self) -> Vec> { - // no further constraints - let specific_constraints = vec![]; - [ - specific_constraints, - self.step_1(), - self.binop(), - self.keep_ram(), - ] - .concat() - } - - pub fn instruction_and(&self) -> Vec> { - // no further constraints - let specific_constraints = vec![]; - [ - specific_constraints, - self.step_1(), - self.binop(), - self.keep_ram(), - ] - .concat() - } - - pub fn instruction_xor(&self) -> Vec> { - // no further constraints - let specific_constraints = vec![]; - [ - specific_constraints, - self.step_1(), - self.binop(), - self.keep_ram(), - ] - .concat() - } - - pub fn instruction_log_2_floor(&self) -> Vec> { - // no further constraints - let specific_constraints = vec![]; - [ - specific_constraints, - self.step_1(), - self.unop(), - self.keep_ram(), - ] - .concat() - } - - pub fn instruction_pow(&self) -> Vec> { - // no further constraints - let specific_constraints = vec![]; - [ - specific_constraints, - self.step_1(), - self.binop(), - self.keep_ram(), - ] - .concat() - } - - pub fn instruction_div(&self) -> Vec> { - // `n == d·q + r` means `st0 - st1·st1' - st0'` - let numerator_is_quotient_times_denominator_plus_remainder = - self.st0() - self.st1() * self.st1_next() - self.st0_next(); - - let st2_does_not_change = self.st2_next() - self.st2(); - - let specific_constraints = vec![ - numerator_is_quotient_times_denominator_plus_remainder, - st2_does_not_change, - ]; - [ - specific_constraints, - self.step_1(), - self.stack_remains_and_top_three_elements_unconstrained(), - self.keep_ram(), + Self::instruction_group_grow_op_stack_and_top_two_elements_unconstrained( + circuit_builder, + ), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_pop_count(&self) -> Vec> { - // no further constraints - let specific_constraints = vec![]; + fn instruction_lt( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { [ - specific_constraints, - self.step_1(), - self.unop(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_binop(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_xxadd(&self) -> Vec> { - // The result of adding st0 to st3 is moved into st0. - let st0_becomes_st0_plus_st3 = self.st0_next() - (self.st0() + self.st3()); - - // The result of adding st1 to st4 is moved into st1. - let st1_becomes_st1_plus_st4 = self.st1_next() - (self.st1() + self.st4()); - - // The result of adding st2 to st5 is moved into st2. - let st2_becomes_st2_plus_st5 = self.st2_next() - (self.st2() + self.st5()); - - let specific_constraints = vec![ - st0_becomes_st0_plus_st3, - st1_becomes_st1_plus_st4, - st2_becomes_st2_plus_st5, - ]; + fn instruction_and( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { [ - specific_constraints, - self.stack_remains_and_top_three_elements_unconstrained(), - self.step_1(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_binop(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_xxmul(&self) -> Vec> { - // The coefficient of x^0 of multiplying the two X-Field elements on the stack is moved into st0. - // - // $st0' - (st0·st3 - st2·st4 - st1·st5)$ - let st0_becomes_coefficient_0 = self.st0_next() - - (self.st0() * self.st3() - self.st2() * self.st4() - self.st1() * self.st5()); - - // The coefficient of x^1 of multiplying the two X-Field elements on the stack is moved into st1. - // - // st1' - (st1·st3 + st0·st4 - st2·st5 + st2·st4 + st1·st5) - let st1_becomes_coefficient_1 = self.st1_next() - - (self.st1() * self.st3() + self.st0() * self.st4() - self.st2() * self.st5() - + self.st2() * self.st4() - + self.st1() * self.st5()); - - // The coefficient of x^2 of multiplying the two X-Field elements on the stack is moved into st2. - // - // st2' - (st2·st3 + st1·st4 + st0·st5 + st2·st5) - let st2_becomes_coefficient_2 = self.st2_next() - - (self.st2() * self.st3() - + self.st1() * self.st4() - + self.st0() * self.st5() - + self.st2() * self.st5()); - - let specific_constraints = vec![ - st0_becomes_coefficient_0, - st1_becomes_coefficient_1, - st2_becomes_coefficient_2, - ]; + fn instruction_xor( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { [ - specific_constraints, - self.stack_remains_and_top_three_elements_unconstrained(), - self.step_1(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_binop(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_xinv(&self) -> Vec> { - // The coefficient of x^0 of multiplying X-Field element on top of the current stack and on top of the next stack is 1. - // - // $st0·st0' - st2·st1' - st1·st2' - 1 = 0$ - let first_coefficient_of_product_of_element_and_inverse_is_1 = self.st0() * self.st0_next() - - self.st2() * self.st1_next() - - self.st1() * self.st2_next() - - self.one(); - - // The coefficient of x^1 of multiplying X-Field element on top of the current stack and on top of the next stack is 0. - // - // $st1·st0' + st0·st1' - st2·st2' + st2·st1' + st1·st2' = 0$ - let second_coefficient_of_product_of_element_and_inverse_is_0 = - self.st1() * self.st0_next() + self.st0() * self.st1_next() - - self.st2() * self.st2_next() - + self.st2() * self.st1_next() - + self.st1() * self.st2_next(); - - // The coefficient of x^2 of multiplying X-Field element on top of the current stack and on top of the next stack is 0. - // - // $st2·st0' + st1·st1' + st0·st2' + st2·st2' = 0$ - let third_coefficient_of_product_of_element_and_inverse_is_0 = self.st2() * self.st0_next() - + self.st1() * self.st1_next() - + self.st0() * self.st2_next() - + self.st2() * self.st2_next(); - - let specific_constraints = vec![ - first_coefficient_of_product_of_element_and_inverse_is_1, - second_coefficient_of_product_of_element_and_inverse_is_0, - third_coefficient_of_product_of_element_and_inverse_is_0, - ]; + fn instruction_log_2_floor( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { [ - specific_constraints, - self.stack_remains_and_top_three_elements_unconstrained(), - self.step_1(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_unop(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn instruction_xbmul(&self) -> Vec> { - // The result of multiplying the top of the stack with the X-Field element's coefficient for x^0 is moved into st0. - // - // st0' - st0·st1 - let first_coeff_scalar_multiplication = self.st0_next() - self.st0() * self.st1(); - - // The result of multiplying the top of the stack with the X-Field element's coefficient for x^1 is moved into st1. - // - // st1' - st0·st2 - let secnd_coeff_scalar_multiplication = self.st1_next() - self.st0() * self.st2(); - - // The result of multiplying the top of the stack with the X-Field element's coefficient for x^2 is moved into st2. - // - // st2' - st0·st3 - let third_coeff_scalar_multiplication = self.st2_next() - self.st0() * self.st3(); - - let specific_constraints = vec![ - first_coeff_scalar_multiplication, - secnd_coeff_scalar_multiplication, - third_coeff_scalar_multiplication, - ]; + fn instruction_pow( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { [ - specific_constraints, - self.stack_shrinks_and_top_three_elements_unconstrained(), - self.step_1(), - self.keep_ram(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_binop(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - /// This instruction has no additional transition constraints. - /// - /// An Evaluation Argument with the list of input symbols guarantees correct transition. - pub fn instruction_read_io(&self) -> Vec> { - [self.step_1(), self.grow_stack(), self.keep_ram()].concat() - } - - /// This instruction has no additional transition constraints. - /// - /// An Evaluation Argument with the list of output symbols guarantees correct transition. - pub fn instruction_write_io(&self) -> Vec> { - [self.step_1(), self.shrink_stack(), self.keep_ram()].concat() - } - - pub fn zero(&self) -> ConstraintCircuitMonad { - self.zero.clone() - } - - pub fn one(&self) -> ConstraintCircuitMonad { - self.one.clone() - } - - pub fn two(&self) -> ConstraintCircuitMonad { - self.two.clone() - } - - pub fn constant(&self, constant: u32) -> ConstraintCircuitMonad { - self.circuit_builder.b_constant(constant.into()) - } - - pub fn constant_b(&self, constant: BFieldElement) -> ConstraintCircuitMonad { - self.circuit_builder.b_constant(constant) - } - - pub fn clk(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[CLK.master_base_table_index()].clone() - } - - pub fn ip(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[IP.master_base_table_index()].clone() - } - - pub fn ci(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[CI.master_base_table_index()].clone() - } - - pub fn nia(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[NIA.master_base_table_index()].clone() - } - - pub fn ib0(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[IB0.master_base_table_index()].clone() - } - - pub fn ib1(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[IB1.master_base_table_index()].clone() - } - - pub fn ib2(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[IB2.master_base_table_index()].clone() - } - - pub fn ib3(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[IB3.master_base_table_index()].clone() - } - - pub fn ib4(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[IB4.master_base_table_index()].clone() - } - - pub fn ib5(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[IB5.master_base_table_index()].clone() - } - - pub fn ib6(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[IB6.master_base_table_index()].clone() - } - - pub fn ib7(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[IB7.master_base_table_index()].clone() - } - - pub fn jsp(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[JSP.master_base_table_index()].clone() - } - - pub fn jsd(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[JSD.master_base_table_index()].clone() - } - - pub fn jso(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[JSO.master_base_table_index()].clone() - } - - pub fn st0(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST0.master_base_table_index()].clone() - } - - pub fn st1(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST1.master_base_table_index()].clone() - } - - pub fn st2(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST2.master_base_table_index()].clone() - } - - pub fn st3(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST3.master_base_table_index()].clone() - } - - pub fn st4(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST4.master_base_table_index()].clone() - } - - pub fn st5(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST5.master_base_table_index()].clone() - } - - pub fn st6(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST6.master_base_table_index()].clone() - } - - pub fn st7(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST7.master_base_table_index()].clone() - } - - pub fn st8(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST8.master_base_table_index()].clone() - } - - pub fn st9(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST9.master_base_table_index()].clone() - } - - pub fn st10(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST10.master_base_table_index()].clone() - } - - pub fn st11(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST11.master_base_table_index()].clone() - } - - pub fn st12(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST12.master_base_table_index()].clone() - } - - pub fn st13(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST13.master_base_table_index()].clone() - } - - pub fn st14(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST14.master_base_table_index()].clone() - } - - pub fn st15(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[ST15.master_base_table_index()].clone() - } - - pub fn osp(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[OSP.master_base_table_index()].clone() - } - - pub fn osv(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[OSV.master_base_table_index()].clone() - } - - pub fn hv0(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[HV0.master_base_table_index()].clone() - } - - pub fn hv1(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[HV1.master_base_table_index()].clone() - } - - pub fn hv2(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[HV2.master_base_table_index()].clone() - } - - pub fn hv3(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[HV3.master_base_table_index()].clone() - } - - pub fn ramp(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[RAMP.master_base_table_index()].clone() - } - - pub fn previous_instruction(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[PreviousInstruction.master_base_table_index()].clone() - } - - pub fn ramv(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[RAMV.master_base_table_index()].clone() - } - - pub fn is_padding(&self) -> ConstraintCircuitMonad { - self.current_base_row_variables[IsPadding.master_base_table_index()].clone() - } - - pub fn running_evaluation_standard_input(&self) -> ConstraintCircuitMonad { - self.current_ext_row_variables[InputTableEvalArg.master_ext_table_index()].clone() - } - pub fn running_evaluation_standard_output(&self) -> ConstraintCircuitMonad { - self.current_ext_row_variables[OutputTableEvalArg.master_ext_table_index()].clone() - } - pub fn instruction_lookup_log_derivative(&self) -> ConstraintCircuitMonad { - self.current_ext_row_variables - [InstructionLookupClientLogDerivative.master_ext_table_index()] - .clone() - } - pub fn running_product_op_stack_table(&self) -> ConstraintCircuitMonad { - self.current_ext_row_variables[OpStackTablePermArg.master_ext_table_index()].clone() - } - pub fn running_product_ram_table(&self) -> ConstraintCircuitMonad { - self.current_ext_row_variables[RamTablePermArg.master_ext_table_index()].clone() - } - pub fn running_product_jump_stack_table(&self) -> ConstraintCircuitMonad { - self.current_ext_row_variables[JumpStackTablePermArg.master_ext_table_index()].clone() - } - pub fn clock_jump_difference_lookup_server_log_derivative( - &self, - ) -> ConstraintCircuitMonad { - self.current_ext_row_variables - [ClockJumpDifferenceLookupServerLogDerivative.master_ext_table_index()] - .clone() - } - pub fn running_evaluation_hash_input(&self) -> ConstraintCircuitMonad { - self.current_ext_row_variables[HashInputEvalArg.master_ext_table_index()].clone() - } - pub fn running_evaluation_hash_digest(&self) -> ConstraintCircuitMonad { - self.current_ext_row_variables[HashDigestEvalArg.master_ext_table_index()].clone() - } - pub fn running_evaluation_sponge(&self) -> ConstraintCircuitMonad { - self.current_ext_row_variables[SpongeEvalArg.master_ext_table_index()].clone() - } - pub fn u32_table_running_sum_log_derivative(&self) -> ConstraintCircuitMonad { - self.current_ext_row_variables[U32LookupClientLogDerivative.master_ext_table_index()] - .clone() - } - - // Property: All polynomial variables that contain '_next' have the same - // variable position / value as the one without '_next', +/- NUM_COLUMNS. - pub fn clk_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[CLK.master_base_table_index()].clone() - } - - pub fn ip_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[IP.master_base_table_index()].clone() - } - - pub fn ci_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[CI.master_base_table_index()].clone() - } - - pub fn nia_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[NIA.master_base_table_index()].clone() - } - - pub fn ib0_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[IB0.master_base_table_index()].clone() - } - pub fn ib1_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[IB1.master_base_table_index()].clone() - } - pub fn ib2_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[IB2.master_base_table_index()].clone() - } - pub fn ib3_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[IB3.master_base_table_index()].clone() - } - pub fn ib4_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[IB4.master_base_table_index()].clone() - } - pub fn ib5_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[IB5.master_base_table_index()].clone() - } - pub fn ib6_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[IB6.master_base_table_index()].clone() - } - pub fn ib7_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[IB7.master_base_table_index()].clone() - } - - pub fn jsp_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[JSP.master_base_table_index()].clone() - } - - pub fn jsd_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[JSD.master_base_table_index()].clone() - } - - pub fn jso_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[JSO.master_base_table_index()].clone() - } - - pub fn st0_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST0.master_base_table_index()].clone() - } - - pub fn st1_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST1.master_base_table_index()].clone() - } - - pub fn st2_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST2.master_base_table_index()].clone() - } - - pub fn st3_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST3.master_base_table_index()].clone() - } - - pub fn st4_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST4.master_base_table_index()].clone() - } - - pub fn st5_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST5.master_base_table_index()].clone() - } - - pub fn st6_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST6.master_base_table_index()].clone() - } - - pub fn st7_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST7.master_base_table_index()].clone() - } - - pub fn st8_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST8.master_base_table_index()].clone() - } - - pub fn st9_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST9.master_base_table_index()].clone() - } - - pub fn st10_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST10.master_base_table_index()].clone() - } - - pub fn st11_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST11.master_base_table_index()].clone() - } - - pub fn st12_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST12.master_base_table_index()].clone() - } - - pub fn st13_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST13.master_base_table_index()].clone() - } - - pub fn st14_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST14.master_base_table_index()].clone() - } - - pub fn st15_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[ST15.master_base_table_index()].clone() - } - - pub fn osp_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[OSP.master_base_table_index()].clone() - } - - pub fn osv_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[OSV.master_base_table_index()].clone() - } - - pub fn ramp_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[RAMP.master_base_table_index()].clone() - } - - pub fn previous_instruction_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[PreviousInstruction.master_base_table_index()].clone() - } - - pub fn ramv_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[RAMV.master_base_table_index()].clone() - } - - pub fn clock_jump_difference_lookup_multiplicity_next( - &self, - ) -> ConstraintCircuitMonad { - self.next_base_row_variables - [ClockJumpDifferenceLookupMultiplicity.master_base_table_index()] - .clone() - } - - pub fn is_padding_next(&self) -> ConstraintCircuitMonad { - self.next_base_row_variables[IsPadding.master_base_table_index()].clone() - } - - pub fn running_evaluation_standard_input_next( - &self, - ) -> ConstraintCircuitMonad { - self.next_ext_row_variables[InputTableEvalArg.master_ext_table_index()].clone() - } - pub fn running_evaluation_standard_output_next( - &self, - ) -> ConstraintCircuitMonad { - self.next_ext_row_variables[OutputTableEvalArg.master_ext_table_index()].clone() - } - pub fn instruction_lookup_log_derivative_next( - &self, - ) -> ConstraintCircuitMonad { - self.next_ext_row_variables[InstructionLookupClientLogDerivative.master_ext_table_index()] - .clone() - } - pub fn running_product_op_stack_table_next(&self) -> ConstraintCircuitMonad { - self.next_ext_row_variables[OpStackTablePermArg.master_ext_table_index()].clone() - } - pub fn running_product_ram_table_next(&self) -> ConstraintCircuitMonad { - self.next_ext_row_variables[RamTablePermArg.master_ext_table_index()].clone() - } - pub fn running_product_jump_stack_table_next( - &self, - ) -> ConstraintCircuitMonad { - self.next_ext_row_variables[JumpStackTablePermArg.master_ext_table_index()].clone() - } - pub fn clock_jump_difference_lookup_server_log_derivative_next( - &self, - ) -> ConstraintCircuitMonad { - self.next_ext_row_variables - [ClockJumpDifferenceLookupServerLogDerivative.master_ext_table_index()] - .clone() - } - pub fn running_evaluation_hash_input_next(&self) -> ConstraintCircuitMonad { - self.next_ext_row_variables[HashInputEvalArg.master_ext_table_index()].clone() - } - pub fn running_evaluation_hash_digest_next(&self) -> ConstraintCircuitMonad { - self.next_ext_row_variables[HashDigestEvalArg.master_ext_table_index()].clone() - } - pub fn running_evaluation_sponge_next(&self) -> ConstraintCircuitMonad { - self.next_ext_row_variables[SpongeEvalArg.master_ext_table_index()].clone() - } - pub fn u32_table_running_sum_log_derivative_next( - &self, - ) -> ConstraintCircuitMonad { - self.next_ext_row_variables[U32LookupClientLogDerivative.master_ext_table_index()].clone() - } - - pub fn decompose_arg(&self) -> Vec> { - let hv0_is_a_bit = self.hv0() * (self.hv0() - self.one()); - let hv1_is_a_bit = self.hv1() * (self.hv1() - self.one()); - let hv2_is_a_bit = self.hv2() * (self.hv2() - self.one()); - let hv3_is_a_bit = self.hv3() * (self.hv3() - self.one()); - let helper_variables_are_binary_decomposition_of_nia = self.nia() - - self.constant(8) * self.hv3() - - self.constant(4) * self.hv2() - - self.constant(2) * self.hv1() - - self.hv0(); - vec![ - hv0_is_a_bit, - hv1_is_a_bit, - hv2_is_a_bit, - hv3_is_a_bit, - helper_variables_are_binary_decomposition_of_nia, - ] - } - - pub fn keep_jump_stack(&self) -> Vec> { - let jsp_does_not_change = self.jsp_next() - self.jsp(); - let jso_does_not_change = self.jso_next() - self.jso(); - let jsd_does_not_change = self.jsd_next() - self.jsd(); - vec![ - jsp_does_not_change, - jso_does_not_change, - jsd_does_not_change, - ] - } - - pub fn step_1(&self) -> Vec> { - let instruction_pointer_increases_by_one = self.ip_next() - self.ip() - self.one(); - let specific_constraints = vec![instruction_pointer_increases_by_one]; - [specific_constraints, self.keep_jump_stack()].concat() - } - - pub fn step_2(&self) -> Vec> { - let instruction_pointer_increases_by_two = self.ip_next() - self.ip() - self.two(); - let specific_constraints = vec![instruction_pointer_increases_by_two]; - [specific_constraints, self.keep_jump_stack()].concat() - } - - pub fn grow_stack_and_top_two_elements_unconstrained( - &self, - ) -> Vec> { - vec![ - // The stack element in st1 is moved into st2. - self.st2_next() - self.st1(), - // And so on... - self.st3_next() - self.st2(), - self.st4_next() - self.st3(), - self.st5_next() - self.st4(), - self.st6_next() - self.st5(), - self.st7_next() - self.st6(), - self.st8_next() - self.st7(), - self.st9_next() - self.st8(), - self.st10_next() - self.st9(), - self.st11_next() - self.st10(), - self.st12_next() - self.st11(), - self.st13_next() - self.st12(), - self.st14_next() - self.st13(), - self.st15_next() - self.st14(), - // The stack element in st15 is moved to the top of OpStack underflow, i.e., osv. - self.osv_next() - self.st15(), - // The OpStack pointer is incremented by 1. - self.osp_next() - (self.osp() + self.one()), - ] - } + fn instruction_div( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + // `n == d·q + r` means `st0 - st1·st1' - st0'` + let numerator_is_quotient_times_denominator_plus_remainder = + curr_base_row(ST0) - curr_base_row(ST1) * next_base_row(ST1) - next_base_row(ST0); + + let st2_does_not_change = next_base_row(ST2) - curr_base_row(ST2); - pub fn grow_stack(&self) -> Vec> { let specific_constraints = vec![ - // The stack element in st0 is moved into st1. - self.st1_next() - self.st0(), + numerator_is_quotient_times_denominator_plus_remainder, + st2_does_not_change, ]; [ specific_constraints, - self.grow_stack_and_top_two_elements_unconstrained(), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_op_stack_remains_and_top_three_elements_unconstrained( + circuit_builder, + ), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn stack_shrinks_and_top_three_elements_unconstrained( - &self, + fn instruction_pop_count( + circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { - vec![ - // The stack element in st4 is moved into st3. - self.st3_next() - self.st4(), - // The stack element in st5 is moved into st4. - self.st4_next() - self.st5(), - // And so on... - self.st5_next() - self.st6(), - self.st6_next() - self.st7(), - self.st7_next() - self.st8(), - self.st8_next() - self.st9(), - self.st9_next() - self.st10(), - self.st10_next() - self.st11(), - self.st11_next() - self.st12(), - self.st12_next() - self.st13(), - self.st13_next() - self.st14(), - self.st14_next() - self.st15(), - // The stack element at the top of OpStack underflow, i.e., osv, is moved into st15. - self.st15_next() - self.osv(), - // The OpStack pointer, osp, is decremented by 1. - self.osp_next() - (self.osp() - self.one()), - // The helper variable register hv3 holds the inverse of (osp - 16). - (self.osp() - self.constant(16)) * self.hv3() - self.one(), + [ + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_unop(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] + .concat() } - pub fn binop(&self) -> Vec> { + fn instruction_xxadd( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let st0_becomes_st0_plus_st3 = + next_base_row(ST0) - (curr_base_row(ST0) + curr_base_row(ST3)); + let st1_becomes_st1_plus_st4 = + next_base_row(ST1) - (curr_base_row(ST1) + curr_base_row(ST4)); + let st2_becomes_st2_plus_st5 = + next_base_row(ST2) - (curr_base_row(ST2) + curr_base_row(ST5)); + let specific_constraints = vec![ - // The stack element in st2 is moved into st1. - self.st1_next() - self.st2(), - // The stack element in st3 is moved into st2. - self.st2_next() - self.st3(), + st0_becomes_st0_plus_st3, + st1_becomes_st1_plus_st4, + st2_becomes_st2_plus_st5, ]; [ specific_constraints, - self.stack_shrinks_and_top_three_elements_unconstrained(), + Self::instruction_group_op_stack_remains_and_top_three_elements_unconstrained( + circuit_builder, + ), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn shrink_stack(&self) -> Vec> { - let specific_constrants = vec![self.st0_next() - self.st1()]; - [specific_constrants, self.binop()].concat() - } - - pub fn stack_remains_and_top_eleven_elements_unconstrained( - &self, + fn instruction_xxmul( + circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { - vec![ - self.st11_next() - self.st11(), - self.st12_next() - self.st12(), - self.st13_next() - self.st13(), - self.st14_next() - self.st14(), - self.st15_next() - self.st15(), - // The top of the OpStack underflow, i.e., osv, does not change. - self.osv_next() - self.osv(), - // The OpStack pointer, osp, does not change. - self.osp_next() - self.osp(), - ] - } + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; - pub fn stack_remains_and_top_ten_elements_unconstrained( - &self, - ) -> Vec> { - let specific_constraints = vec![self.st10_next() - self.st10()]; + let st0_becomes_coefficient_0 = next_base_row(ST0) + - (curr_base_row(ST0) * curr_base_row(ST3) + - curr_base_row(ST2) * curr_base_row(ST4) + - curr_base_row(ST1) * curr_base_row(ST5)); + let st1_becomes_coefficient_1 = next_base_row(ST1) + - (curr_base_row(ST1) * curr_base_row(ST3) + curr_base_row(ST0) * curr_base_row(ST4) + - curr_base_row(ST2) * curr_base_row(ST5) + + curr_base_row(ST2) * curr_base_row(ST4) + + curr_base_row(ST1) * curr_base_row(ST5)); + let st2_becomes_coefficient_2 = next_base_row(ST2) + - (curr_base_row(ST2) * curr_base_row(ST3) + + curr_base_row(ST1) * curr_base_row(ST4) + + curr_base_row(ST0) * curr_base_row(ST5) + + curr_base_row(ST2) * curr_base_row(ST5)); + + let specific_constraints = vec![ + st0_becomes_coefficient_0, + st1_becomes_coefficient_1, + st2_becomes_coefficient_2, + ]; [ specific_constraints, - self.stack_remains_and_top_eleven_elements_unconstrained(), + Self::instruction_group_op_stack_remains_and_top_three_elements_unconstrained( + circuit_builder, + ), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn stack_remains_and_top_three_elements_unconstrained( - &self, + fn instruction_xinv( + circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { + let constant = |c: u64| circuit_builder.b_constant(c.into()); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let first_coefficient_of_product_of_element_and_inverse_is_1 = curr_base_row(ST0) + * next_base_row(ST0) + - curr_base_row(ST2) * next_base_row(ST1) + - curr_base_row(ST1) * next_base_row(ST2) + - constant(1); + + let second_coefficient_of_product_of_element_and_inverse_is_0 = + curr_base_row(ST1) * next_base_row(ST0) + curr_base_row(ST0) * next_base_row(ST1) + - curr_base_row(ST2) * next_base_row(ST2) + + curr_base_row(ST2) * next_base_row(ST1) + + curr_base_row(ST1) * next_base_row(ST2); + + let third_coefficient_of_product_of_element_and_inverse_is_0 = curr_base_row(ST2) + * next_base_row(ST0) + + curr_base_row(ST1) * next_base_row(ST1) + + curr_base_row(ST0) * next_base_row(ST2) + + curr_base_row(ST2) * next_base_row(ST2); + let specific_constraints = vec![ - self.st3_next() - self.st3(), - self.st4_next() - self.st4(), - self.st5_next() - self.st5(), - self.st6_next() - self.st6(), - self.st7_next() - self.st7(), - self.st8_next() - self.st8(), - self.st9_next() - self.st9(), + first_coefficient_of_product_of_element_and_inverse_is_1, + second_coefficient_of_product_of_element_and_inverse_is_0, + third_coefficient_of_product_of_element_and_inverse_is_0, ]; [ specific_constraints, - self.stack_remains_and_top_ten_elements_unconstrained(), + Self::instruction_group_op_stack_remains_and_top_three_elements_unconstrained( + circuit_builder, + ), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn unop(&self) -> Vec> { + fn instruction_xbmul( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + + let first_coeff_scalar_multiplication = + next_base_row(ST0) - curr_base_row(ST0) * curr_base_row(ST1); + let secnd_coeff_scalar_multiplication = + next_base_row(ST1) - curr_base_row(ST0) * curr_base_row(ST2); + let third_coeff_scalar_multiplication = + next_base_row(ST2) - curr_base_row(ST0) * curr_base_row(ST3); + let specific_constraints = vec![ - // The stack element in st1 does not change. - self.st1_next() - self.st1(), - // The stack element in st2 does not change. - self.st2_next() - self.st2(), + first_coeff_scalar_multiplication, + secnd_coeff_scalar_multiplication, + third_coeff_scalar_multiplication, ]; [ specific_constraints, - self.stack_remains_and_top_three_elements_unconstrained(), + Self::instruction_group_op_stack_shrinks_and_top_three_elements_unconstrained( + circuit_builder, + ), + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] .concat() } - pub fn keep_stack(&self) -> Vec> { - let specific_constraints = vec![self.st0_next() - self.st0()]; - [specific_constraints, self.unop()].concat() + fn instruction_read_io( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + [ + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_grow_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), + ] + .concat() } - pub fn keep_ram(&self) -> Vec> { - vec![ - self.ramv_next() - self.ramv(), - self.ramp_next() - self.ramp(), + fn instruction_write_io( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + [ + Self::instruction_group_step_1(circuit_builder), + Self::instruction_group_shrink_op_stack(circuit_builder), + Self::instruction_group_keep_ram(circuit_builder), ] + .concat() + } + + fn get_transition_constraints_for_instruction( + circuit_builder: &ConstraintCircuitBuilder, + instruction: Instruction, + ) -> Vec> { + match instruction { + Pop => ExtProcessorTable::instruction_pop(circuit_builder), + Push(_) => ExtProcessorTable::instruction_push(circuit_builder), + Divine(_) => ExtProcessorTable::instruction_divine(circuit_builder), + Dup(_) => ExtProcessorTable::instruction_dup(circuit_builder), + Swap(_) => ExtProcessorTable::instruction_swap(circuit_builder), + Nop => ExtProcessorTable::instruction_nop(circuit_builder), + Skiz => ExtProcessorTable::instruction_skiz(circuit_builder), + Call(_) => ExtProcessorTable::instruction_call(circuit_builder), + Return => ExtProcessorTable::instruction_return(circuit_builder), + Recurse => ExtProcessorTable::instruction_recurse(circuit_builder), + Assert => ExtProcessorTable::instruction_assert(circuit_builder), + Halt => ExtProcessorTable::instruction_halt(circuit_builder), + ReadMem => ExtProcessorTable::instruction_read_mem(circuit_builder), + WriteMem => ExtProcessorTable::instruction_write_mem(circuit_builder), + Hash => ExtProcessorTable::instruction_hash(circuit_builder), + DivineSibling => ExtProcessorTable::instruction_divine_sibling(circuit_builder), + AssertVector => ExtProcessorTable::instruction_assert_vector(circuit_builder), + AbsorbInit => ExtProcessorTable::instruction_absorb_init(circuit_builder), + Absorb => ExtProcessorTable::instruction_absorb(circuit_builder), + Squeeze => ExtProcessorTable::instruction_squeeze(circuit_builder), + Add => ExtProcessorTable::instruction_add(circuit_builder), + Mul => ExtProcessorTable::instruction_mul(circuit_builder), + Invert => ExtProcessorTable::instruction_invert(circuit_builder), + Eq => ExtProcessorTable::instruction_eq(circuit_builder), + Split => ExtProcessorTable::instruction_split(circuit_builder), + Lt => ExtProcessorTable::instruction_lt(circuit_builder), + And => ExtProcessorTable::instruction_and(circuit_builder), + Xor => ExtProcessorTable::instruction_xor(circuit_builder), + Log2Floor => ExtProcessorTable::instruction_log_2_floor(circuit_builder), + Pow => ExtProcessorTable::instruction_pow(circuit_builder), + Div => ExtProcessorTable::instruction_div(circuit_builder), + PopCount => ExtProcessorTable::instruction_pop_count(circuit_builder), + XxAdd => ExtProcessorTable::instruction_xxadd(circuit_builder), + XxMul => ExtProcessorTable::instruction_xxmul(circuit_builder), + XInvert => ExtProcessorTable::instruction_xinv(circuit_builder), + XbMul => ExtProcessorTable::instruction_xbmul(circuit_builder), + ReadIo => ExtProcessorTable::instruction_read_io(circuit_builder), + WriteIo => ExtProcessorTable::instruction_write_io(circuit_builder), + } } - pub fn running_evaluation_for_standard_input_updates_correctly( - &self, + fn log_derivative_accumulates_clk_next( + circuit_builder: &ConstraintCircuitBuilder, ) -> ConstraintCircuitMonad { - let indeterminate = self.circuit_builder.challenge(StandardInputIndeterminate); + let challenge = |c: ChallengeId| circuit_builder.challenge(c); + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let curr_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(CurrentExtRow(col.master_ext_table_index())) + }; + let next_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(NextExtRow(col.master_ext_table_index())) + }; + + (next_ext_row(ClockJumpDifferenceLookupServerLogDerivative) + - curr_ext_row(ClockJumpDifferenceLookupServerLogDerivative)) + * (challenge(ClockJumpDifferenceLookupIndeterminate) - next_base_row(CLK)) + - next_base_row(ClockJumpDifferenceLookupMultiplicity) + } + + fn running_evaluation_for_standard_input_updates_correctly( + circuit_builder: &ConstraintCircuitBuilder, + ) -> ConstraintCircuitMonad { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let challenge = |c: ChallengeId| circuit_builder.challenge(c); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let curr_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(CurrentExtRow(col.master_ext_table_index())) + }; + let next_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(NextExtRow(col.master_ext_table_index())) + }; + let read_io_deselector = - InstructionDeselectors::instruction_deselector(self, Instruction::ReadIo); - let read_io_selector = self.ci() - self.constant_b(Instruction::ReadIo.opcode_b()); - let input_symbol = self.st0_next(); - let running_evaluation_updates = self.running_evaluation_standard_input_next() - - indeterminate * self.running_evaluation_standard_input() - - input_symbol; - let running_evaluation_remains = self.running_evaluation_standard_input_next() - - self.running_evaluation_standard_input(); + Self::instruction_deselector_current_row(circuit_builder, Instruction::ReadIo); + let read_io_selector = curr_base_row(CI) - constant(Instruction::ReadIo.opcode()); + + let running_evaluation_updates = next_ext_row(InputTableEvalArg) + - challenge(StandardInputIndeterminate) * curr_ext_row(InputTableEvalArg) + - next_base_row(ST0); + let running_evaluation_remains = + next_ext_row(InputTableEvalArg) - curr_ext_row(InputTableEvalArg); read_io_selector * running_evaluation_remains + read_io_deselector * running_evaluation_updates } - pub fn log_derivative_for_instruction_lookup_updates_correctly( - &self, + fn log_derivative_for_instruction_lookup_updates_correctly( + circuit_builder: &ConstraintCircuitBuilder, ) -> ConstraintCircuitMonad { - let indeterminate = self - .circuit_builder - .challenge(InstructionLookupIndeterminate); - let ip_weight = self.circuit_builder.challenge(ProgramAddressWeight); - let ci_weight = self.circuit_builder.challenge(ProgramInstructionWeight); - let nia_weight = self.circuit_builder.challenge(ProgramNextInstructionWeight); - let compressed_row = - ip_weight * self.ip_next() + ci_weight * self.ci_next() + nia_weight * self.nia_next(); - let log_derivative_updates = (self.instruction_lookup_log_derivative_next() - - self.instruction_lookup_log_derivative()) - * (indeterminate - compressed_row) - - self.one(); - let log_derivative_remains = self.instruction_lookup_log_derivative_next() - - self.instruction_lookup_log_derivative(); - - (self.one() - self.is_padding_next()) * log_derivative_updates - + self.is_padding_next() * log_derivative_remains - } - - pub fn running_evaluation_for_standard_output_updates_correctly( - &self, + let one = || circuit_builder.b_constant(1_u32.into()); + let challenge = |c: ChallengeId| circuit_builder.challenge(c); + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let curr_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(CurrentExtRow(col.master_ext_table_index())) + }; + let next_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(NextExtRow(col.master_ext_table_index())) + }; + + let compressed_row = challenge(ProgramAddressWeight) * next_base_row(IP) + + challenge(ProgramInstructionWeight) * next_base_row(CI) + + challenge(ProgramNextInstructionWeight) * next_base_row(NIA); + let log_derivative_updates = (next_ext_row(InstructionLookupClientLogDerivative) + - curr_ext_row(InstructionLookupClientLogDerivative)) + * (challenge(InstructionLookupIndeterminate) - compressed_row) + - one(); + let log_derivative_remains = next_ext_row(InstructionLookupClientLogDerivative) + - curr_ext_row(InstructionLookupClientLogDerivative); + + (one() - next_base_row(IsPadding)) * log_derivative_updates + + next_base_row(IsPadding) * log_derivative_remains + } + + fn running_evaluation_for_standard_output_updates_correctly( + circuit_builder: &ConstraintCircuitBuilder, ) -> ConstraintCircuitMonad { - let indeterminate = self.circuit_builder.challenge(StandardOutputIndeterminate); + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let challenge = |c: ChallengeId| circuit_builder.challenge(c); + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let curr_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(CurrentExtRow(col.master_ext_table_index())) + }; + let next_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(NextExtRow(col.master_ext_table_index())) + }; + let write_io_deselector = - InstructionDeselectors::instruction_deselector_next(self, Instruction::WriteIo); - let write_io_selector = self.ci_next() - self.constant_b(Instruction::WriteIo.opcode_b()); - let output_symbol = self.st0_next(); - let running_evaluation_updates = self.running_evaluation_standard_output_next() - - indeterminate * self.running_evaluation_standard_output() - - output_symbol; - let running_evaluation_remains = self.running_evaluation_standard_output_next() - - self.running_evaluation_standard_output(); + Self::instruction_deselector_next_row(circuit_builder, Instruction::WriteIo); + let write_io_selector = next_base_row(CI) - constant(Instruction::WriteIo.opcode()); + + let running_evaluation_updates = next_ext_row(OutputTableEvalArg) + - challenge(StandardOutputIndeterminate) * curr_ext_row(OutputTableEvalArg) + - next_base_row(ST0); + let running_evaluation_remains = + next_ext_row(OutputTableEvalArg) - curr_ext_row(OutputTableEvalArg); write_io_selector * running_evaluation_remains + write_io_deselector * running_evaluation_updates } - pub fn running_product_for_op_stack_table_updates_correctly( - &self, + fn running_product_for_op_stack_table_updates_correctly( + circuit_builder: &ConstraintCircuitBuilder, ) -> ConstraintCircuitMonad { - let indeterminate = self.circuit_builder.challenge(OpStackIndeterminate); - let clk_weight = self.circuit_builder.challenge(OpStackClkWeight); - let ib1_weight = self.circuit_builder.challenge(OpStackIb1Weight); - let osp_weight = self.circuit_builder.challenge(OpStackOspWeight); - let osv_weight = self.circuit_builder.challenge(OpStackOsvWeight); - let compressed_row = clk_weight * self.clk_next() - + ib1_weight * self.ib1_next() - + osp_weight * self.osp_next() - + osv_weight * self.osv_next(); - - self.running_product_op_stack_table_next() - - self.running_product_op_stack_table() * (indeterminate - compressed_row) - } - - pub fn running_product_for_ram_table_updates_correctly( - &self, + let challenge = |c: ChallengeId| circuit_builder.challenge(c); + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let curr_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(CurrentExtRow(col.master_ext_table_index())) + }; + let next_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(NextExtRow(col.master_ext_table_index())) + }; + + let compressed_row = challenge(OpStackClkWeight) * next_base_row(CLK) + + challenge(OpStackIb1Weight) * next_base_row(IB1) + + challenge(OpStackOspWeight) * next_base_row(OSP) + + challenge(OpStackOsvWeight) * next_base_row(OSV); + + next_ext_row(OpStackTablePermArg) + - curr_ext_row(OpStackTablePermArg) * (challenge(OpStackIndeterminate) - compressed_row) + } + + fn running_product_for_ram_table_updates_correctly( + circuit_builder: &ConstraintCircuitBuilder, ) -> ConstraintCircuitMonad { - let indeterminate = self.circuit_builder.challenge(RamIndeterminate); - let clk_weight = self.circuit_builder.challenge(RamClkWeight); - let ramp_weight = self.circuit_builder.challenge(RamRampWeight); - let ramv_weight = self.circuit_builder.challenge(RamRamvWeight); - let previous_instruction_weight = - self.circuit_builder.challenge(RamPreviousInstructionWeight); - let compressed_row = clk_weight * self.clk_next() - + ramp_weight * self.ramp_next() - + ramv_weight * self.ramv_next() - + previous_instruction_weight * self.previous_instruction_next(); - - self.running_product_ram_table_next() - - self.running_product_ram_table() * (indeterminate - compressed_row) - } - - pub fn running_product_for_jump_stack_table_updates_correctly( - &self, + let challenge = |c: ChallengeId| circuit_builder.challenge(c); + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let curr_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(CurrentExtRow(col.master_ext_table_index())) + }; + let next_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(NextExtRow(col.master_ext_table_index())) + }; + + let compressed_row = challenge(RamClkWeight) * next_base_row(CLK) + + challenge(RamRampWeight) * next_base_row(RAMP) + + challenge(RamRamvWeight) * next_base_row(RAMV) + + challenge(RamPreviousInstructionWeight) * next_base_row(PreviousInstruction); + + next_ext_row(RamTablePermArg) + - curr_ext_row(RamTablePermArg) * (challenge(RamIndeterminate) - compressed_row) + } + + fn running_product_for_jump_stack_table_updates_correctly( + circuit_builder: &ConstraintCircuitBuilder, ) -> ConstraintCircuitMonad { - let indeterminate = self.circuit_builder.challenge(JumpStackIndeterminate); - let clk_weight = self.circuit_builder.challenge(JumpStackClkWeight); - let ci_weight = self.circuit_builder.challenge(JumpStackCiWeight); - let jsp_weight = self.circuit_builder.challenge(JumpStackJspWeight); - let jso_weight = self.circuit_builder.challenge(JumpStackJsoWeight); - let jsd_weight = self.circuit_builder.challenge(JumpStackJsdWeight); - let compressed_row = clk_weight * self.clk_next() - + ci_weight * self.ci_next() - + jsp_weight * self.jsp_next() - + jso_weight * self.jso_next() - + jsd_weight * self.jsd_next(); - - self.running_product_jump_stack_table_next() - - self.running_product_jump_stack_table() * (indeterminate - compressed_row) - } - - pub fn running_evaluation_hash_input_updates_correctly( - &self, + let challenge = |c: ChallengeId| circuit_builder.challenge(c); + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let curr_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(CurrentExtRow(col.master_ext_table_index())) + }; + let next_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(NextExtRow(col.master_ext_table_index())) + }; + + let compressed_row = challenge(JumpStackClkWeight) * next_base_row(CLK) + + challenge(JumpStackCiWeight) * next_base_row(CI) + + challenge(JumpStackJspWeight) * next_base_row(JSP) + + challenge(JumpStackJsoWeight) * next_base_row(JSO) + + challenge(JumpStackJsdWeight) * next_base_row(JSD); + + next_ext_row(JumpStackTablePermArg) + - curr_ext_row(JumpStackTablePermArg) + * (challenge(JumpStackIndeterminate) - compressed_row) + } + + fn running_evaluation_hash_input_updates_correctly( + circuit_builder: &ConstraintCircuitBuilder, ) -> ConstraintCircuitMonad { - let hash_deselector = - InstructionDeselectors::instruction_deselector_next(self, Instruction::Hash); - let hash_selector = self.ci_next() - self.constant_b(Instruction::Hash.opcode_b()); + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let challenge = |c: ChallengeId| circuit_builder.challenge(c); + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let curr_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(CurrentExtRow(col.master_ext_table_index())) + }; + let next_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(NextExtRow(col.master_ext_table_index())) + }; - let indeterminate = self.circuit_builder.challenge(HashInputIndeterminate); + let hash_deselector = + Self::instruction_deselector_next_row(circuit_builder, Instruction::Hash); + let hash_selector = next_base_row(CI) - constant(Instruction::Hash.opcode()); let weights = [ HashStateWeight0, @@ -2628,41 +2408,55 @@ impl DualRowConstraints { HashStateWeight8, HashStateWeight9, ] - .map(|w| self.circuit_builder.challenge(w)); + .map(challenge); let state = [ - self.st0_next(), - self.st1_next(), - self.st2_next(), - self.st3_next(), - self.st4_next(), - self.st5_next(), - self.st6_next(), - self.st7_next(), - self.st8_next(), - self.st9_next(), + next_base_row(ST0), + next_base_row(ST1), + next_base_row(ST2), + next_base_row(ST3), + next_base_row(ST4), + next_base_row(ST5), + next_base_row(ST6), + next_base_row(ST7), + next_base_row(ST8), + next_base_row(ST9), ]; let compressed_row = weights .into_iter() .zip_eq(state.into_iter()) .map(|(weight, state)| weight * state) .sum(); - let running_evaluation_updates = self.running_evaluation_hash_input_next() - - indeterminate * self.running_evaluation_hash_input() + + let running_evaluation_updates = next_ext_row(HashInputEvalArg) + - challenge(HashInputIndeterminate) * curr_ext_row(HashInputEvalArg) - compressed_row; let running_evaluation_remains = - self.running_evaluation_hash_input_next() - self.running_evaluation_hash_input(); + next_ext_row(HashInputEvalArg) - curr_ext_row(HashInputEvalArg); hash_selector * running_evaluation_remains + hash_deselector * running_evaluation_updates } - pub fn running_evaluation_hash_digest_updates_correctly( - &self, + fn running_evaluation_hash_digest_updates_correctly( + circuit_builder: &ConstraintCircuitBuilder, ) -> ConstraintCircuitMonad { - let hash_deselector = - InstructionDeselectors::instruction_deselector(self, Instruction::Hash); - let hash_selector = self.ci() - self.constant_b(Instruction::Hash.opcode_b()); + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let challenge = |c: ChallengeId| circuit_builder.challenge(c); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let curr_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(CurrentExtRow(col.master_ext_table_index())) + }; + let next_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(NextExtRow(col.master_ext_table_index())) + }; - let indeterminate = self.circuit_builder.challenge(HashDigestIndeterminate); + let hash_deselector = + Self::instruction_deselector_current_row(circuit_builder, Instruction::Hash); + let hash_selector = curr_base_row(CI) - constant(Instruction::Hash.opcode()); let weights = [ HashStateWeight0, @@ -2671,44 +2465,58 @@ impl DualRowConstraints { HashStateWeight3, HashStateWeight4, ] - .map(|w| self.circuit_builder.challenge(w)); + .map(challenge); let state = [ - self.st5_next(), - self.st6_next(), - self.st7_next(), - self.st8_next(), - self.st9_next(), + next_base_row(ST5), + next_base_row(ST6), + next_base_row(ST7), + next_base_row(ST8), + next_base_row(ST9), ]; let compressed_row = weights .into_iter() .zip_eq(state.into_iter()) .map(|(weight, state)| weight * state) .sum(); - let running_evaluation_updates = self.running_evaluation_hash_digest_next() - - indeterminate * self.running_evaluation_hash_digest() + + let running_evaluation_updates = next_ext_row(HashDigestEvalArg) + - challenge(HashDigestIndeterminate) * curr_ext_row(HashDigestEvalArg) - compressed_row; let running_evaluation_remains = - self.running_evaluation_hash_digest_next() - self.running_evaluation_hash_digest(); + next_ext_row(HashDigestEvalArg) - curr_ext_row(HashDigestEvalArg); hash_selector * running_evaluation_remains + hash_deselector * running_evaluation_updates } - pub fn running_evaluation_sponge_updates_correctly( - &self, + fn running_evaluation_sponge_updates_correctly( + circuit_builder: &ConstraintCircuitBuilder, ) -> ConstraintCircuitMonad { + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let challenge = |c: ChallengeId| circuit_builder.challenge(c); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let curr_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(CurrentExtRow(col.master_ext_table_index())) + }; + let next_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(NextExtRow(col.master_ext_table_index())) + }; + let absorb_init_deselector = - InstructionDeselectors::instruction_deselector(self, Instruction::AbsorbInit); + Self::instruction_deselector_current_row(circuit_builder, Instruction::AbsorbInit); let absorb_deselector = - InstructionDeselectors::instruction_deselector(self, Instruction::Absorb); + Self::instruction_deselector_current_row(circuit_builder, Instruction::Absorb); let squeeze_deselector = - InstructionDeselectors::instruction_deselector(self, Instruction::Squeeze); + Self::instruction_deselector_current_row(circuit_builder, Instruction::Squeeze); - let opcode_absorb_init = self.constant_b(Instruction::AbsorbInit.opcode_b()); - let opcode_absorb = self.constant_b(Instruction::Absorb.opcode_b()); - let opcode_squeeze = self.constant_b(Instruction::Squeeze.opcode_b()); - let sponge_instruction_selector = (self.ci() - opcode_absorb_init) - * (self.ci() - opcode_absorb) - * (self.ci() - opcode_squeeze); + let sponge_instruction_selector = (curr_base_row(CI) + - constant(Instruction::AbsorbInit.opcode())) + * (curr_base_row(CI) - constant(Instruction::Absorb.opcode())) + * (curr_base_row(CI) - constant(Instruction::Squeeze.opcode())); let weights = [ HashStateWeight0, @@ -2722,18 +2530,18 @@ impl DualRowConstraints { HashStateWeight8, HashStateWeight9, ] - .map(|w| self.circuit_builder.challenge(w)); + .map(challenge); let state_next = [ - self.st0_next(), - self.st1_next(), - self.st2_next(), - self.st3_next(), - self.st4_next(), - self.st5_next(), - self.st6_next(), - self.st7_next(), - self.st8_next(), - self.st9_next(), + next_base_row(ST0), + next_base_row(ST1), + next_base_row(ST2), + next_base_row(ST3), + next_base_row(ST4), + next_base_row(ST5), + next_base_row(ST6), + next_base_row(ST7), + next_base_row(ST8), + next_base_row(ST9), ]; let compressed_row_next = weights .into_iter() @@ -2741,14 +2549,11 @@ impl DualRowConstraints { .map(|(weight, st_next)| weight * st_next) .sum(); - let indeterminate = self.circuit_builder.challenge(SpongeIndeterminate); - let ci_weight = self.circuit_builder.challenge(HashCIWeight); - let running_evaluation_updates = self.running_evaluation_sponge_next() - - indeterminate * self.running_evaluation_sponge() - - ci_weight * self.ci() + let running_evaluation_updates = next_ext_row(SpongeEvalArg) + - challenge(SpongeIndeterminate) * curr_ext_row(SpongeEvalArg) + - challenge(HashCIWeight) * curr_base_row(CI) - compressed_row_next; - let running_evaluation_remains = - self.running_evaluation_sponge_next() - self.running_evaluation_sponge(); + let running_evaluation_remains = next_ext_row(SpongeEvalArg) - curr_ext_row(SpongeEvalArg); sponge_instruction_selector * running_evaluation_remains + absorb_init_deselector * running_evaluation_updates.clone() @@ -2756,86 +2561,99 @@ impl DualRowConstraints { + squeeze_deselector * running_evaluation_updates } - pub fn log_derivative_with_u32_table_updates_correctly( - &self, + fn log_derivative_with_u32_table_updates_correctly( + circuit_builder: &ConstraintCircuitBuilder, ) -> ConstraintCircuitMonad { - let opcode_of_and = self.circuit_builder.b_constant(Instruction::And.opcode_b()); - let two_inverse = self - .circuit_builder - .b_constant(BFieldElement::new(2).inverse()); - - let indeterminate = self.circuit_builder.challenge(U32Indeterminate); - let lhs_weight = self.circuit_builder.challenge(U32LhsWeight); - let rhs_weight = self.circuit_builder.challenge(U32RhsWeight); - let ci_weight = self.circuit_builder.challenge(U32CiWeight); - let result_weight = self.circuit_builder.challenge(U32ResultWeight); + let constant = |c: u32| circuit_builder.b_constant(c.into()); + let one = || constant(1); + let two_inverse = circuit_builder.b_constant(BFieldElement::new(2).inverse()); + let challenge = |c: ChallengeId| circuit_builder.challenge(c); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; + let curr_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(CurrentExtRow(col.master_ext_table_index())) + }; + let next_ext_row = |col: ProcessorExtTableColumn| { + circuit_builder.input(NextExtRow(col.master_ext_table_index())) + }; let split_deselector = - InstructionDeselectors::instruction_deselector(self, Instruction::Split); - let lt_deselector = InstructionDeselectors::instruction_deselector(self, Instruction::Lt); - let and_deselector = InstructionDeselectors::instruction_deselector(self, Instruction::And); - let xor_deselector = InstructionDeselectors::instruction_deselector(self, Instruction::Xor); - let pow_deselector = InstructionDeselectors::instruction_deselector(self, Instruction::Pow); + Self::instruction_deselector_current_row(circuit_builder, Instruction::Split); + let lt_deselector = + Self::instruction_deselector_current_row(circuit_builder, Instruction::Lt); + let and_deselector = + Self::instruction_deselector_current_row(circuit_builder, Instruction::And); + let xor_deselector = + Self::instruction_deselector_current_row(circuit_builder, Instruction::Xor); + let pow_deselector = + Self::instruction_deselector_current_row(circuit_builder, Instruction::Pow); let log_2_floor_deselector = - InstructionDeselectors::instruction_deselector(self, Instruction::Log2Floor); - let div_deselector = InstructionDeselectors::instruction_deselector(self, Instruction::Div); + Self::instruction_deselector_current_row(circuit_builder, Instruction::Log2Floor); + let div_deselector = + Self::instruction_deselector_current_row(circuit_builder, Instruction::Div); let pop_count_deselector = - InstructionDeselectors::instruction_deselector(self, Instruction::PopCount); - - let running_sum = self.u32_table_running_sum_log_derivative(); - let running_sum_next = self.u32_table_running_sum_log_derivative_next(); - - let split_factor = indeterminate.clone() - - lhs_weight.clone() * self.st0_next() - - rhs_weight.clone() * self.st1_next() - - ci_weight.clone() * self.ci(); - let binop_factor = indeterminate.clone() - - lhs_weight.clone() * self.st0() - - rhs_weight.clone() * self.st1() - - ci_weight.clone() * self.ci() - - result_weight.clone() * self.st0_next(); - let xor_factor = indeterminate.clone() - - lhs_weight.clone() * self.st0() - - rhs_weight.clone() * self.st1() - - ci_weight.clone() * opcode_of_and - - result_weight.clone() * (self.st0() + self.st1() - self.st0_next()) * two_inverse; - let unop_factor = indeterminate.clone() - - lhs_weight.clone() * self.st0() - - ci_weight.clone() * self.ci() - - result_weight.clone() * self.st0_next(); - let div_factor_for_lt = indeterminate.clone() - - lhs_weight.clone() * self.st0_next() - - rhs_weight.clone() * self.st1() - - ci_weight.clone() * self.constant_b(Instruction::Lt.opcode_b()) - - result_weight; - let div_factor_for_range_check = indeterminate - - lhs_weight * self.st0() - - rhs_weight * self.st1_next() - - ci_weight * self.constant_b(Instruction::Split.opcode_b()); - - let split_summand = split_deselector - * ((running_sum_next.clone() - running_sum.clone()) * split_factor - self.one()); - let lt_summand = lt_deselector - * ((running_sum_next.clone() - running_sum.clone()) * binop_factor.clone() - - self.one()); - let and_summand = and_deselector - * ((running_sum_next.clone() - running_sum.clone()) * binop_factor.clone() - - self.one()); - let xor_summand = xor_deselector - * ((running_sum_next.clone() - running_sum.clone()) * xor_factor - self.one()); - let pow_summand = pow_deselector - * ((running_sum_next.clone() - running_sum.clone()) * binop_factor - self.one()); - let log_2_floor_summand = log_2_floor_deselector - * ((running_sum_next.clone() - running_sum.clone()) * unop_factor.clone() - self.one()); + Self::instruction_deselector_current_row(circuit_builder, Instruction::PopCount); + + let running_sum = curr_ext_row(U32LookupClientLogDerivative); + let running_sum_next = next_ext_row(U32LookupClientLogDerivative); + + let split_factor = challenge(U32Indeterminate) + - challenge(U32LhsWeight) * next_base_row(ST0) + - challenge(U32RhsWeight) * next_base_row(ST1) + - challenge(U32CiWeight) * curr_base_row(CI); + let binop_factor = challenge(U32Indeterminate) + - challenge(U32LhsWeight) * curr_base_row(ST0) + - challenge(U32RhsWeight) * curr_base_row(ST1) + - challenge(U32CiWeight) * curr_base_row(CI) + - challenge(U32ResultWeight) * next_base_row(ST0); + let xor_factor = challenge(U32Indeterminate) + - challenge(U32LhsWeight) * curr_base_row(ST0) + - challenge(U32RhsWeight) * curr_base_row(ST1) + - challenge(U32CiWeight) * constant(Instruction::And.opcode()) + - challenge(U32ResultWeight) + * (curr_base_row(ST0) + curr_base_row(ST1) - next_base_row(ST0)) + * two_inverse; + let unop_factor = challenge(U32Indeterminate) + - challenge(U32LhsWeight) * curr_base_row(ST0) + - challenge(U32CiWeight) * curr_base_row(CI) + - challenge(U32ResultWeight) * next_base_row(ST0); + let div_factor_for_lt = challenge(U32Indeterminate) + - challenge(U32LhsWeight) * next_base_row(ST0) + - challenge(U32RhsWeight) * curr_base_row(ST1) + - challenge(U32CiWeight) * constant(Instruction::Lt.opcode()) + - challenge(U32ResultWeight); + let div_factor_for_range_check = challenge(U32Indeterminate) + - challenge(U32LhsWeight) * curr_base_row(ST0) + - challenge(U32RhsWeight) * next_base_row(ST1) + - challenge(U32CiWeight) * constant(Instruction::Split.opcode()); + + let running_sum_absorbs_split_factor = + (running_sum_next.clone() - running_sum.clone()) * split_factor - one(); + let running_sum_absorbs_binop_factor = + (running_sum_next.clone() - running_sum.clone()) * binop_factor - one(); + let running_sum_absorb_xor_factor = + (running_sum_next.clone() - running_sum.clone()) * xor_factor - one(); + let running_sum_absorbs_unop_factor = + (running_sum_next.clone() - running_sum.clone()) * unop_factor - one(); + + let split_summand = split_deselector * running_sum_absorbs_split_factor; + let lt_summand = lt_deselector * running_sum_absorbs_binop_factor.clone(); + let and_summand = and_deselector * running_sum_absorbs_binop_factor.clone(); + let xor_summand = xor_deselector * running_sum_absorb_xor_factor; + let pow_summand = pow_deselector * running_sum_absorbs_binop_factor; + let log_2_floor_summand = log_2_floor_deselector * running_sum_absorbs_unop_factor.clone(); let div_summand = div_deselector * ((running_sum_next.clone() - running_sum.clone()) * div_factor_for_lt.clone() * div_factor_for_range_check.clone() - div_factor_for_lt - div_factor_for_range_check); - let pop_count_summand = pop_count_deselector - * ((running_sum_next.clone() - running_sum.clone()) * unop_factor - self.one()); - let no_update_summand = (self.one() - self.ib2()) * (running_sum_next - running_sum); + let pop_count_summand = pop_count_deselector * running_sum_absorbs_unop_factor; + let no_update_summand = (one() - curr_base_row(IB2)) * (running_sum_next - running_sum); split_summand + lt_summand @@ -2847,137 +2665,98 @@ impl DualRowConstraints { + pop_count_summand + no_update_summand } -} - -#[derive(Debug, Clone)] -pub struct InstructionDeselectors { - deselectors: HashMap>, -} -impl InstructionDeselectors { - fn new(factory: &mut DualRowConstraints) -> Self { - let deselectors = Self::create(factory); - - Self { deselectors } - } - - /// A polynomial that has solutions when `ci` is not `instruction`. - /// - /// This is naively achieved by constructing a polynomial that has - /// a solution when `ci` is any other instruction. This deselector - /// can be replaced with an efficient one based on `ib` registers. - pub fn get(&self, instruction: Instruction) -> ConstraintCircuitMonad { - self.deselectors - .get(&instruction) - .unwrap_or_else(|| panic!("The instruction {instruction} does not exist!")) - .clone() - } + pub fn ext_transition_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let constant = |c: u64| circuit_builder.b_constant(c.into()); + let curr_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(CurrentBaseRow(col.master_base_table_index())) + }; + let next_base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(NextBaseRow(col.master_base_table_index())) + }; - /// internal helper function to de-duplicate functionality common between the similar (but - /// different on a type level) functions for construction deselectors - fn instruction_deselector_common_functionality( - circuit_builder: &ConstraintCircuitBuilder, - instruction: Instruction, - instruction_bucket_polynomials: [ConstraintCircuitMonad; Ord8::COUNT], - ) -> ConstraintCircuitMonad { - let one = circuit_builder.b_constant(1u32.into()); + // instruction-specific constraints + let all_transition_constraints_by_instruction = ALL_INSTRUCTIONS.map(|instruction| { + Self::get_transition_constraints_for_instruction(circuit_builder, instruction) + }); + let all_instruction_transition_constraints = ALL_INSTRUCTIONS + .into_iter() + .zip_eq(all_transition_constraints_by_instruction.into_iter()) + .collect_vec() + .try_into() + .unwrap(); - let selector_bits: [_; Ord8::COUNT] = [ - instruction.ib(Ord8::IB0), - instruction.ib(Ord8::IB1), - instruction.ib(Ord8::IB2), - instruction.ib(Ord8::IB3), - instruction.ib(Ord8::IB4), - instruction.ib(Ord8::IB5), - instruction.ib(Ord8::IB6), - instruction.ib(Ord8::IB7), - ]; - let deselector_polynomials = - selector_bits.map(|b| one.clone() - circuit_builder.b_constant(b)); + let deselected_transition_constraints = + Self::combine_instruction_constraints_with_deselectors( + circuit_builder, + all_instruction_transition_constraints, + ); - instruction_bucket_polynomials - .into_iter() - .zip_eq(deselector_polynomials.into_iter()) - .map(|(bucket_poly, deselector_poly)| bucket_poly - deselector_poly) - .fold(one, ConstraintCircuitMonad::mul) - } + // if next row is padding row: disable transition constraints, enable padding constraints + let mut transition_constraints = + Self::combine_transition_constraints_with_padding_constraints( + circuit_builder, + deselected_transition_constraints, + ); - /// A polynomial that has no solutions when ci is 'instruction' - pub fn instruction_deselector( - factory: &DualRowConstraints, - instruction: Instruction, - ) -> ConstraintCircuitMonad { - let instruction_bucket_polynomials = [ - factory.ib0(), - factory.ib1(), - factory.ib2(), - factory.ib3(), - factory.ib4(), - factory.ib5(), - factory.ib6(), - factory.ib7(), - ]; + // constraints common to all instructions + let clk_always_increases_by_1 = next_base_row(CLK) - curr_base_row(CLK) - constant(1); + let is_padding_is_0_or_does_not_change = + curr_base_row(IsPadding) * (next_base_row(IsPadding) - curr_base_row(IsPadding)); + let previous_instruction_is_copied_correctly = (next_base_row(PreviousInstruction) + - curr_base_row(CI)) + * (constant(1) - next_base_row(IsPadding)); - Self::instruction_deselector_common_functionality( - &factory.circuit_builder, - instruction, - instruction_bucket_polynomials, - ) - } + transition_constraints.insert(0, clk_always_increases_by_1); + transition_constraints.insert(1, is_padding_is_0_or_does_not_change); + transition_constraints.insert(2, previous_instruction_is_copied_correctly); - /// A polynomial that has no solutions when ci is 'instruction' - pub fn instruction_deselector_single_row( - factory: &SingleRowConstraints, - instruction: Instruction, - ) -> ConstraintCircuitMonad { - let instruction_bucket_polynomials = [ - factory.ib0(), - factory.ib1(), - factory.ib2(), - factory.ib3(), - factory.ib4(), - factory.ib5(), - factory.ib6(), - factory.ib7(), - ]; + // constraints related to evaluation and permutation arguments + transition_constraints.push(Self::log_derivative_accumulates_clk_next(circuit_builder)); + transition_constraints + .push(Self::running_evaluation_for_standard_input_updates_correctly(circuit_builder)); + transition_constraints + .push(Self::log_derivative_for_instruction_lookup_updates_correctly(circuit_builder)); + transition_constraints + .push(Self::running_evaluation_for_standard_output_updates_correctly(circuit_builder)); + transition_constraints.push(Self::running_product_for_op_stack_table_updates_correctly( + circuit_builder, + )); + transition_constraints.push(Self::running_product_for_ram_table_updates_correctly( + circuit_builder, + )); + transition_constraints + .push(Self::running_product_for_jump_stack_table_updates_correctly(circuit_builder)); + transition_constraints.push(Self::running_evaluation_hash_input_updates_correctly( + circuit_builder, + )); + transition_constraints.push(Self::running_evaluation_hash_digest_updates_correctly( + circuit_builder, + )); + transition_constraints.push(Self::running_evaluation_sponge_updates_correctly( + circuit_builder, + )); + transition_constraints.push(Self::log_derivative_with_u32_table_updates_correctly( + circuit_builder, + )); - Self::instruction_deselector_common_functionality( - &factory.circuit_builder, - instruction, - instruction_bucket_polynomials, - ) + transition_constraints } - /// A polynomial that has no solutions when ci_next is 'instruction' - pub fn instruction_deselector_next( - factory: &DualRowConstraints, - instruction: Instruction, - ) -> ConstraintCircuitMonad { - let instruction_bucket_polynomials = [ - factory.ib0_next(), - factory.ib1_next(), - factory.ib2_next(), - factory.ib3_next(), - factory.ib4_next(), - factory.ib5_next(), - factory.ib6_next(), - factory.ib7_next(), - ]; + pub fn ext_terminal_constraints_as_circuits( + circuit_builder: &ConstraintCircuitBuilder, + ) -> Vec> { + let base_row = |col: ProcessorBaseTableColumn| { + circuit_builder.input(BaseRow(col.master_base_table_index())) + }; + let constant = |c| circuit_builder.b_constant(c); - Self::instruction_deselector_common_functionality( - &factory.circuit_builder, - instruction, - instruction_bucket_polynomials, - ) - } + // In the last row, register “current instruction” `ci` corresponds to instruction `halt`. + let last_ci_is_halt = base_row(CI) - constant(Instruction::Halt.opcode_b()); - pub fn create( - factory: &mut DualRowConstraints, - ) -> HashMap> { - ALL_INSTRUCTIONS - .iter() - .map(|&instr| (instr, Self::instruction_deselector(factory, instr))) - .collect() + vec![last_ci_is_halt] } } @@ -3173,6 +2952,7 @@ impl<'a> Display for ExtProcessorTraceRow<'a> { #[cfg(test)] mod constraint_polynomial_tests { use ndarray::Array2; + use triton_opcodes::ord_n::Ord16; use triton_opcodes::program::Program; @@ -3181,6 +2961,8 @@ mod constraint_polynomial_tests { use crate::shared_tests::SourceCodeAndInput; use crate::stark::triton_stark_tests::parse_simulate_pad; use crate::table::master_table::MasterTable; + use crate::table::master_table::NUM_BASE_COLUMNS; + use crate::table::master_table::NUM_EXT_COLUMNS; use crate::table::processor_table::ProcessorTraceRow; use crate::vm::simulate; @@ -3206,58 +2988,13 @@ mod constraint_polynomial_tests { .to_owned() } - fn get_transition_constraints_for_instruction( - instruction: Instruction, - ) -> Vec> { - let tc = DualRowConstraints::default(); - match instruction { - Pop => tc.instruction_pop(), - Push(_) => tc.instruction_push(), - Divine(_) => tc.instruction_divine(), - Dup(_) => tc.instruction_dup(), - Swap(_) => tc.instruction_swap(), - Nop => tc.instruction_nop(), - Skiz => tc.instruction_skiz(), - Call(_) => tc.instruction_call(), - Return => tc.instruction_return(), - Recurse => tc.instruction_recurse(), - Assert => tc.instruction_assert(), - Halt => tc.instruction_halt(), - ReadMem => tc.instruction_read_mem(), - WriteMem => tc.instruction_write_mem(), - Hash => tc.instruction_hash(), - DivineSibling => tc.instruction_divine_sibling(), - AssertVector => tc.instruction_assert_vector(), - AbsorbInit => tc.instruction_absorb_init(), - Absorb => tc.instruction_absorb(), - Squeeze => tc.instruction_squeeze(), - Add => tc.instruction_add(), - Mul => tc.instruction_mul(), - Invert => tc.instruction_invert(), - Eq => tc.instruction_eq(), - Split => tc.instruction_split(), - Lt => tc.instruction_lt(), - And => tc.instruction_and(), - Xor => tc.instruction_xor(), - Log2Floor => tc.instruction_log_2_floor(), - Pow => tc.instruction_pow(), - Div => tc.instruction_div(), - PopCount => tc.instruction_pop_count(), - XxAdd => tc.instruction_xxadd(), - XxMul => tc.instruction_xxmul(), - XInvert => tc.instruction_xinv(), - XbMul => tc.instruction_xbmul(), - ReadIo => tc.instruction_read_io(), - WriteIo => tc.instruction_write_io(), - } - } - fn test_constraints_for_rows_with_debug_info( instruction: Instruction, master_base_tables: &[Array2], debug_cols_curr_row: &[ProcessorBaseTableColumn], debug_cols_next_row: &[ProcessorBaseTableColumn], ) { + let circuit_builder = ConstraintCircuitBuilder::new(); let challenges = Challenges::placeholder(&[], &[]); let fake_ext_table = Array2::zeros([2, NUM_EXT_COLUMNS]); for (case_idx, test_rows) in master_base_tables.iter().enumerate() { @@ -3282,9 +3019,12 @@ mod constraint_polynomial_tests { "The test is trying to check the wrong transition constraint polynomials." ); for (constraint_idx, constraint_circuit) in - get_transition_constraints_for_instruction(instruction) - .into_iter() - .enumerate() + ExtProcessorTable::get_transition_constraints_for_instruction( + &circuit_builder, + instruction, + ) + .into_iter() + .enumerate() { let evaluation_result = constraint_circuit.consume().evaluate( test_rows.view(), @@ -3641,8 +3381,7 @@ mod constraint_polynomial_tests { #[test] fn instruction_deselector_gives_0_for_all_other_instructions_test() { - let mut factory = DualRowConstraints::default(); - let deselectors = InstructionDeselectors::new(&mut factory); + let circuit_builder = ConstraintCircuitBuilder::new(); let mut master_base_table = Array2::zeros([2, NUM_BASE_COLUMNS]); let master_ext_table = Array2::zeros([2, NUM_EXT_COLUMNS]); @@ -3651,7 +3390,10 @@ mod constraint_polynomial_tests { let dummy_challenges = Challenges::placeholder(&[], &[]); for instruction in ALL_INSTRUCTIONS { use ProcessorBaseTableColumn::*; - let deselector = deselectors.get(instruction); + let deselector = ExtProcessorTable::instruction_deselector_current_row( + &circuit_builder, + instruction, + ); println!("\n\nThe Deselector for instruction {instruction} is:\n{deselector}",); @@ -3708,53 +3450,15 @@ mod constraint_polynomial_tests { #[test] fn print_number_and_degrees_of_transition_constraints_for_all_instructions() { - let factory = DualRowConstraints::default(); - let all_instructions_and_their_transition_constraints: [(Instruction, _); - Instruction::COUNT] = [ - (Pop, factory.instruction_pop()), - (Push(Default::default()), factory.instruction_push()), - (Divine(Default::default()), factory.instruction_divine()), - (Dup(Default::default()), factory.instruction_dup()), - (Swap(Default::default()), factory.instruction_swap()), - (Nop, factory.instruction_nop()), - (Skiz, factory.instruction_skiz()), - (Call(Default::default()), factory.instruction_call()), - (Return, factory.instruction_return()), - (Recurse, factory.instruction_recurse()), - (Assert, factory.instruction_assert()), - (Halt, factory.instruction_halt()), - (ReadMem, factory.instruction_read_mem()), - (WriteMem, factory.instruction_write_mem()), - (Hash, factory.instruction_hash()), - (DivineSibling, factory.instruction_divine_sibling()), - (AssertVector, factory.instruction_assert_vector()), - (AbsorbInit, factory.instruction_absorb_init()), - (Absorb, factory.instruction_absorb()), - (Squeeze, factory.instruction_squeeze()), - (Add, factory.instruction_add()), - (Mul, factory.instruction_mul()), - (Invert, factory.instruction_invert()), - (Eq, factory.instruction_eq()), - (Split, factory.instruction_split()), - (Lt, factory.instruction_lt()), - (And, factory.instruction_and()), - (Xor, factory.instruction_xor()), - (Log2Floor, factory.instruction_log_2_floor()), - (Pow, factory.instruction_pow()), - (Div, factory.instruction_div()), - (PopCount, factory.instruction_pop_count()), - (XxAdd, factory.instruction_xxadd()), - (XxMul, factory.instruction_xxmul()), - (XInvert, factory.instruction_xinv()), - (XbMul, factory.instruction_xbmul()), - (ReadIo, factory.instruction_read_io()), - (WriteIo, factory.instruction_write_io()), - ]; - println!(); println!("| Instruction | #polys | max deg | Degrees"); println!("|:----------------|-------:|--------:|:------------"); - for (instruction, constraints) in all_instructions_and_their_transition_constraints { + let circuit_builder = ConstraintCircuitBuilder::new(); + for instruction in ALL_INSTRUCTIONS { + let constraints = ExtProcessorTable::get_transition_constraints_for_instruction( + &circuit_builder, + instruction, + ); let degrees = constraints .iter() .map(|circuit| circuit.clone().consume().degree()) From 84270d6a514c5bde7b9a6bb6dde999d608dda827 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Sun, 7 May 2023 23:45:37 +0200 Subject: [PATCH 10/72] clean up transition constraint building in Processor Table --- triton-vm/src/table/processor_table.rs | 80 ++++++++++++-------------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/triton-vm/src/table/processor_table.rs b/triton-vm/src/table/processor_table.rs index 68c90c8a..e472c708 100644 --- a/triton-vm/src/table/processor_table.rs +++ b/triton-vm/src/table/processor_table.rs @@ -2677,11 +2677,25 @@ impl ExtProcessorTable { circuit_builder.input(NextBaseRow(col.master_base_table_index())) }; + // constraints common to all instructions + let clk_increases_by_1 = next_base_row(CLK) - curr_base_row(CLK) - constant(1); + let is_padding_is_0_or_does_not_change = + curr_base_row(IsPadding) * (next_base_row(IsPadding) - curr_base_row(IsPadding)); + let previous_instruction_is_copied_correctly = (next_base_row(PreviousInstruction) + - curr_base_row(CI)) + * (constant(1) - next_base_row(IsPadding)); + + let instruction_independent_constraints = vec![ + clk_increases_by_1, + is_padding_is_0_or_does_not_change, + previous_instruction_is_copied_correctly, + ]; + // instruction-specific constraints let all_transition_constraints_by_instruction = ALL_INSTRUCTIONS.map(|instruction| { Self::get_transition_constraints_for_instruction(circuit_builder, instruction) }); - let all_instruction_transition_constraints = ALL_INSTRUCTIONS + let all_instructions_and_their_transition_constraints = ALL_INSTRUCTIONS .into_iter() .zip_eq(all_transition_constraints_by_instruction.into_iter()) .collect_vec() @@ -2691,58 +2705,36 @@ impl ExtProcessorTable { let deselected_transition_constraints = Self::combine_instruction_constraints_with_deselectors( circuit_builder, - all_instruction_transition_constraints, + all_instructions_and_their_transition_constraints, ); // if next row is padding row: disable transition constraints, enable padding constraints - let mut transition_constraints = + let doubly_deselected_transition_constraints = Self::combine_transition_constraints_with_padding_constraints( circuit_builder, deselected_transition_constraints, ); - // constraints common to all instructions - let clk_always_increases_by_1 = next_base_row(CLK) - curr_base_row(CLK) - constant(1); - let is_padding_is_0_or_does_not_change = - curr_base_row(IsPadding) * (next_base_row(IsPadding) - curr_base_row(IsPadding)); - let previous_instruction_is_copied_correctly = (next_base_row(PreviousInstruction) - - curr_base_row(CI)) - * (constant(1) - next_base_row(IsPadding)); - - transition_constraints.insert(0, clk_always_increases_by_1); - transition_constraints.insert(1, is_padding_is_0_or_does_not_change); - transition_constraints.insert(2, previous_instruction_is_copied_correctly); - - // constraints related to evaluation and permutation arguments - transition_constraints.push(Self::log_derivative_accumulates_clk_next(circuit_builder)); - transition_constraints - .push(Self::running_evaluation_for_standard_input_updates_correctly(circuit_builder)); - transition_constraints - .push(Self::log_derivative_for_instruction_lookup_updates_correctly(circuit_builder)); - transition_constraints - .push(Self::running_evaluation_for_standard_output_updates_correctly(circuit_builder)); - transition_constraints.push(Self::running_product_for_op_stack_table_updates_correctly( - circuit_builder, - )); - transition_constraints.push(Self::running_product_for_ram_table_updates_correctly( - circuit_builder, - )); - transition_constraints - .push(Self::running_product_for_jump_stack_table_updates_correctly(circuit_builder)); - transition_constraints.push(Self::running_evaluation_hash_input_updates_correctly( - circuit_builder, - )); - transition_constraints.push(Self::running_evaluation_hash_digest_updates_correctly( - circuit_builder, - )); - transition_constraints.push(Self::running_evaluation_sponge_updates_correctly( - circuit_builder, - )); - transition_constraints.push(Self::log_derivative_with_u32_table_updates_correctly( - circuit_builder, - )); + let table_linking_constraints = vec![ + Self::log_derivative_accumulates_clk_next(circuit_builder), + Self::running_evaluation_for_standard_input_updates_correctly(circuit_builder), + Self::log_derivative_for_instruction_lookup_updates_correctly(circuit_builder), + Self::running_evaluation_for_standard_output_updates_correctly(circuit_builder), + Self::running_product_for_op_stack_table_updates_correctly(circuit_builder), + Self::running_product_for_ram_table_updates_correctly(circuit_builder), + Self::running_product_for_jump_stack_table_updates_correctly(circuit_builder), + Self::running_evaluation_hash_input_updates_correctly(circuit_builder), + Self::running_evaluation_hash_digest_updates_correctly(circuit_builder), + Self::running_evaluation_sponge_updates_correctly(circuit_builder), + Self::log_derivative_with_u32_table_updates_correctly(circuit_builder), + ]; - transition_constraints + vec![ + instruction_independent_constraints, + doubly_deselected_transition_constraints, + table_linking_constraints, + ] + .concat() } pub fn ext_terminal_constraints_as_circuits( From 76cd029c4e63061a06d8f32617bc7d4b793f558e Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Mon, 8 May 2023 10:07:07 +0200 Subject: [PATCH 11/72] simplify testing of circuit and sub-circuit uniqueness --- constraint-evaluation-generator/src/main.rs | 2 + triton-vm/src/table/constraint_circuit.rs | 360 +++++++++----------- 2 files changed, 156 insertions(+), 206 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 2f4239a8..4ff9898e 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -139,6 +139,8 @@ fn main() { } } +/// Get the constraints defined in the given function, perform constant folding, and return +/// them as a vector of `ConstraintCircuit`s. fn build_fold_circuitify( circuit_monad_function: &dyn Fn( &ConstraintCircuitBuilder, diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index a0152b59..3f367df9 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -546,97 +546,6 @@ impl ConstraintCircuit { } } - /// Panics if two nodes evaluate to the same value - pub fn assert_all_evaluate_different( - constraints: &[Self], - challenges: &Challenges, - base_table: ArrayView2, - ext_table: ArrayView2, - ) { - let mut evaluated_values = HashMap::default(); - for constraint in constraints.iter() { - Self::evaluate_and_store_and_assert_unique( - constraint, - challenges, - base_table, - ext_table, - &mut evaluated_values, - ); - } - } - - /// Return own value and whether own value was seen before. Stores own value in hash map. - fn evaluate_and_store_and_assert_unique( - &self, - challenges: &Challenges, - base_table: ArrayView2, - ext_table: ArrayView2, - evaluated_values: &mut HashMap)>, - ) -> XFieldElement { - // assert_eq!( - // self.var_count, - // input.len(), - // "Input length match circuit's var count" - // ); - let value = match &self.expression { - XConstant(xfe) => { - if self.id == 107 || self.id == 454 { - println!("{}: XFE", self.id); - } - xfe.to_owned() - } - BConstant(bfe) => { - if self.id == 107 || self.id == 454 { - println!("{}: BFE", self.id); - } - bfe.lift() - } - Input(s) => { - s.evaluate(base_table, ext_table) - // if s.is_base_table_row() { - // base_input[s.base_row_index()].lift() - // } else { - // ext_input[s.ext_row_index()] - // } - } - Challenge(cid) => challenges.get_challenge(*cid), - BinaryOperation(binop, lhs, rhs) => { - let lhs = lhs.as_ref().borrow().evaluate_and_store_and_assert_unique( - challenges, - base_table, - ext_table, - evaluated_values, - ); - let rhs = rhs.as_ref().borrow().evaluate_and_store_and_assert_unique( - challenges, - base_table, - ext_table, - evaluated_values, - ); - match binop { - BinOp::Add => lhs + rhs, - BinOp::Sub => lhs - rhs, - BinOp::Mul => lhs * rhs, - } - } - }; - - let self_evaluated_is_unique = - evaluated_values.insert(value, (self.id.to_owned(), self.clone())); - if let Some((collided_circuit_id, collided_circuit)) = self_evaluated_is_unique { - let own_id = self.id.to_owned(); - if collided_circuit_id != self.id { - panic!( - "Circuit ID {collided_circuit_id} and circuit ID {own_id} are not unique. \ - Collission on:\n \ - {collided_circuit_id}: {collided_circuit}\n {own_id}: {self}. \ - Value was {value}", - ); - } - } - value - } - pub fn evaluate( &self, base_table: ArrayView2, @@ -1159,6 +1068,7 @@ mod constraint_circuit_tests { use std::collections::hash_map::DefaultHasher; use std::hash::Hasher; + use itertools::Itertools; use ndarray::Array2; use rand::random; use rand::thread_rng; @@ -1641,45 +1551,96 @@ mod constraint_circuit_tests { } } - /// Get the constraints defined in the given function, perform constant folding, and return - /// them as a vector of `ConstraintCircuit`s. - fn build_fold_circuitify( - circuit_monad_function: &dyn Fn( - &ConstraintCircuitBuilder, - ) -> Vec>, - ) -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); - let mut constraints = circuit_monad_function(&circuit_builder); - ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints - .into_iter() - .map(|circuit| circuit.consume()) - .collect() + /// Recursively evaluates the given constraint circuit and its sub-circuits on the given + /// base and extension table, and returns the result of the evaluation. + /// At each recursive step, updates the given HashMap with the result of the evaluation. + /// If the HashMap already contains the result of the evaluation, panics. + /// This function is used to assert that the evaluation of a constraint circuit + /// and its sub-circuits is unique. + /// It is used to identify redundant constraints or sub-circuits. + /// The employed method is the Schwartz-Zippel lemma. + fn evaluate_and_assert_uniqueness( + constraint: &ConstraintCircuit, + challenges: &Challenges, + base_table: ArrayView2, + ext_table: ArrayView2, + evaluated_values: &mut HashMap)>, + ) -> XFieldElement { + let value = match &constraint.expression { + BConstant(bfe) => bfe.lift(), + XConstant(xfe) => xfe.to_owned(), + Input(s) => s.evaluate(base_table, ext_table), + Challenge(cid) => challenges.get_challenge(*cid), + BinaryOperation(binop, lhs, rhs) => { + let lhs = evaluate_and_assert_uniqueness( + &lhs.as_ref().borrow(), + challenges, + base_table, + ext_table, + evaluated_values, + ); + let rhs = evaluate_and_assert_uniqueness( + &rhs.as_ref().borrow(), + challenges, + base_table, + ext_table, + evaluated_values, + ); + match binop { + BinOp::Add => lhs + rhs, + BinOp::Sub => lhs - rhs, + BinOp::Mul => lhs * rhs, + } + } + }; + + let own_id = constraint.id.to_owned(); + let maybe_entry = evaluated_values.insert(value, (own_id, constraint.clone())); + if let Some((collided_circuit_id, collided_circuit)) = maybe_entry { + if collided_circuit_id != own_id { + panic!( + "Circuit ID {collided_circuit_id} and circuit ID {own_id} are not unique. \ + Collision on:\n\ + ID {collided_circuit_id} – {collided_circuit}\n\ + ID {own_id} – {constraint}\n\ + Value was {value}.", + ); + } + } + + value } /// Verify that all nodes evaluate to a unique value when given a randomized input. /// If this is not the case two nodes that are not equal evaluate to the same value. fn table_constraints_prop( - mut constraints: Vec>, - challenges: &Challenges, + constraint_builder: &dyn Fn( + &ConstraintCircuitBuilder, + ) -> Vec>, table_name: &str, ) { + let challenges = Challenges::placeholder(&[], &[]); + let circuit_builder = ConstraintCircuitBuilder::new(); + let mut constraints = constraint_builder(&circuit_builder); + ConstraintCircuitMonad::constant_folding(&mut constraints); + let mut constraints = constraints.into_iter().map(|c| c.consume()).collect_vec(); ConstraintCircuit::assert_has_unique_ids(&mut constraints); - let base_table = Array2::from_shape_simple_fn( - [2, master_table::NUM_BASE_COLUMNS], - random::, - ); - let ext_table = Array2::from_shape_simple_fn( - [2, master_table::NUM_EXT_COLUMNS], - random::, - ); - ConstraintCircuit::assert_all_evaluate_different( - &constraints, - challenges, - base_table.view(), - ext_table.view(), - ); + let base_table_shape = [2, master_table::NUM_BASE_COLUMNS]; + let ext_table_shape = [2, master_table::NUM_EXT_COLUMNS]; + let base_table = Array2::from_shape_simple_fn(base_table_shape, random::); + let ext_table = Array2::from_shape_simple_fn(ext_table_shape, random::); + + let mut evaluated_values = HashMap::new(); + for constraint in constraints.iter() { + evaluate_and_assert_uniqueness( + constraint, + &challenges, + base_table.view(), + ext_table.view(), + &mut evaluated_values, + ); + } println!("nodes in {table_name}: {}", node_counter(&mut constraints)); let circuit_degree = constraints.iter().map(|c| c.degree()).max().unwrap_or(-1); @@ -1688,122 +1649,109 @@ mod constraint_circuit_tests { #[test] fn constant_folding_processor_table_test() { - let challenges = Challenges::placeholder(&[], &[]); - let init = build_fold_circuitify(&ExtProcessorTable::ext_initial_constraints_as_circuits); - let cons = - build_fold_circuitify(&ExtProcessorTable::ext_consistency_constraints_as_circuits); - let tran = - build_fold_circuitify(&ExtProcessorTable::ext_transition_constraints_as_circuits); - let term = build_fold_circuitify(&ExtProcessorTable::ext_terminal_constraints_as_circuits); - table_constraints_prop(init, &challenges, "processor initial"); - table_constraints_prop(cons, &challenges, "processor consistency"); - table_constraints_prop(tran, &challenges, "processor transition"); - table_constraints_prop(term, &challenges, "processor terminal"); + let init = ExtProcessorTable::ext_initial_constraints_as_circuits; + let cons = ExtProcessorTable::ext_consistency_constraints_as_circuits; + let tran = ExtProcessorTable::ext_transition_constraints_as_circuits; + let term = ExtProcessorTable::ext_terminal_constraints_as_circuits; + table_constraints_prop(&init, "processor initial"); + table_constraints_prop(&cons, "processor consistency"); + table_constraints_prop(&tran, "processor transition"); + table_constraints_prop(&term, "processor terminal"); } #[test] fn constant_folding_program_table_test() { - let challenges = Challenges::placeholder(&[], &[]); - let init = build_fold_circuitify(&ExtProgramTable::ext_initial_constraints_as_circuits); - let cons = build_fold_circuitify(&ExtProgramTable::ext_consistency_constraints_as_circuits); - let tran = build_fold_circuitify(&ExtProgramTable::ext_transition_constraints_as_circuits); - let term = build_fold_circuitify(&ExtProgramTable::ext_terminal_constraints_as_circuits); - table_constraints_prop(init, &challenges, "program initial"); - table_constraints_prop(cons, &challenges, "program consistency"); - table_constraints_prop(tran, &challenges, "program transition"); - table_constraints_prop(term, &challenges, "program terminal"); + let init = ExtProgramTable::ext_initial_constraints_as_circuits; + let cons = ExtProgramTable::ext_consistency_constraints_as_circuits; + let tran = ExtProgramTable::ext_transition_constraints_as_circuits; + let term = ExtProgramTable::ext_terminal_constraints_as_circuits; + table_constraints_prop(&init, "program initial"); + table_constraints_prop(&cons, "program consistency"); + table_constraints_prop(&tran, "program transition"); + table_constraints_prop(&term, "program terminal"); } #[test] fn constant_folding_jump_stack_table_test() { - let challenges = Challenges::placeholder(&[], &[]); - let init = build_fold_circuitify(&ExtJumpStackTable::ext_initial_constraints_as_circuits); - let cons = - build_fold_circuitify(&ExtJumpStackTable::ext_consistency_constraints_as_circuits); - let tran = - build_fold_circuitify(&ExtJumpStackTable::ext_transition_constraints_as_circuits); - let term = build_fold_circuitify(&ExtJumpStackTable::ext_terminal_constraints_as_circuits); - table_constraints_prop(init, &challenges, "jump stack initial"); - table_constraints_prop(cons, &challenges, "jump stack consistency"); - table_constraints_prop(tran, &challenges, "jump stack transition"); - table_constraints_prop(term, &challenges, "jump stack terminal"); + let init = ExtJumpStackTable::ext_initial_constraints_as_circuits; + let cons = ExtJumpStackTable::ext_consistency_constraints_as_circuits; + let tran = ExtJumpStackTable::ext_transition_constraints_as_circuits; + let term = ExtJumpStackTable::ext_terminal_constraints_as_circuits; + table_constraints_prop(&init, "jump stack initial"); + table_constraints_prop(&cons, "jump stack consistency"); + table_constraints_prop(&tran, "jump stack transition"); + table_constraints_prop(&term, "jump stack terminal"); } #[test] fn constant_folding_op_stack_table_test() { - let challenges = Challenges::placeholder(&[], &[]); - let init = build_fold_circuitify(&ExtOpStackTable::ext_initial_constraints_as_circuits); - let cons = build_fold_circuitify(&ExtOpStackTable::ext_consistency_constraints_as_circuits); - let tran = build_fold_circuitify(&ExtOpStackTable::ext_transition_constraints_as_circuits); - let term = build_fold_circuitify(&ExtOpStackTable::ext_terminal_constraints_as_circuits); - table_constraints_prop(init, &challenges, "op stack initial"); - table_constraints_prop(cons, &challenges, "op stack consistency"); - table_constraints_prop(tran, &challenges, "op stack transition"); - table_constraints_prop(term, &challenges, "op stack terminal"); + let init = ExtOpStackTable::ext_initial_constraints_as_circuits; + let cons = ExtOpStackTable::ext_consistency_constraints_as_circuits; + let tran = ExtOpStackTable::ext_transition_constraints_as_circuits; + let term = ExtOpStackTable::ext_terminal_constraints_as_circuits; + table_constraints_prop(&init, "op stack initial"); + table_constraints_prop(&cons, "op stack consistency"); + table_constraints_prop(&tran, "op stack transition"); + table_constraints_prop(&term, "op stack terminal"); } #[test] fn constant_folding_ram_table_test() { - let challenges = Challenges::placeholder(&[], &[]); - let init = build_fold_circuitify(&ExtRamTable::ext_initial_constraints_as_circuits); - let cons = build_fold_circuitify(&ExtRamTable::ext_consistency_constraints_as_circuits); - let tran = build_fold_circuitify(&ExtRamTable::ext_transition_constraints_as_circuits); - let term = build_fold_circuitify(&ExtRamTable::ext_terminal_constraints_as_circuits); - table_constraints_prop(init, &challenges, "ram initial"); - table_constraints_prop(cons, &challenges, "ram consistency"); - table_constraints_prop(tran, &challenges, "ram transition"); - table_constraints_prop(term, &challenges, "ram terminal"); + let init = ExtRamTable::ext_initial_constraints_as_circuits; + let cons = ExtRamTable::ext_consistency_constraints_as_circuits; + let tran = ExtRamTable::ext_transition_constraints_as_circuits; + let term = ExtRamTable::ext_terminal_constraints_as_circuits; + table_constraints_prop(&init, "ram initial"); + table_constraints_prop(&cons, "ram consistency"); + table_constraints_prop(&tran, "ram transition"); + table_constraints_prop(&term, "ram terminal"); } #[test] fn constant_folding_hash_table_test() { - let challenges = Challenges::placeholder(&[], &[]); - let init = build_fold_circuitify(&ExtHashTable::ext_initial_constraints_as_circuits); - let cons = build_fold_circuitify(&ExtHashTable::ext_consistency_constraints_as_circuits); - let tran = build_fold_circuitify(&ExtHashTable::ext_transition_constraints_as_circuits); - let term = build_fold_circuitify(&ExtHashTable::ext_terminal_constraints_as_circuits); - table_constraints_prop(init, &challenges, "hash initial"); - table_constraints_prop(cons, &challenges, "hash consistency"); - table_constraints_prop(tran, &challenges, "hash transition"); - table_constraints_prop(term, &challenges, "hash terminal"); + let init = ExtHashTable::ext_initial_constraints_as_circuits; + let cons = ExtHashTable::ext_consistency_constraints_as_circuits; + let tran = ExtHashTable::ext_transition_constraints_as_circuits; + let term = ExtHashTable::ext_terminal_constraints_as_circuits; + table_constraints_prop(&init, "hash initial"); + table_constraints_prop(&cons, "hash consistency"); + table_constraints_prop(&tran, "hash transition"); + table_constraints_prop(&term, "hash terminal"); } #[test] fn constant_folding_u32_table_test() { - let challenges = Challenges::placeholder(&[], &[]); - let init = build_fold_circuitify(&ExtU32Table::ext_initial_constraints_as_circuits); - let cons = build_fold_circuitify(&ExtU32Table::ext_consistency_constraints_as_circuits); - let tran = build_fold_circuitify(&ExtU32Table::ext_transition_constraints_as_circuits); - let term = build_fold_circuitify(&ExtU32Table::ext_terminal_constraints_as_circuits); - table_constraints_prop(init, &challenges, "u32 initial"); - table_constraints_prop(cons, &challenges, "u32 consistency"); - table_constraints_prop(tran, &challenges, "u32 transition"); - table_constraints_prop(term, &challenges, "u32 terminal"); + let init = ExtU32Table::ext_initial_constraints_as_circuits; + let cons = ExtU32Table::ext_consistency_constraints_as_circuits; + let tran = ExtU32Table::ext_transition_constraints_as_circuits; + let term = ExtU32Table::ext_terminal_constraints_as_circuits; + table_constraints_prop(&init, "u32 initial"); + table_constraints_prop(&cons, "u32 consistency"); + table_constraints_prop(&tran, "u32 transition"); + table_constraints_prop(&term, "u32 terminal"); } #[test] fn constant_folding_cascade_table_test() { - let challenges = Challenges::placeholder(&[], &[]); - let init = build_fold_circuitify(&ExtCascadeTable::ext_initial_constraints_as_circuits); - let cons = build_fold_circuitify(&ExtCascadeTable::ext_consistency_constraints_as_circuits); - let tran = build_fold_circuitify(&ExtCascadeTable::ext_transition_constraints_as_circuits); - let term = build_fold_circuitify(&ExtCascadeTable::ext_terminal_constraints_as_circuits); - table_constraints_prop(init, &challenges, "cascade initial"); - table_constraints_prop(cons, &challenges, "cascade consistency"); - table_constraints_prop(tran, &challenges, "cascade transition"); - table_constraints_prop(term, &challenges, "cascade terminal"); + let init = ExtCascadeTable::ext_initial_constraints_as_circuits; + let cons = ExtCascadeTable::ext_consistency_constraints_as_circuits; + let tran = ExtCascadeTable::ext_transition_constraints_as_circuits; + let term = ExtCascadeTable::ext_terminal_constraints_as_circuits; + table_constraints_prop(&init, "cascade initial"); + table_constraints_prop(&cons, "cascade consistency"); + table_constraints_prop(&tran, "cascade transition"); + table_constraints_prop(&term, "cascade terminal"); } #[test] fn constant_folding_lookup_table_test() { - let challenges = Challenges::placeholder(&[], &[]); - let init = build_fold_circuitify(&ExtLookupTable::ext_initial_constraints_as_circuits); - let cons = build_fold_circuitify(&ExtLookupTable::ext_consistency_constraints_as_circuits); - let tran = build_fold_circuitify(&ExtLookupTable::ext_transition_constraints_as_circuits); - let term = build_fold_circuitify(&ExtLookupTable::ext_terminal_constraints_as_circuits); - table_constraints_prop(init, &challenges, "lookup initial"); - table_constraints_prop(cons, &challenges, "lookup consistency"); - table_constraints_prop(tran, &challenges, "lookup transition"); - table_constraints_prop(term, &challenges, "lookup terminal"); + let init = ExtLookupTable::ext_initial_constraints_as_circuits; + let cons = ExtLookupTable::ext_consistency_constraints_as_circuits; + let tran = ExtLookupTable::ext_transition_constraints_as_circuits; + let term = ExtLookupTable::ext_terminal_constraints_as_circuits; + table_constraints_prop(&init, "lookup initial"); + table_constraints_prop(&cons, "lookup consistency"); + table_constraints_prop(&tran, "lookup transition"); + table_constraints_prop(&term, "lookup terminal"); } } From 633acc4cb84bec9199544933fcc3a2e2a095a51d Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Mon, 8 May 2023 10:33:52 +0200 Subject: [PATCH 12/72] rename `ext_(.*)_constraints_as_circuits` to `$1_constraints` --- constraint-evaluation-generator/src/main.rs | 80 ++++++++++----------- triton-vm/src/table/cascade_table.rs | 8 +-- triton-vm/src/table/constraint_circuit.rs | 72 +++++++++---------- triton-vm/src/table/cross_table_argument.rs | 8 +-- triton-vm/src/table/hash_table.rs | 8 +-- triton-vm/src/table/jump_stack_table.rs | 8 +-- triton-vm/src/table/lookup_table.rs | 8 +-- triton-vm/src/table/op_stack_table.rs | 8 +-- triton-vm/src/table/processor_table.rs | 8 +-- triton-vm/src/table/program_table.rs | 8 +-- triton-vm/src/table/ram_table.rs | 8 +-- triton-vm/src/table/u32_table.rs | 8 +-- 12 files changed, 116 insertions(+), 116 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 4ff9898e..8c7d3385 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -26,10 +26,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut build_fold_circuitify(&ExtProgramTable::ext_initial_constraints_as_circuits), - &mut build_fold_circuitify(&ExtProgramTable::ext_consistency_constraints_as_circuits), - &mut build_fold_circuitify(&ExtProgramTable::ext_transition_constraints_as_circuits), - &mut build_fold_circuitify(&ExtProgramTable::ext_terminal_constraints_as_circuits), + &mut build_fold_circuitify(&ExtProgramTable::initial_constraints), + &mut build_fold_circuitify(&ExtProgramTable::consistency_constraints), + &mut build_fold_circuitify(&ExtProgramTable::transition_constraints), + &mut build_fold_circuitify(&ExtProgramTable::terminal_constraints), ); write(&table_name_snake, source_code); @@ -37,10 +37,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut build_fold_circuitify(&ExtProcessorTable::ext_initial_constraints_as_circuits), - &mut build_fold_circuitify(&ExtProcessorTable::ext_consistency_constraints_as_circuits), - &mut build_fold_circuitify(&ExtProcessorTable::ext_transition_constraints_as_circuits), - &mut build_fold_circuitify(&ExtProcessorTable::ext_terminal_constraints_as_circuits), + &mut build_fold_circuitify(&ExtProcessorTable::initial_constraints), + &mut build_fold_circuitify(&ExtProcessorTable::consistency_constraints), + &mut build_fold_circuitify(&ExtProcessorTable::transition_constraints), + &mut build_fold_circuitify(&ExtProcessorTable::terminal_constraints), ); write(&table_name_snake, source_code); @@ -48,10 +48,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut build_fold_circuitify(&ExtOpStackTable::ext_initial_constraints_as_circuits), - &mut build_fold_circuitify(&ExtOpStackTable::ext_consistency_constraints_as_circuits), - &mut build_fold_circuitify(&ExtOpStackTable::ext_transition_constraints_as_circuits), - &mut build_fold_circuitify(&ExtOpStackTable::ext_terminal_constraints_as_circuits), + &mut build_fold_circuitify(&ExtOpStackTable::initial_constraints), + &mut build_fold_circuitify(&ExtOpStackTable::consistency_constraints), + &mut build_fold_circuitify(&ExtOpStackTable::transition_constraints), + &mut build_fold_circuitify(&ExtOpStackTable::terminal_constraints), ); write(&table_name_snake, source_code); @@ -59,10 +59,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut build_fold_circuitify(&ExtRamTable::ext_initial_constraints_as_circuits), - &mut build_fold_circuitify(&ExtRamTable::ext_consistency_constraints_as_circuits), - &mut build_fold_circuitify(&ExtRamTable::ext_transition_constraints_as_circuits), - &mut build_fold_circuitify(&ExtRamTable::ext_terminal_constraints_as_circuits), + &mut build_fold_circuitify(&ExtRamTable::initial_constraints), + &mut build_fold_circuitify(&ExtRamTable::consistency_constraints), + &mut build_fold_circuitify(&ExtRamTable::transition_constraints), + &mut build_fold_circuitify(&ExtRamTable::terminal_constraints), ); write(&table_name_snake, source_code); @@ -71,10 +71,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut build_fold_circuitify(&ExtJumpStackTable::ext_initial_constraints_as_circuits), - &mut build_fold_circuitify(&ExtJumpStackTable::ext_consistency_constraints_as_circuits), - &mut build_fold_circuitify(&ExtJumpStackTable::ext_transition_constraints_as_circuits), - &mut build_fold_circuitify(&ExtJumpStackTable::ext_terminal_constraints_as_circuits), + &mut build_fold_circuitify(&ExtJumpStackTable::initial_constraints), + &mut build_fold_circuitify(&ExtJumpStackTable::consistency_constraints), + &mut build_fold_circuitify(&ExtJumpStackTable::transition_constraints), + &mut build_fold_circuitify(&ExtJumpStackTable::terminal_constraints), ); write(&table_name_snake, source_code); @@ -82,10 +82,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut build_fold_circuitify(&ExtHashTable::ext_initial_constraints_as_circuits), - &mut build_fold_circuitify(&ExtHashTable::ext_consistency_constraints_as_circuits), - &mut build_fold_circuitify(&ExtHashTable::ext_transition_constraints_as_circuits), - &mut build_fold_circuitify(&ExtHashTable::ext_terminal_constraints_as_circuits), + &mut build_fold_circuitify(&ExtHashTable::initial_constraints), + &mut build_fold_circuitify(&ExtHashTable::consistency_constraints), + &mut build_fold_circuitify(&ExtHashTable::transition_constraints), + &mut build_fold_circuitify(&ExtHashTable::terminal_constraints), ); write(&table_name_snake, source_code); @@ -93,10 +93,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut build_fold_circuitify(&ExtCascadeTable::ext_initial_constraints_as_circuits), - &mut build_fold_circuitify(&ExtCascadeTable::ext_consistency_constraints_as_circuits), - &mut build_fold_circuitify(&ExtCascadeTable::ext_transition_constraints_as_circuits), - &mut build_fold_circuitify(&ExtCascadeTable::ext_terminal_constraints_as_circuits), + &mut build_fold_circuitify(&ExtCascadeTable::initial_constraints), + &mut build_fold_circuitify(&ExtCascadeTable::consistency_constraints), + &mut build_fold_circuitify(&ExtCascadeTable::transition_constraints), + &mut build_fold_circuitify(&ExtCascadeTable::terminal_constraints), ); write(&table_name_snake, source_code); @@ -104,10 +104,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut build_fold_circuitify(&ExtLookupTable::ext_initial_constraints_as_circuits), - &mut build_fold_circuitify(&ExtLookupTable::ext_consistency_constraints_as_circuits), - &mut build_fold_circuitify(&ExtLookupTable::ext_transition_constraints_as_circuits), - &mut build_fold_circuitify(&ExtLookupTable::ext_terminal_constraints_as_circuits), + &mut build_fold_circuitify(&ExtLookupTable::initial_constraints), + &mut build_fold_circuitify(&ExtLookupTable::consistency_constraints), + &mut build_fold_circuitify(&ExtLookupTable::transition_constraints), + &mut build_fold_circuitify(&ExtLookupTable::terminal_constraints), ); write(&table_name_snake, source_code); @@ -115,10 +115,10 @@ fn main() { let source_code = gen( &table_name_snake, &table_name_camel, - &mut build_fold_circuitify(&ExtU32Table::ext_initial_constraints_as_circuits), - &mut build_fold_circuitify(&ExtU32Table::ext_consistency_constraints_as_circuits), - &mut build_fold_circuitify(&ExtU32Table::ext_transition_constraints_as_circuits), - &mut build_fold_circuitify(&ExtU32Table::ext_terminal_constraints_as_circuits), + &mut build_fold_circuitify(&ExtU32Table::initial_constraints), + &mut build_fold_circuitify(&ExtU32Table::consistency_constraints), + &mut build_fold_circuitify(&ExtU32Table::transition_constraints), + &mut build_fold_circuitify(&ExtU32Table::terminal_constraints), ); write(&table_name_snake, source_code); @@ -127,10 +127,10 @@ fn main() { let source_code = gen( table_name_snake, table_name_camel, - &mut build_fold_circuitify(&GrandCrossTableArg::ext_initial_constraints_as_circuits), - &mut build_fold_circuitify(&GrandCrossTableArg::ext_consistency_constraints_as_circuits), - &mut build_fold_circuitify(&GrandCrossTableArg::ext_transition_constraints_as_circuits), - &mut build_fold_circuitify(&GrandCrossTableArg::ext_terminal_constraints_as_circuits), + &mut build_fold_circuitify(&GrandCrossTableArg::initial_constraints), + &mut build_fold_circuitify(&GrandCrossTableArg::consistency_constraints), + &mut build_fold_circuitify(&GrandCrossTableArg::transition_constraints), + &mut build_fold_circuitify(&GrandCrossTableArg::terminal_constraints), ); write(table_name_snake, source_code); diff --git a/triton-vm/src/table/cascade_table.rs b/triton-vm/src/table/cascade_table.rs index f1263538..2c6f1871 100644 --- a/triton-vm/src/table/cascade_table.rs +++ b/triton-vm/src/table/cascade_table.rs @@ -134,7 +134,7 @@ impl CascadeTable { } impl ExtCascadeTable { - pub fn ext_initial_constraints_as_circuits( + pub fn initial_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let base_row = |col_id: CascadeBaseTableColumn| { @@ -205,7 +205,7 @@ impl ExtCascadeTable { ] } - pub fn ext_consistency_constraints_as_circuits( + pub fn consistency_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let base_row = |col_id: CascadeBaseTableColumn| { @@ -219,7 +219,7 @@ impl ExtCascadeTable { vec![is_padding_is_0_or_1] } - pub fn ext_transition_constraints_as_circuits( + pub fn transition_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let challenge = |c| circuit_builder.challenge(c); @@ -307,7 +307,7 @@ impl ExtCascadeTable { ] } - pub fn ext_terminal_constraints_as_circuits( + pub fn terminal_constraints( _circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { // no further constraints diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 3f367df9..66b54b9b 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -1649,10 +1649,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_processor_table_test() { - let init = ExtProcessorTable::ext_initial_constraints_as_circuits; - let cons = ExtProcessorTable::ext_consistency_constraints_as_circuits; - let tran = ExtProcessorTable::ext_transition_constraints_as_circuits; - let term = ExtProcessorTable::ext_terminal_constraints_as_circuits; + let init = ExtProcessorTable::initial_constraints; + let cons = ExtProcessorTable::consistency_constraints; + let tran = ExtProcessorTable::transition_constraints; + let term = ExtProcessorTable::terminal_constraints; table_constraints_prop(&init, "processor initial"); table_constraints_prop(&cons, "processor consistency"); table_constraints_prop(&tran, "processor transition"); @@ -1661,10 +1661,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_program_table_test() { - let init = ExtProgramTable::ext_initial_constraints_as_circuits; - let cons = ExtProgramTable::ext_consistency_constraints_as_circuits; - let tran = ExtProgramTable::ext_transition_constraints_as_circuits; - let term = ExtProgramTable::ext_terminal_constraints_as_circuits; + let init = ExtProgramTable::initial_constraints; + let cons = ExtProgramTable::consistency_constraints; + let tran = ExtProgramTable::transition_constraints; + let term = ExtProgramTable::terminal_constraints; table_constraints_prop(&init, "program initial"); table_constraints_prop(&cons, "program consistency"); table_constraints_prop(&tran, "program transition"); @@ -1673,10 +1673,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_jump_stack_table_test() { - let init = ExtJumpStackTable::ext_initial_constraints_as_circuits; - let cons = ExtJumpStackTable::ext_consistency_constraints_as_circuits; - let tran = ExtJumpStackTable::ext_transition_constraints_as_circuits; - let term = ExtJumpStackTable::ext_terminal_constraints_as_circuits; + let init = ExtJumpStackTable::initial_constraints; + let cons = ExtJumpStackTable::consistency_constraints; + let tran = ExtJumpStackTable::transition_constraints; + let term = ExtJumpStackTable::terminal_constraints; table_constraints_prop(&init, "jump stack initial"); table_constraints_prop(&cons, "jump stack consistency"); table_constraints_prop(&tran, "jump stack transition"); @@ -1685,10 +1685,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_op_stack_table_test() { - let init = ExtOpStackTable::ext_initial_constraints_as_circuits; - let cons = ExtOpStackTable::ext_consistency_constraints_as_circuits; - let tran = ExtOpStackTable::ext_transition_constraints_as_circuits; - let term = ExtOpStackTable::ext_terminal_constraints_as_circuits; + let init = ExtOpStackTable::initial_constraints; + let cons = ExtOpStackTable::consistency_constraints; + let tran = ExtOpStackTable::transition_constraints; + let term = ExtOpStackTable::terminal_constraints; table_constraints_prop(&init, "op stack initial"); table_constraints_prop(&cons, "op stack consistency"); table_constraints_prop(&tran, "op stack transition"); @@ -1697,10 +1697,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_ram_table_test() { - let init = ExtRamTable::ext_initial_constraints_as_circuits; - let cons = ExtRamTable::ext_consistency_constraints_as_circuits; - let tran = ExtRamTable::ext_transition_constraints_as_circuits; - let term = ExtRamTable::ext_terminal_constraints_as_circuits; + let init = ExtRamTable::initial_constraints; + let cons = ExtRamTable::consistency_constraints; + let tran = ExtRamTable::transition_constraints; + let term = ExtRamTable::terminal_constraints; table_constraints_prop(&init, "ram initial"); table_constraints_prop(&cons, "ram consistency"); table_constraints_prop(&tran, "ram transition"); @@ -1709,10 +1709,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_hash_table_test() { - let init = ExtHashTable::ext_initial_constraints_as_circuits; - let cons = ExtHashTable::ext_consistency_constraints_as_circuits; - let tran = ExtHashTable::ext_transition_constraints_as_circuits; - let term = ExtHashTable::ext_terminal_constraints_as_circuits; + let init = ExtHashTable::initial_constraints; + let cons = ExtHashTable::consistency_constraints; + let tran = ExtHashTable::transition_constraints; + let term = ExtHashTable::terminal_constraints; table_constraints_prop(&init, "hash initial"); table_constraints_prop(&cons, "hash consistency"); table_constraints_prop(&tran, "hash transition"); @@ -1721,10 +1721,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_u32_table_test() { - let init = ExtU32Table::ext_initial_constraints_as_circuits; - let cons = ExtU32Table::ext_consistency_constraints_as_circuits; - let tran = ExtU32Table::ext_transition_constraints_as_circuits; - let term = ExtU32Table::ext_terminal_constraints_as_circuits; + let init = ExtU32Table::initial_constraints; + let cons = ExtU32Table::consistency_constraints; + let tran = ExtU32Table::transition_constraints; + let term = ExtU32Table::terminal_constraints; table_constraints_prop(&init, "u32 initial"); table_constraints_prop(&cons, "u32 consistency"); table_constraints_prop(&tran, "u32 transition"); @@ -1733,10 +1733,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_cascade_table_test() { - let init = ExtCascadeTable::ext_initial_constraints_as_circuits; - let cons = ExtCascadeTable::ext_consistency_constraints_as_circuits; - let tran = ExtCascadeTable::ext_transition_constraints_as_circuits; - let term = ExtCascadeTable::ext_terminal_constraints_as_circuits; + let init = ExtCascadeTable::initial_constraints; + let cons = ExtCascadeTable::consistency_constraints; + let tran = ExtCascadeTable::transition_constraints; + let term = ExtCascadeTable::terminal_constraints; table_constraints_prop(&init, "cascade initial"); table_constraints_prop(&cons, "cascade consistency"); table_constraints_prop(&tran, "cascade transition"); @@ -1745,10 +1745,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_lookup_table_test() { - let init = ExtLookupTable::ext_initial_constraints_as_circuits; - let cons = ExtLookupTable::ext_consistency_constraints_as_circuits; - let tran = ExtLookupTable::ext_transition_constraints_as_circuits; - let term = ExtLookupTable::ext_terminal_constraints_as_circuits; + let init = ExtLookupTable::initial_constraints; + let cons = ExtLookupTable::consistency_constraints; + let tran = ExtLookupTable::transition_constraints; + let term = ExtLookupTable::terminal_constraints; table_constraints_prop(&init, "lookup initial"); table_constraints_prop(&cons, "lookup consistency"); table_constraints_prop(&tran, "lookup transition"); diff --git a/triton-vm/src/table/cross_table_argument.rs b/triton-vm/src/table/cross_table_argument.rs index 3d77c877..27dd34d3 100644 --- a/triton-vm/src/table/cross_table_argument.rs +++ b/triton-vm/src/table/cross_table_argument.rs @@ -132,28 +132,28 @@ impl LookupArg { pub struct GrandCrossTableArg {} impl GrandCrossTableArg { - pub fn ext_initial_constraints_as_circuits( + pub fn initial_constraints( _circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { // no further constraints vec![] } - pub fn ext_consistency_constraints_as_circuits( + pub fn consistency_constraints( _circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { // no further constraints vec![] } - pub fn ext_transition_constraints_as_circuits( + pub fn transition_constraints( _circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { // no further constraints vec![] } - pub fn ext_terminal_constraints_as_circuits( + pub fn terminal_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let challenge = |c| circuit_builder.challenge(c); diff --git a/triton-vm/src/table/hash_table.rs b/triton-vm/src/table/hash_table.rs index fe1a407a..c1b5828a 100644 --- a/triton-vm/src/table/hash_table.rs +++ b/triton-vm/src/table/hash_table.rs @@ -80,7 +80,7 @@ impl ExtHashTable { * capital_r_inv } - pub fn ext_initial_constraints_as_circuits( + pub fn initial_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let challenge = |c| circuit_builder.challenge(c); @@ -380,7 +380,7 @@ impl ExtHashTable { ] } - pub fn ext_consistency_constraints_as_circuits( + pub fn consistency_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let constant = |c: u64| circuit_builder.b_constant(c.into()); @@ -587,7 +587,7 @@ impl ExtHashTable { } } - pub fn ext_transition_constraints_as_circuits( + pub fn transition_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let challenge = |c| circuit_builder.challenge(c); @@ -1192,7 +1192,7 @@ impl ExtHashTable { ] } - pub fn ext_terminal_constraints_as_circuits( + pub fn terminal_constraints( _circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { // no more constraints diff --git a/triton-vm/src/table/jump_stack_table.rs b/triton-vm/src/table/jump_stack_table.rs index 2c7b83f7..873da7ed 100644 --- a/triton-vm/src/table/jump_stack_table.rs +++ b/triton-vm/src/table/jump_stack_table.rs @@ -47,7 +47,7 @@ pub struct JumpStackTable {} pub struct ExtJumpStackTable {} impl ExtJumpStackTable { - pub fn ext_initial_constraints_as_circuits( + pub fn initial_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let clk = circuit_builder.input(BaseRow(CLK.master_base_table_index())); @@ -79,14 +79,14 @@ impl ExtJumpStackTable { ] } - pub fn ext_consistency_constraints_as_circuits( + pub fn consistency_constraints( _circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { // no further constraints vec![] } - pub fn ext_transition_constraints_as_circuits( + pub fn transition_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let one = circuit_builder.b_constant(1u32.into()); @@ -183,7 +183,7 @@ impl ExtJumpStackTable { ] } - pub fn ext_terminal_constraints_as_circuits( + pub fn terminal_constraints( _circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { // no further constraints diff --git a/triton-vm/src/table/lookup_table.rs b/triton-vm/src/table/lookup_table.rs index ded8d8cf..d49005cc 100644 --- a/triton-vm/src/table/lookup_table.rs +++ b/triton-vm/src/table/lookup_table.rs @@ -121,7 +121,7 @@ impl LookupTable { } impl ExtLookupTable { - pub fn ext_initial_constraints_as_circuits( + pub fn initial_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let base_row = |col_id: LookupBaseTableColumn| { @@ -165,7 +165,7 @@ impl ExtLookupTable { ] } - pub fn ext_consistency_constraints_as_circuits( + pub fn consistency_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let constant = |c: u64| circuit_builder.b_constant(c.into()); @@ -178,7 +178,7 @@ impl ExtLookupTable { vec![padding_is_0_or_1] } - pub fn ext_transition_constraints_as_circuits( + pub fn transition_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let one = circuit_builder.b_constant(BFIELD_ONE); @@ -258,7 +258,7 @@ impl ExtLookupTable { ] } - pub fn ext_terminal_constraints_as_circuits( + pub fn terminal_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let challenge = |challenge_id: ChallengeId| circuit_builder.challenge(challenge_id); diff --git a/triton-vm/src/table/op_stack_table.rs b/triton-vm/src/table/op_stack_table.rs index d6e1dfcc..2cc91b4b 100644 --- a/triton-vm/src/table/op_stack_table.rs +++ b/triton-vm/src/table/op_stack_table.rs @@ -45,7 +45,7 @@ pub struct OpStackTable {} pub struct ExtOpStackTable {} impl ExtOpStackTable { - pub fn ext_initial_constraints_as_circuits( + pub fn initial_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let clk = circuit_builder.input(BaseRow(CLK.master_base_table_index())); @@ -82,14 +82,14 @@ impl ExtOpStackTable { ] } - pub fn ext_consistency_constraints_as_circuits( + pub fn consistency_constraints( _circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { // no further constraints vec![] } - pub fn ext_transition_constraints_as_circuits( + pub fn transition_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let one = circuit_builder.b_constant(1u32.into()); @@ -164,7 +164,7 @@ impl ExtOpStackTable { ] } - pub fn ext_terminal_constraints_as_circuits( + pub fn terminal_constraints( _circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { // no further constraints diff --git a/triton-vm/src/table/processor_table.rs b/triton-vm/src/table/processor_table.rs index e472c708..21fb0291 100644 --- a/triton-vm/src/table/processor_table.rs +++ b/triton-vm/src/table/processor_table.rs @@ -393,7 +393,7 @@ impl ProcessorTable { pub struct ExtProcessorTable {} impl ExtProcessorTable { - pub fn ext_initial_constraints_as_circuits( + pub fn initial_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let constant = |c: u32| circuit_builder.b_constant(c.into()); @@ -560,7 +560,7 @@ impl ExtProcessorTable { ] } - pub fn ext_consistency_constraints_as_circuits( + pub fn consistency_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let constant = |c: u32| circuit_builder.b_constant(c.into()); @@ -2666,7 +2666,7 @@ impl ExtProcessorTable { + no_update_summand } - pub fn ext_transition_constraints_as_circuits( + pub fn transition_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let constant = |c: u64| circuit_builder.b_constant(c.into()); @@ -2737,7 +2737,7 @@ impl ExtProcessorTable { .concat() } - pub fn ext_terminal_constraints_as_circuits( + pub fn terminal_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let base_row = |col: ProcessorBaseTableColumn| { diff --git a/triton-vm/src/table/program_table.rs b/triton-vm/src/table/program_table.rs index 28f91e68..b12d8289 100644 --- a/triton-vm/src/table/program_table.rs +++ b/triton-vm/src/table/program_table.rs @@ -39,7 +39,7 @@ pub struct ProgramTable {} pub struct ExtProgramTable {} impl ExtProgramTable { - pub fn ext_initial_constraints_as_circuits( + pub fn initial_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let address = circuit_builder.input(BaseRow(Address.master_base_table_index())); @@ -59,7 +59,7 @@ impl ExtProgramTable { ] } - pub fn ext_consistency_constraints_as_circuits( + pub fn consistency_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let one = circuit_builder.b_constant(1_u32.into()); @@ -70,7 +70,7 @@ impl ExtProgramTable { vec![is_padding_is_bit] } - pub fn ext_transition_constraints_as_circuits( + pub fn transition_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let one = circuit_builder.b_constant(1u32.into()); @@ -117,7 +117,7 @@ impl ExtProgramTable { ] } - pub fn ext_terminal_constraints_as_circuits( + pub fn terminal_constraints( _circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { // no further constraints diff --git a/triton-vm/src/table/ram_table.rs b/triton-vm/src/table/ram_table.rs index 338ade9c..2c65c038 100644 --- a/triton-vm/src/table/ram_table.rs +++ b/triton-vm/src/table/ram_table.rs @@ -312,7 +312,7 @@ impl RamTable { } impl ExtRamTable { - pub fn ext_initial_constraints_as_circuits( + pub fn initial_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let one = circuit_builder.b_constant(1_u32.into()); @@ -376,14 +376,14 @@ impl ExtRamTable { ] } - pub fn ext_consistency_constraints_as_circuits( + pub fn consistency_constraints( _circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { // no further constraints vec![] } - pub fn ext_transition_constraints_as_circuits( + pub fn transition_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let one = circuit_builder.b_constant(1u32.into()); @@ -526,7 +526,7 @@ impl ExtRamTable { ] } - pub fn ext_terminal_constraints_as_circuits( + pub fn terminal_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let one = circuit_builder.b_constant(1_u32.into()); diff --git a/triton-vm/src/table/u32_table.rs b/triton-vm/src/table/u32_table.rs index 0aa678d2..7f1efdab 100644 --- a/triton-vm/src/table/u32_table.rs +++ b/triton-vm/src/table/u32_table.rs @@ -69,7 +69,7 @@ impl ExtU32Table { ) } - pub fn ext_initial_constraints_as_circuits( + pub fn initial_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let challenge = |c| circuit_builder.challenge(c); @@ -105,7 +105,7 @@ impl ExtU32Table { vec![running_sum_log_derivative_starts_correctly] } - pub fn ext_consistency_constraints_as_circuits( + pub fn consistency_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let one = circuit_builder.b_constant(1_u32.into()); @@ -193,7 +193,7 @@ impl ExtU32Table { ] } - pub fn ext_transition_constraints_as_circuits( + pub fn transition_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let challenge = |c| circuit_builder.challenge(c); @@ -385,7 +385,7 @@ impl ExtU32Table { ] } - pub fn ext_terminal_constraints_as_circuits( + pub fn terminal_constraints( circuit_builder: &ConstraintCircuitBuilder, ) -> Vec> { let ci = circuit_builder.input(BaseRow(CI.master_base_table_index())); From 923caf0aed32baed1c047507bb83a76ec4450324 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Mon, 8 May 2023 12:05:16 +0200 Subject: [PATCH 13/72] generate AIR evaluation code that skips Montgomery reduction --- constraint-evaluation-generator/src/main.rs | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 8c7d3385..cbfba9c8 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -554,8 +554,9 @@ fn get_binding_name(circuit: &ConstraintCircuit) -> Stri } /// Recursively check whether a node is composed of only BFieldElements, i.e., only uses -/// (1) inputs from base rows, (2) constants from the B-field, and (3) binary operations on -/// BFieldElements. +/// 1. inputs from base rows, +/// 2. constants from the B-field, and +/// 3. binary operations on BFieldElements. fn is_bfield_element(circuit: &ConstraintCircuit) -> bool { match &circuit.expression { CircuitExpression::XConstant(_) => false, @@ -568,8 +569,9 @@ fn is_bfield_element(circuit: &ConstraintCircuit) -> boo } } -/// Return (1) the code for evaluating a single node and (2) a list of symbols that this evaluation -/// depends on. +/// Return +/// 1. the code for evaluating a single node and +/// 2. a list of symbols that this evaluation depends on. fn evaluate_single_node( requested_visited_count: usize, circuit: &ConstraintCircuit, @@ -614,14 +616,12 @@ fn evaluate_single_node( } fn print_bfe(bfe: &BFieldElement) -> String { - format!("BFieldElement::new({})", bfe.value()) + format!("BFieldElement::from_raw_u64({})", bfe.raw_u64()) } fn print_xfe(xfe: &XFieldElement) -> String { - format!( - "XFieldElement::new_u64([{}, {}, {}])", - xfe.coefficients[0].value(), - xfe.coefficients[1].value(), - xfe.coefficients[2].value() - ) + let coeff_0 = print_bfe(&xfe.coefficients[0]); + let coeff_1 = print_bfe(&xfe.coefficients[1]); + let coeff_2 = print_bfe(&xfe.coefficients[2]); + format!("XFieldElement::new([{coeff_0}, {coeff_1}, {coeff_2}])") } From ac51cd993dd839f422f731665f384ae6959c8dc1 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Mon, 8 May 2023 16:16:09 +0200 Subject: [PATCH 14/72] use return values when constructing binding names --- constraint-evaluation-generator/src/main.rs | 38 +++++++++------------ 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index cbfba9c8..7df051f0 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -469,44 +469,37 @@ fn declare_nodes_with_visit_count( circuits: &[ConstraintCircuit], ) -> String { let mut in_scope: HashSet = HashSet::new(); - let mut output = String::default(); - - for circuit in circuits.iter() { - declare_single_node_with_visit_count( - requested_visited_count, - circuit, - &mut in_scope, - &mut output, - ); - } - output + circuits + .iter() + .map(|circuit| { + declare_single_node_with_visit_count(requested_visited_count, circuit, &mut in_scope) + }) + .collect_vec() + .join("") } fn declare_single_node_with_visit_count( requested_visited_count: usize, circuit: &ConstraintCircuit, in_scope: &mut HashSet, - output: &mut String, -) { +) -> String { if circuit.visited_counter < requested_visited_count { // If the visited counter is not there yet, make a recursive call. We are // not yet ready to bind this node's ID to a value. if let CircuitExpression::BinaryOperation(_binop, lhs, rhs) = &circuit.expression { - declare_single_node_with_visit_count( + let out_left = declare_single_node_with_visit_count( requested_visited_count, &lhs.as_ref().borrow(), in_scope, - output, ); - declare_single_node_with_visit_count( + let out_right = declare_single_node_with_visit_count( requested_visited_count, &rhs.as_ref().borrow(), in_scope, - output, ); + return [out_left, out_right].join("\n"); } - return; } // If this node has already been declared, or visit counter is higher than requested, @@ -520,7 +513,7 @@ fn declare_single_node_with_visit_count( CircuitExpression::BinaryOperation(_, _, _) ) { - return; + return String::default(); } // If this line is met, it means that the visit count is as requested, and that @@ -528,15 +521,16 @@ fn declare_single_node_with_visit_count( // expression for the value, and then put it into scope through a let expression if circuit.visited_counter == requested_visited_count && !in_scope.contains(&circuit.id) { let binding_name = get_binding_name(circuit); - output.push_str(&format!("let {binding_name} =\n")); let (to_output, _) = evaluate_single_node(requested_visited_count, circuit, in_scope); - output.push_str(&to_output); - output.push_str(";\n"); let new_insertion = in_scope.insert(circuit.id); // sanity check: don't declare same node multiple times assert!(new_insertion); + + return format!("let {binding_name} = {to_output};\n"); } + + String::default() } /// Return a variable name for the node. Returns `point[n]` if node is just From 043a6ed93af7346af7edcf59e1b56033ddc7dde8 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Mon, 8 May 2023 17:14:59 +0200 Subject: [PATCH 15/72] clean up binding a single node with given visit counter --- constraint-evaluation-generator/src/main.rs | 105 +++++++++----------- 1 file changed, 49 insertions(+), 56 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 7df051f0..46fc93cf 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -468,77 +468,70 @@ fn declare_nodes_with_visit_count( requested_visited_count: usize, circuits: &[ConstraintCircuit], ) -> String { - let mut in_scope: HashSet = HashSet::new(); + let mut scope: HashSet = HashSet::new(); circuits .iter() .map(|circuit| { - declare_single_node_with_visit_count(requested_visited_count, circuit, &mut in_scope) + declare_single_node_with_visit_count(circuit, requested_visited_count, &mut scope) }) .collect_vec() .join("") } fn declare_single_node_with_visit_count( - requested_visited_count: usize, circuit: &ConstraintCircuit, - in_scope: &mut HashSet, + requested_visited_count: usize, + scope: &mut HashSet, ) -> String { - if circuit.visited_counter < requested_visited_count { - // If the visited counter is not there yet, make a recursive call. We are - // not yet ready to bind this node's ID to a value. - if let CircuitExpression::BinaryOperation(_binop, lhs, rhs) = &circuit.expression { - let out_left = declare_single_node_with_visit_count( - requested_visited_count, - &lhs.as_ref().borrow(), - in_scope, - ); - let out_right = declare_single_node_with_visit_count( - requested_visited_count, - &rhs.as_ref().borrow(), - in_scope, - ); - return [out_left, out_right].join("\n"); - } + // Don't declare a node twice. + if scope.contains(&circuit.id) { + return String::default(); } - // If this node has already been declared, or visit counter is higher than requested, - // then the node value *must* already be in scope. We should not redeclare it. - // We also do not declare nodes that are e.g `row[3]` since they are already in scope - // through the `points` input argument, and we do not declare constants. - if circuit.visited_counter > requested_visited_count - || in_scope.contains(&circuit.id) - || !matches!( - circuit.expression, - CircuitExpression::BinaryOperation(_, _, _) - ) - { + // A higher-than-requested visit counter means the node is already in global scope, albeit not + // necessarily in the passed-in scope. + if circuit.visited_counter > requested_visited_count { return String::default(); } - // If this line is met, it means that the visit count is as requested, and that - // the value is not in scope. So it must be added to the scope. We find the - // expression for the value, and then put it into scope through a let expression - if circuit.visited_counter == requested_visited_count && !in_scope.contains(&circuit.id) { - let binding_name = get_binding_name(circuit); - let (to_output, _) = evaluate_single_node(requested_visited_count, circuit, in_scope); - - let new_insertion = in_scope.insert(circuit.id); - // sanity check: don't declare same node multiple times - assert!(new_insertion); + let CircuitExpression::BinaryOperation(_, lhs, rhs) = &circuit.expression else { + // Constants are already (or can be) trivially declared. + return String::default(); + }; - return format!("let {binding_name} = {to_output};\n"); + // If the visited counter is not yet exact, start recursing on the BinaryOperation's children. + if circuit.visited_counter < requested_visited_count { + let out_left = declare_single_node_with_visit_count( + &lhs.as_ref().borrow(), + requested_visited_count, + scope, + ); + let out_right = declare_single_node_with_visit_count( + &rhs.as_ref().borrow(), + requested_visited_count, + scope, + ); + return [out_left, out_right].join("\n"); } - String::default() + // Declare a new binding. + assert_eq!(circuit.visited_counter, requested_visited_count); + let binding_name = get_binding_name(circuit); + let (evaluation, _) = evaluate_single_node(requested_visited_count, circuit, scope); + + let is_new_insertion = scope.insert(circuit.id); + assert!(is_new_insertion); + + format!("let {binding_name} = {evaluation};\n") } /// Return a variable name for the node. Returns `point[n]` if node is just /// a value from the codewords. Otherwise returns the ID of the circuit. fn get_binding_name(circuit: &ConstraintCircuit) -> String { match &circuit.expression { - CircuitExpression::XConstant(xfe) => print_xfe(xfe), CircuitExpression::BConstant(bfe) => print_bfe(bfe), + CircuitExpression::XConstant(xfe) => print_xfe(xfe), CircuitExpression::Input(idx) => idx.to_string(), CircuitExpression::Challenge(challenge_id) => { format!("challenges.get_challenge({challenge_id})") @@ -547,14 +540,25 @@ fn get_binding_name(circuit: &ConstraintCircuit) -> Stri } } +fn print_bfe(bfe: &BFieldElement) -> String { + format!("BFieldElement::from_raw_u64({})", bfe.raw_u64()) +} + +fn print_xfe(xfe: &XFieldElement) -> String { + let coeff_0 = print_bfe(&xfe.coefficients[0]); + let coeff_1 = print_bfe(&xfe.coefficients[1]); + let coeff_2 = print_bfe(&xfe.coefficients[2]); + format!("XFieldElement::new([{coeff_0}, {coeff_1}, {coeff_2}])") +} + /// Recursively check whether a node is composed of only BFieldElements, i.e., only uses /// 1. inputs from base rows, /// 2. constants from the B-field, and /// 3. binary operations on BFieldElements. fn is_bfield_element(circuit: &ConstraintCircuit) -> bool { match &circuit.expression { - CircuitExpression::XConstant(_) => false, CircuitExpression::BConstant(_) => true, + CircuitExpression::XConstant(_) => false, CircuitExpression::Input(indicator) => indicator.is_base_table_column(), CircuitExpression::Challenge(_) => false, CircuitExpression::BinaryOperation(_, lhs, rhs) => { @@ -608,14 +612,3 @@ fn evaluate_single_node( (output, dependent_symbols) } - -fn print_bfe(bfe: &BFieldElement) -> String { - format!("BFieldElement::from_raw_u64({})", bfe.raw_u64()) -} - -fn print_xfe(xfe: &XFieldElement) -> String { - let coeff_0 = print_bfe(&xfe.coefficients[0]); - let coeff_1 = print_bfe(&xfe.coefficients[1]); - let coeff_2 = print_bfe(&xfe.coefficients[2]); - format!("XFieldElement::new([{coeff_0}, {coeff_1}, {coeff_2}])") -} From 57f80e1faa1a1918ec0c5e40a5bf6db2cb0b7cab Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 9 May 2023 08:28:02 +0200 Subject: [PATCH 16/72] simplify generation of code for evaluating single node --- constraint-evaluation-generator/src/main.rs | 68 ++++++++------------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 46fc93cf..7e52429a 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -420,8 +420,7 @@ fn turn_circuits_into_string( let mut ext_constraint_evaluation_expressions: Vec = vec![]; for constraint in constraint_circuits.iter() { // Build code for expressions that evaluate to the constraints - let (constraint_evaluation, _dependent_symbols) = - evaluate_single_node(1, constraint, &HashSet::default()); + let constraint_evaluation = evaluate_single_node(1, constraint, &HashSet::default()); match is_bfield_element(constraint) { true => base_constraint_evaluation_expressions.push(constraint_evaluation), false => ext_constraint_evaluation_expressions.push(constraint_evaluation), @@ -518,7 +517,7 @@ fn declare_single_node_with_visit_count( // Declare a new binding. assert_eq!(circuit.visited_counter, requested_visited_count); let binding_name = get_binding_name(circuit); - let (evaluation, _) = evaluate_single_node(requested_visited_count, circuit, scope); + let evaluation = evaluate_single_node(requested_visited_count, circuit, scope); let is_new_insertion = scope.insert(circuit.id); assert!(is_new_insertion); @@ -562,53 +561,40 @@ fn is_bfield_element(circuit: &ConstraintCircuit) -> boo CircuitExpression::Input(indicator) => indicator.is_base_table_column(), CircuitExpression::Challenge(_) => false, CircuitExpression::BinaryOperation(_, lhs, rhs) => { - is_bfield_element(&lhs.as_ref().borrow()) && is_bfield_element(&rhs.as_ref().borrow()) + let lhs = lhs.as_ref().borrow(); + let rhs = rhs.as_ref().borrow(); + is_bfield_element(&lhs) && is_bfield_element(&rhs) } } } -/// Return -/// 1. the code for evaluating a single node and -/// 2. a list of symbols that this evaluation depends on. +/// Recursively construct the code for evaluating a single node. fn evaluate_single_node( requested_visited_count: usize, circuit: &ConstraintCircuit, - in_scope: &HashSet, -) -> (String, Vec) { - let mut output = String::default(); - // If this node has already been declared, or visit counter is higher than requested, - // than the node value *must* be in scope, meaning that we can just reference it. - if circuit.visited_counter > requested_visited_count || in_scope.contains(&circuit.id) { - let binding_name = get_binding_name(circuit); - output.push_str(&binding_name); - return match &circuit.expression { - CircuitExpression::BinaryOperation(_, _, _) => (output, vec![binding_name]), - _ => (output, vec![]), - }; + scope: &HashSet, +) -> String { + let binding_name = get_binding_name(circuit); + + // Don't declare a node twice. + if scope.contains(&circuit.id) { + return binding_name; } - // If variable is not already in scope, then we must generate the expression to evaluate it. - let mut dependent_symbols = vec![]; - match &circuit.expression { - CircuitExpression::BinaryOperation(binop, lhs, rhs) => { - output.push('('); - let (to_output, lhs_symbols) = - evaluate_single_node(requested_visited_count, &lhs.as_ref().borrow(), in_scope); - output.push_str(&to_output); - output.push(')'); - output.push_str(&binop.to_string()); - output.push('('); - let (to_output, rhs_symbols) = - evaluate_single_node(requested_visited_count, &rhs.as_ref().borrow(), in_scope); - output.push_str(&to_output); - output.push(')'); - - let ret_as_vec = vec![lhs_symbols, rhs_symbols].concat(); - let ret_as_hash_set: HashSet = ret_as_vec.into_iter().collect(); - dependent_symbols = ret_as_hash_set.into_iter().collect_vec() - } - _ => output.push_str(&get_binding_name(circuit)), + // The binding must already be known. + if circuit.visited_counter > requested_visited_count { + return binding_name; } - (output, dependent_symbols) + // Constants have trivial bindings. + let CircuitExpression::BinaryOperation(binop, lhs, rhs) = &circuit.expression else { + return binding_name; + }; + + let lhs = lhs.as_ref().borrow(); + let rhs = rhs.as_ref().borrow(); + let evaluated_lhs = evaluate_single_node(requested_visited_count, &lhs, scope); + let evaluated_rhs = evaluate_single_node(requested_visited_count, &rhs, scope); + let operation = binop.to_string(); + format!("({evaluated_lhs}) {operation} ({evaluated_rhs})") } From 2dbfd5f253307a9a972bfc3e1c3c773a4c7488e3 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 9 May 2023 08:37:50 +0200 Subject: [PATCH 17/72] simplify `CircuitExpression`s `PartialEq` --- triton-vm/src/table/constraint_circuit.rs | 36 +++++------------------ 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 66b54b9b..97ae6967 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -252,35 +252,15 @@ impl Hash for CircuitExpression { impl PartialEq for CircuitExpression { fn eq(&self, other: &Self) -> bool { - match self { - XConstant(self_xfe) => match other { - XConstant(other_xfe) => self_xfe == other_xfe, - _ => false, - }, - BConstant(self_bfe) => match other { - BConstant(other_bfe) => self_bfe == other_bfe, - _ => false, - }, - Input(self_input) => match other { - Input(other_input) => self_input == other_input, - _ => false, - }, - Challenge(self_challenge_id) => match other { - Challenge(other_challenge_id) => self_challenge_id == other_challenge_id, - _ => false, - }, - BinaryOperation(binop_self, lhs_self, rhs_self) => { - match other { - BinaryOperation(binop_other, lhs_other, rhs_other) => { - // a = b `op0` c, - // d = e `op1` f => - // a = d <= op0 == op1 && b == e && c ==f - binop_self == binop_other && lhs_self == lhs_other && rhs_self == rhs_other - } - - _ => false, - } + match (self, other) { + (BConstant(bfe_self), BConstant(bfe_other)) => bfe_self == bfe_other, + (XConstant(xfe_self), XConstant(xfe_other)) => xfe_self == xfe_other, + (Input(input_self), Input(input_other)) => input_self == input_other, + (Challenge(id_self), Challenge(id_other)) => id_self == id_other, + (BinaryOperation(op_s, lhs_s, rhs_s), BinaryOperation(op_o, lhs_o, rhs_o)) => { + op_s == op_o && lhs_s == lhs_o && rhs_s == rhs_o } + _ => false, } } } From b6dfef2e42ce2959cef34b69fb98741961a464fe Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 9 May 2023 10:39:58 +0200 Subject: [PATCH 18/72] fix bug: base and extension constraints can be listed in arbitrary order --- constraint-evaluation-generator/src/main.rs | 150 ++++++++++---------- 1 file changed, 78 insertions(+), 72 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 7e52429a..898ce53e 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -189,23 +189,26 @@ fn gen( let num_transition_constraints = transition_constraint_circuits.len(); let num_terminal_constraints = terminal_constraint_circuits.len(); - let initial_constraints_degrees = - turn_circuits_into_degree_bounds_string(initial_constraint_circuits); - let consistency_constraints_degrees = - turn_circuits_into_degree_bounds_string(consistency_constraint_circuits); - let transition_constraints_degrees = - turn_circuits_into_degree_bounds_string(transition_constraint_circuits); - let terminal_constraints_degrees = - turn_circuits_into_degree_bounds_string(terminal_constraint_circuits); - - let (initial_constraint_strings_bfe, initial_constraint_strings_xfe) = - turn_circuits_into_string(initial_constraint_circuits); - let (consistency_constraint_strings_bfe, consistency_constraint_strings_xfe) = - turn_circuits_into_string(consistency_constraint_circuits); - let (transition_constraint_strings_bfe, transition_constraint_strings_xfe) = - turn_circuits_into_string(transition_constraint_circuits); - let (terminal_constraint_strings_bfe, terminal_constraint_strings_xfe) = - turn_circuits_into_string(terminal_constraint_circuits); + let ( + initial_constraints_degrees, + initial_constraint_strings_bfe, + initial_constraint_strings_xfe, + ) = turn_circuits_into_string(initial_constraint_circuits); + let ( + consistency_constraints_degrees, + consistency_constraint_strings_bfe, + consistency_constraint_strings_xfe, + ) = turn_circuits_into_string(consistency_constraint_circuits); + let ( + transition_constraints_degrees, + transition_constraint_strings_bfe, + transition_constraint_strings_xfe, + ) = turn_circuits_into_string(transition_constraint_circuits); + let ( + terminal_constraints_degrees, + terminal_constraint_strings_bfe, + terminal_constraint_strings_xfe, + ) = turn_circuits_into_string(terminal_constraint_circuits); format!( " @@ -366,80 +369,79 @@ impl Quotientable for {table_mod_name} {{ ) } -fn turn_circuits_into_degree_bounds_string( - constraint_circuits: &[ConstraintCircuit], -) -> String { - constraint_circuits - .iter() - .map(|circuit| circuit.degree()) - .map(|degree| format!("interpolant_degree * {degree} as Degree - zerofier_degree")) - .join(",\n") -} - +/// Given a slice of constraint circuits, return a tuple of strings corresponding to code +/// evaluating these constraints as well as their degrees. In particular: +/// 1. The first string contains code that, when evaluated, produces the constraints' degrees, +/// 1. the second string contains code that, when evaluated, produces the constraints' values, with +/// the input type for the base row being `BFieldElement`, and +/// 1. the third string is like the second string, except that the input type for the base row is +/// `XFieldElement`. fn turn_circuits_into_string( constraint_circuits: &mut [ConstraintCircuit], -) -> (String, String) { +) -> (String, String, String) { if constraint_circuits.is_empty() { - return ("vec![]".to_string(), "vec![]".to_string()); + return ("".to_string(), "vec![]".to_string(), "vec![]".to_string()); } - // Assert that all node IDs are unique (sanity check) + // Sanity check: all node IDs must be unique. ConstraintCircuit::assert_has_unique_ids(constraint_circuits); - // Count number of times each node is visited + // Count number of times each node is referenced. ConstraintCircuit::traverse_multiple(constraint_circuits); - // Get all values for the visited counters in the entire multi-circuit - let mut visited_counters = vec![]; - for constraint in constraint_circuits.iter() { - visited_counters.append(&mut constraint.get_all_visited_counters()); - } - + // Get all unique reference counts. + let mut visited_counters = constraint_circuits + .iter() + .flat_map(|constraint| constraint.get_all_visited_counters()) + .collect_vec(); visited_counters.sort_unstable(); - visited_counters.reverse(); visited_counters.dedup(); - // Declare shared values - // In the main function we predeclare all variables with a visit count of more than 1 - // These declarations must be made from the highest count number to the lowest, otherwise - // the code will refer to bindings that have not yet been made - let mut shared_evaluations: Vec = vec![]; - for visited_counter in visited_counters { - if visited_counter == 1 { - continue; - } - shared_evaluations.push(declare_nodes_with_visit_count( - visited_counter, - constraint_circuits, - )); - } - - let shared_declarations = shared_evaluations.join(""); + // Declare all shared variables, i.e., those with a visit count greater than 1. + // These declarations must be made starting from the highest visit count. + // Otherwise, the resulting code will refer to bindings that have not yet been made. + let shared_declarations = visited_counters + .into_iter() + .filter(|&x| x > 1) + .rev() + .map(|visit_count| declare_nodes_with_visit_count(visit_count, constraint_circuits)) + .collect_vec() + .join(""); - let mut base_constraint_evaluation_expressions: Vec = vec![]; - let mut ext_constraint_evaluation_expressions: Vec = vec![]; - for constraint in constraint_circuits.iter() { - // Build code for expressions that evaluate to the constraints - let constraint_evaluation = evaluate_single_node(1, constraint, &HashSet::default()); - match is_bfield_element(constraint) { - true => base_constraint_evaluation_expressions.push(constraint_evaluation), - false => ext_constraint_evaluation_expressions.push(constraint_evaluation), - } - } + let (base_constraints, ext_constraints): (Vec<_>, Vec<_>) = constraint_circuits + .iter() + .partition(|constraint| is_bfield_element(constraint)); - let base_constraint_evaluations_joined = base_constraint_evaluation_expressions.join(",\n"); - let ext_constraint_evaluations_joined = ext_constraint_evaluation_expressions.join(",\n"); + // The order of the constraints' degrees must match the order of the constraints. + // Hence, listing the degrees is only possible after the partition into base and extension + // constraints is known. + let degree_bounds_string = base_constraints + .iter() + .chain(ext_constraints.iter()) + .map(|circuit| circuit.degree()) + .map(|degree| format!("interpolant_degree * {degree} as Degree - zerofier_degree")) + .join(",\n"); + + let build_constraint_evaluation_code = |constraints: &[&ConstraintCircuit]| { + constraints + .iter() + .map(|constraint| evaluate_single_node(1, constraint, &HashSet::default())) + .collect_vec() + .join(",\n") + }; + let base_constraint_strings = build_constraint_evaluation_code(&base_constraints); + let ext_constraint_strings = build_constraint_evaluation_code(&ext_constraints); // If there are no base constraints, the type needs to be explicitly declared. - let base_constraint_bfe_type = match base_constraint_evaluation_expressions.is_empty() { + let base_constraint_bfe_type = match base_constraints.is_empty() { true => ": [BFieldElement; 0]", false => "", }; let constraint_string_bfe = format!( "{shared_declarations} - let base_constraints{base_constraint_bfe_type} = [{base_constraint_evaluations_joined}]; - let ext_constraints = [{ext_constraint_evaluations_joined}]; + let base_constraints{base_constraint_bfe_type} = [{base_constraint_strings}]; + let ext_constraints = [{ext_constraint_strings}]; base_constraints .into_iter() .map(|bfe| bfe.lift()) @@ -449,15 +451,19 @@ fn turn_circuits_into_string( let constraint_string_xfe = format!( "{shared_declarations} - let base_constraints = [{base_constraint_evaluations_joined}]; - let ext_constraints = [{ext_constraint_evaluations_joined}]; + let base_constraints = [{base_constraint_strings}]; + let ext_constraints = [{ext_constraint_strings}]; base_constraints .into_iter() .chain(ext_constraints.into_iter()) .collect()" ); - (constraint_string_bfe, constraint_string_xfe) + ( + degree_bounds_string, + constraint_string_bfe, + constraint_string_xfe, + ) } /// Produce the code to evaluate code for all nodes that share a value number of From 818c3246b3b59eb59413ff5c0321087936c0c9e4 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 9 May 2023 12:18:22 +0200 Subject: [PATCH 19/72] remove unused function to evaluate all constraints --- triton-vm/src/table/master_table.rs | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index 08ea30bd..c5553592 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -1697,28 +1697,6 @@ pub fn evaluate_all_terminal_constraints( .concat() } -pub fn evaluate_all_constraints( - current_base_row: ArrayView1, - current_ext_row: ArrayView1, - next_base_row: ArrayView1, - next_ext_row: ArrayView1, - challenges: &Challenges, -) -> Vec { - [ - evaluate_all_initial_constraints(current_base_row, current_ext_row, challenges), - evaluate_all_consistency_constraints(current_base_row, current_ext_row, challenges), - evaluate_all_transition_constraints( - current_base_row, - current_ext_row, - next_base_row, - next_ext_row, - challenges, - ), - evaluate_all_terminal_constraints(current_base_row, current_ext_row, challenges), - ] - .concat() -} - pub fn randomized_padded_trace_len(padded_height: usize, num_trace_randomizers: usize) -> usize { roundup_npo2((padded_height + num_trace_randomizers) as u64) as usize } From f221c4b521dfcb5d59d336882431cf97b67e46ba Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 9 May 2023 12:36:28 +0200 Subject: [PATCH 20/72] test constraint evaluation in their respective tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While this is a slowdown for the tests, it brings many advantages: - Constraint testing is independent from generated code. This means: • debugging new functionality is simplified, • bugs introduced during code generation don't affect this test, and • changes to code generation don't affect this test. - Identifying which of the constraints failed now doesn't require: • cumbersome index juggling, • manual index backtracking, or • maintaining bloated methods to keep track of indices. --- triton-vm/src/stark.rs | 157 +++++++------------- triton-vm/src/table/cascade_table.rs | 94 ++++++++++++ triton-vm/src/table/hash_table.rs | 91 ++++++++++++ triton-vm/src/table/jump_stack_table.rs | 95 +++++++++++++ triton-vm/src/table/lookup_table.rs | 93 ++++++++++++ triton-vm/src/table/master_table.rs | 181 ------------------------ triton-vm/src/table/op_stack_table.rs | 94 ++++++++++++ triton-vm/src/table/processor_table.rs | 94 ++++++++++++ triton-vm/src/table/program_table.rs | 93 ++++++++++++ triton-vm/src/table/ram_table.rs | 91 ++++++++++++ triton-vm/src/table/u32_table.rs | 91 ++++++++++++ 11 files changed, 888 insertions(+), 286 deletions(-) diff --git a/triton-vm/src/stark.rs b/triton-vm/src/stark.rs index 272804ab..4c2821f8 100644 --- a/triton-vm/src/stark.rs +++ b/triton-vm/src/stark.rs @@ -905,6 +905,7 @@ pub(crate) mod triton_stark_tests { use twenty_first::shared_math::other::random_elements; use crate::shared_tests::*; + use crate::table::cascade_table; use crate::table::cascade_table::ExtCascadeTable; use crate::table::challenges::ChallengeId::StandardInputIndeterminate; use crate::table::challenges::ChallengeId::StandardInputTerminal; @@ -915,15 +916,22 @@ pub(crate) mod triton_stark_tests { use crate::table::cross_table_argument::GrandCrossTableArg; use crate::table::extension_table::Evaluable; use crate::table::extension_table::Quotientable; + use crate::table::hash_table; use crate::table::hash_table::ExtHashTable; + use crate::table::jump_stack_table; use crate::table::jump_stack_table::ExtJumpStackTable; + use crate::table::lookup_table; use crate::table::lookup_table::ExtLookupTable; use crate::table::master_table::all_degrees_with_origin; use crate::table::master_table::MasterExtTable; use crate::table::master_table::TableId::ProcessorTable; + use crate::table::op_stack_table; use crate::table::op_stack_table::ExtOpStackTable; + use crate::table::processor_table; use crate::table::processor_table::ExtProcessorTable; + use crate::table::program_table; use crate::table::program_table::ExtProgramTable; + use crate::table::ram_table; use crate::table::ram_table::ExtRamTable; use crate::table::table_column::MasterBaseTableColumn; use crate::table::table_column::MasterExtTableColumn; @@ -931,6 +939,7 @@ pub(crate) mod triton_stark_tests { use crate::table::table_column::ProcessorExtTableColumn::InputTableEvalArg; use crate::table::table_column::ProcessorExtTableColumn::OutputTableEvalArg; use crate::table::table_column::RamBaseTableColumn; + use crate::table::u32_table; use crate::table::u32_table::ExtU32Table; use crate::vm::simulate; use crate::vm::triton_vm_tests::property_based_test_programs; @@ -1715,7 +1724,6 @@ pub(crate) mod triton_stark_tests { } pub fn triton_table_constraints_evaluate_to_zero(source_code_and_input: SourceCodeAndInput) { - let zero = XFieldElement::zero(); let (_, _, _, master_base_table, master_ext_table, challenges) = parse_simulate_pad_extend( &source_code_and_input.source_code, source_code_and_input.input, @@ -1733,112 +1741,51 @@ pub(crate) mod triton_stark_tests { master_ext_trace_table.nrows() ); - let first_base_row_lifted = master_base_trace_table.row(0).map(|e| e.lift()); - let evaluated_initial_constraints = evaluate_all_initial_constraints( - first_base_row_lifted.view(), - master_ext_trace_table.row(0), + assert!(program_table::tests::constraints_evaluate_to_zero( + master_base_trace_table, + master_ext_trace_table, &challenges, - ); - let num_initial_constraints = evaluated_initial_constraints.len(); - assert_eq!(num_all_initial_quotients(), num_initial_constraints); - for (constraint_idx, ebc) in evaluated_initial_constraints.into_iter().enumerate() { - let (table_idx, table_name) = initial_constraint_table_idx_and_name(constraint_idx); - assert_eq!( - zero, ebc, - "Failed initial constraint with global index {constraint_idx}. \ - Total number of initial constraints: {num_initial_constraints}. \ - Table: {table_name}. Index within table: {table_idx}", - ); - } - - let num_rows = master_base_trace_table.nrows(); - for row_idx in 0..num_rows { - let base_row = master_base_trace_table.row(row_idx); - let ext_row = master_ext_trace_table.row(row_idx); - let base_row_lifted = base_row.map(|e| e.lift()); - let evaluated_consistency_constraints = - evaluate_all_consistency_constraints(base_row_lifted.view(), ext_row, &challenges); - let num_consistency_constraints = evaluated_consistency_constraints.len(); - assert_eq!(num_all_consistency_quotients(), num_consistency_constraints); - for (constraint_idx, ecc) in evaluated_consistency_constraints.into_iter().enumerate() { - let (table_idx, table_name) = - consistency_constraint_table_idx_and_name(constraint_idx); - assert_eq!( - zero, ecc, - "Failed consistency constraint with global index {constraint_idx}. \ - Total number of consistency constraints: {num_consistency_constraints}. \ - Table: {table_name}. Index within table: {table_idx} \ - Row index: {row_idx}. \ - Total rows: {num_rows}", - ); - } - } - - for row_idx in 0..num_rows - 1 { - let base_row = master_base_trace_table.row(row_idx); - let ext_row = master_ext_trace_table.row(row_idx); - let next_base_row = master_base_trace_table.row(row_idx + 1); - let next_ext_row = master_ext_trace_table.row(row_idx + 1); - let base_row_lifted = base_row.map(|e| e.lift()); - let next_base_row_lifted = next_base_row.map(|e| e.lift()); - let evaluated_transition_constraints = evaluate_all_transition_constraints( - base_row_lifted.view(), - ext_row, - next_base_row_lifted.view(), - next_ext_row, - &challenges, - ); - let num_transition_constraints = evaluated_transition_constraints.len(); - assert_eq!(num_all_transition_quotients(), num_transition_constraints); - for (constraint_idx, etc) in evaluated_transition_constraints.into_iter().enumerate() { - if zero != etc { - let pi_idx = - ProcessorBaseTableColumn::PreviousInstruction.master_base_table_index(); - let ci_idx = ProcessorBaseTableColumn::CI.master_base_table_index(); - let nia_idx = ProcessorBaseTableColumn::NIA.master_base_table_index(); - let pi = base_row[pi_idx].value(); - let ci = base_row[ci_idx].value(); - let nia = base_row[nia_idx].value(); - let previous_instruction = - AnInstruction::::try_from(pi).unwrap(); - let current_instruction = AnInstruction::::try_from(ci).unwrap(); - let next_instruction_str = match AnInstruction::::try_from(nia) { - Ok(instr) => format!("{instr:?}"), - Err(_) => "not an instruction".to_string(), - }; - let (table_idx, table_name) = - transition_constraint_table_idx_and_name(constraint_idx); - panic!( - "Failed transition constraint with global index {constraint_idx}. \ - Total number of transition constraints: {num_transition_constraints}. \ - Table: {table_name}. Index within table: {table_idx} \ - Row index: {row_idx}. \ - Total rows: {num_rows}\n\ - Previous Instruction: {previous_instruction:?} – opcode: {pi}\n\ - Current Instruction: {current_instruction:?} – opcode: {ci}\n\ - Next Instruction: {next_instruction_str} – opcode: {nia}\n" - ); - } - } - } - - let last_row_lifted = master_base_trace_table.row(num_rows - 1).map(|e| e.lift()); - let evaluated_terminal_constraints = evaluate_all_terminal_constraints( - last_row_lifted.view(), - master_ext_trace_table.row(num_rows - 1), + )); + assert!(processor_table::tests::constraints_evaluate_to_zero( + master_base_trace_table, + master_ext_trace_table, &challenges, - ); - let num_terminal_constraints = evaluated_terminal_constraints.len(); - assert_eq!(num_all_terminal_quotients(), num_terminal_constraints); - for (constraint_idx, etermc) in evaluated_terminal_constraints.into_iter().enumerate() { - let (table_idx, table_name) = terminal_constraint_table_idx_and_name(constraint_idx); - assert_eq!( - zero, etermc, - "Failed terminal constraint with global index {constraint_idx}. \ - Total number of terminal constraints: {num_terminal_constraints}. \ - Table: {table_name}. Index within table: {table_idx}", - ); - } + )); + assert!(op_stack_table::tests::constraints_evaluate_to_zero( + master_base_trace_table, + master_ext_trace_table, + &challenges, + )); + assert!(ram_table::tests::constraints_evaluate_to_zero( + master_base_trace_table, + master_ext_trace_table, + &challenges, + )); + assert!(jump_stack_table::tests::constraints_evaluate_to_zero( + master_base_trace_table, + master_ext_trace_table, + &challenges, + )); + assert!(hash_table::tests::constraints_evaluate_to_zero( + master_base_trace_table, + master_ext_trace_table, + &challenges, + )); + assert!(cascade_table::tests::constraints_evaluate_to_zero( + master_base_trace_table, + master_ext_trace_table, + &challenges, + )); + assert!(lookup_table::tests::constraints_evaluate_to_zero( + master_base_trace_table, + master_ext_trace_table, + &challenges, + )); + assert!(u32_table::tests::constraints_evaluate_to_zero( + master_base_trace_table, + master_ext_trace_table, + &challenges, + )); } #[test] diff --git a/triton-vm/src/table/cascade_table.rs b/triton-vm/src/table/cascade_table.rs index 2c6f1871..33061565 100644 --- a/triton-vm/src/table/cascade_table.rs +++ b/triton-vm/src/table/cascade_table.rs @@ -314,3 +314,97 @@ impl ExtCascadeTable { vec![] } } + +#[cfg(test)] +pub mod tests { + use super::*; + use num_traits::Zero; + + pub fn constraints_evaluate_to_zero( + master_base_trace_table: ArrayView2, + master_ext_trace_table: ArrayView2, + challenges: &Challenges, + ) -> bool { + let zero = XFieldElement::zero(); + assert_eq!( + master_base_trace_table.nrows(), + master_ext_trace_table.nrows() + ); + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtCascadeTable::initial_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![..1, ..]), + master_ext_trace_table.slice(s![..1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Initial constraint {constraint_idx} failed." + ); + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtCascadeTable::consistency_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 1, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Consistency constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtCascadeTable::transition_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() - 1 { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 2, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 2, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Transition constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtCascadeTable::terminal_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), + master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Terminal constraint {constraint_idx} failed." + ); + } + + true + } +} diff --git a/triton-vm/src/table/hash_table.rs b/triton-vm/src/table/hash_table.rs index c1b5828a..2692e45b 100644 --- a/triton-vm/src/table/hash_table.rs +++ b/triton-vm/src/table/hash_table.rs @@ -1769,3 +1769,94 @@ mod constraint_tests { } } } + +#[cfg(test)] +pub mod tests { + use super::*; + + pub fn constraints_evaluate_to_zero( + master_base_trace_table: ArrayView2, + master_ext_trace_table: ArrayView2, + challenges: &Challenges, + ) -> bool { + let zero = XFieldElement::zero(); + assert_eq!( + master_base_trace_table.nrows(), + master_ext_trace_table.nrows() + ); + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtHashTable::initial_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![..1, ..]), + master_ext_trace_table.slice(s![..1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Initial constraint {constraint_idx} failed." + ); + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtHashTable::consistency_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 1, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Consistency constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtHashTable::transition_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() - 1 { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 2, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 2, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Transition constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtHashTable::terminal_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), + master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Terminal constraint {constraint_idx} failed." + ); + } + + true + } +} diff --git a/triton-vm/src/table/jump_stack_table.rs b/triton-vm/src/table/jump_stack_table.rs index 873da7ed..eeb22ecc 100644 --- a/triton-vm/src/table/jump_stack_table.rs +++ b/triton-vm/src/table/jump_stack_table.rs @@ -385,3 +385,98 @@ impl Display for JumpStackTraceRow { ) } } + +#[cfg(test)] +pub mod tests { + use super::*; + use num_traits::Zero; + + pub fn constraints_evaluate_to_zero( + master_base_trace_table: ArrayView2, + master_ext_trace_table: ArrayView2, + challenges: &Challenges, + ) -> bool { + let zero = XFieldElement::zero(); + assert_eq!( + master_base_trace_table.nrows(), + master_ext_trace_table.nrows() + ); + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtJumpStackTable::initial_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![..1, ..]), + master_ext_trace_table.slice(s![..1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Initial constraint {constraint_idx} failed." + ); + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtJumpStackTable::consistency_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 1, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Consistency constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtJumpStackTable::transition_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() - 1 { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 2, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 2, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Transition constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtJumpStackTable::terminal_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), + master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Terminal constraint {constraint_idx} failed." + ); + } + + true + } +} diff --git a/triton-vm/src/table/lookup_table.rs b/triton-vm/src/table/lookup_table.rs index d49005cc..7d2d8c8c 100644 --- a/triton-vm/src/table/lookup_table.rs +++ b/triton-vm/src/table/lookup_table.rs @@ -272,3 +272,96 @@ impl ExtLookupTable { vec![narrow_table_terminal_matches_user_supplied_terminal] } } + +#[cfg(test)] +pub mod tests { + use super::*; + use num_traits::Zero; + + pub fn constraints_evaluate_to_zero( + master_base_trace_table: ArrayView2, + master_ext_trace_table: ArrayView2, + challenges: &Challenges, + ) -> bool { + let zero = XFieldElement::zero(); + assert_eq!( + master_base_trace_table.nrows(), + master_ext_trace_table.nrows() + ); + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtLookupTable::initial_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![..1, ..]), + master_ext_trace_table.slice(s![..1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Initial constraint {constraint_idx} failed." + ); + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtLookupTable::consistency_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 1, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Consistency constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtLookupTable::transition_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() - 1 { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 2, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 2, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Transition constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtLookupTable::terminal_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), + master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Terminal constraint {constraint_idx} failed." + ); + } + + true + } +} diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index c5553592..76432c36 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -1713,187 +1713,6 @@ pub fn derive_domain_generator(domain_length: u64) -> BFieldElement { BFieldElement::primitive_root_of_unity(domain_length).unwrap() } -/// Primarily for debugging purposes. -/// Given the section index of some initial constraint, returns -/// 1. the index within the specific table for that constraint, and -/// 2. the name of that table. -pub fn initial_constraint_table_idx_and_name(constraint_idx: usize) -> (usize, &'static str) { - let program_start = 0; - let program_end = program_start + ExtProgramTable::num_initial_quotients(); - let processor_start = program_end; - let processor_end = processor_start + ExtProcessorTable::num_initial_quotients(); - let op_stack_start = processor_end; - let op_stack_end = op_stack_start + ExtOpStackTable::num_initial_quotients(); - let ram_start = op_stack_end; - let ram_end = ram_start + ExtRamTable::num_initial_quotients(); - let jump_stack_start = ram_end; - let jump_stack_end = jump_stack_start + ExtJumpStackTable::num_initial_quotients(); - let hash_start = jump_stack_end; - let hash_end = hash_start + ExtHashTable::num_initial_quotients(); - let cascade_start = hash_end; - let cascade_end = cascade_start + ExtCascadeTable::num_initial_quotients(); - let lookup_start = cascade_end; - let lookup_end = lookup_start + ExtLookupTable::num_initial_quotients(); - let u32_start = lookup_end; - let u32_end = u32_start + ExtU32Table::num_initial_quotients(); - let degree_lowering_start = u32_end; - let degree_lowering_end = - degree_lowering_start + ExtDegreeLoweringTable::num_initial_quotients(); - assert_eq!(num_all_initial_quotients(), degree_lowering_end); - match constraint_idx { - i if program_start <= i && i < program_end => (i - program_start, "Program"), - i if processor_start <= i && i < processor_end => (i - processor_start, "Processor"), - i if op_stack_start <= i && i < op_stack_end => (i - op_stack_start, "OpStack"), - i if ram_start <= i && i < ram_end => (i - ram_start, "Ram"), - i if jump_stack_start <= i && i < jump_stack_end => (i - jump_stack_start, "JumpStack"), - i if hash_start <= i && i < hash_end => (i - hash_start, "Hash"), - i if cascade_start <= i && i < cascade_end => (i - cascade_start, "Cascade"), - i if lookup_start <= i && i < lookup_end => (i - lookup_start, "Lookup"), - i if u32_start <= i && i < u32_end => (i - u32_start, "U32"), - i if degree_lowering_start <= i && i < degree_lowering_end => { - (i - degree_lowering_start, "DegreeLowering") - } - _ => (0, "Unknown"), - } -} - -/// Primarily for debugging purposes. -/// Given the section index of some consistency constraint, returns -/// 1. the index within the specific table for that constraint, and -/// 2. the name of that table. -pub fn consistency_constraint_table_idx_and_name(constraint_idx: usize) -> (usize, &'static str) { - let program_start = 0; - let program_end = program_start + ExtProgramTable::num_consistency_quotients(); - let processor_start = program_end; - let processor_end = processor_start + ExtProcessorTable::num_consistency_quotients(); - let op_stack_start = processor_end; - let op_stack_end = op_stack_start + ExtOpStackTable::num_consistency_quotients(); - let ram_start = op_stack_end; - let ram_end = ram_start + ExtRamTable::num_consistency_quotients(); - let jump_stack_start = ram_end; - let jump_stack_end = jump_stack_start + ExtJumpStackTable::num_consistency_quotients(); - let hash_start = jump_stack_end; - let hash_end = hash_start + ExtHashTable::num_consistency_quotients(); - let cascade_start = hash_end; - let cascade_end = cascade_start + ExtCascadeTable::num_consistency_quotients(); - let lookup_start = cascade_end; - let lookup_end = lookup_start + ExtLookupTable::num_consistency_quotients(); - let u32_start = lookup_end; - let u32_end = u32_start + ExtU32Table::num_consistency_quotients(); - let degree_lowering_start = u32_end; - let degree_lowering_end = - degree_lowering_start + ExtDegreeLoweringTable::num_consistency_quotients(); - assert_eq!(num_all_consistency_quotients(), degree_lowering_end); - match constraint_idx { - i if program_start <= i && i < program_end => (i - program_start, "Program"), - i if processor_start <= i && i < processor_end => (i - processor_start, "Processor"), - i if op_stack_start <= i && i < op_stack_end => (i - op_stack_start, "OpStack"), - i if ram_start <= i && i < ram_end => (i - ram_start, "Ram"), - i if jump_stack_start <= i && i < jump_stack_end => (i - jump_stack_start, "JumpStack"), - i if hash_start <= i && i < hash_end => (i - hash_start, "Hash"), - i if cascade_start <= i && i < cascade_end => (i - cascade_start, "Cascade"), - i if lookup_start <= i && i < lookup_end => (i - lookup_start, "Lookup"), - i if u32_start <= i && i < u32_end => (i - u32_start, "U32"), - i if degree_lowering_start <= i && i < degree_lowering_end => { - (i - degree_lowering_start, "DegreeLowering") - } - _ => (0, "Unknown"), - } -} - -/// Primarily for debugging purposes. -/// Given the section index of some transition constraint, returns -/// 1. the index within the specific table for that constraint, and -/// 2. the name of that table. -pub fn transition_constraint_table_idx_and_name(constraint_idx: usize) -> (usize, &'static str) { - let program_start = 0; - let program_end = program_start + ExtProgramTable::num_transition_quotients(); - let processor_start = program_end; - let processor_end = processor_start + ExtProcessorTable::num_transition_quotients(); - let op_stack_start = processor_end; - let op_stack_end = op_stack_start + ExtOpStackTable::num_transition_quotients(); - let ram_start = op_stack_end; - let ram_end = ram_start + ExtRamTable::num_transition_quotients(); - let jump_stack_start = ram_end; - let jump_stack_end = jump_stack_start + ExtJumpStackTable::num_transition_quotients(); - let hash_start = jump_stack_end; - let hash_end = hash_start + ExtHashTable::num_transition_quotients(); - let cascade_start = hash_end; - let cascade_end = cascade_start + ExtCascadeTable::num_transition_quotients(); - let lookup_start = cascade_end; - let lookup_end = lookup_start + ExtLookupTable::num_transition_quotients(); - let u32_start = lookup_end; - let u32_end = u32_start + ExtU32Table::num_transition_quotients(); - let degree_lowering_start = u32_end; - let degree_lowering_end = - degree_lowering_start + ExtDegreeLoweringTable::num_transition_quotients(); - assert_eq!(num_all_transition_quotients(), degree_lowering_end); - match constraint_idx { - i if program_start <= i && i < program_end => (i - program_start, "Program"), - i if processor_start <= i && i < processor_end => (i - processor_start, "Processor"), - i if op_stack_start <= i && i < op_stack_end => (i - op_stack_start, "OpStack"), - i if ram_start <= i && i < ram_end => (i - ram_start, "Ram"), - i if jump_stack_start <= i && i < jump_stack_end => (i - jump_stack_start, "JumpStack"), - i if hash_start <= i && i < hash_end => (i - hash_start, "Hash"), - i if cascade_start <= i && i < cascade_end => (i - cascade_start, "Cascade"), - i if lookup_start <= i && i < lookup_end => (i - lookup_start, "Lookup"), - i if u32_start <= i && i < u32_end => (i - u32_start, "U32"), - i if degree_lowering_start <= i && i < degree_lowering_end => { - (i - degree_lowering_start, "DegreeLowering") - } - _ => (0, "Unknown"), - } -} - -/// Primarily for debugging purposes. -/// Given the section index of some terminal constraint, returns -/// 1. the index within the specific table for that constraint, and -/// 2. the name of that table. -pub fn terminal_constraint_table_idx_and_name(constraint_idx: usize) -> (usize, &'static str) { - let program_start = 0; - let program_end = program_start + ExtProgramTable::num_terminal_quotients(); - let processor_start = program_end; - let processor_end = processor_start + ExtProcessorTable::num_terminal_quotients(); - let op_stack_start = processor_end; - let op_stack_end = op_stack_start + ExtOpStackTable::num_terminal_quotients(); - let ram_start = op_stack_end; - let ram_end = ram_start + ExtRamTable::num_terminal_quotients(); - let jump_stack_start = ram_end; - let jump_stack_end = jump_stack_start + ExtJumpStackTable::num_terminal_quotients(); - let hash_start = jump_stack_end; - let hash_end = hash_start + ExtHashTable::num_terminal_quotients(); - let cascade_start = hash_end; - let cascade_end = cascade_start + ExtCascadeTable::num_terminal_quotients(); - let lookup_start = cascade_end; - let lookup_end = lookup_start + ExtLookupTable::num_terminal_quotients(); - let u32_start = lookup_end; - let u32_end = u32_start + ExtU32Table::num_terminal_quotients(); - let degree_lowering_start = u32_end; - let degree_lowering_end = - degree_lowering_start + ExtDegreeLoweringTable::num_terminal_quotients(); - let cross_table_start = degree_lowering_end; - let cross_table_end = cross_table_start + GrandCrossTableArg::num_terminal_quotients(); - assert_eq!(num_all_terminal_quotients(), cross_table_end); - match constraint_idx { - i if program_start <= i && i < program_end => (i - program_start, "Program"), - i if processor_start <= i && i < processor_end => (i - processor_start, "Processor"), - i if op_stack_start <= i && i < op_stack_end => (i - op_stack_start, "OpStack"), - i if ram_start <= i && i < ram_end => (i - ram_start, "Ram"), - i if jump_stack_start <= i && i < jump_stack_end => (i - jump_stack_start, "JumpStack"), - i if hash_start <= i && i < hash_end => (i - hash_start, "Hash"), - i if cascade_start <= i && i < cascade_end => (i - cascade_start, "Cascade"), - i if lookup_start <= i && i < lookup_end => (i - lookup_start, "Lookup"), - i if u32_start <= i && i < u32_end => (i - u32_start, "U32"), - i if degree_lowering_start <= i && i < degree_lowering_end => { - (i - degree_lowering_start, "DegreeLowering") - } - i if cross_table_start <= i && i < cross_table_end => { - (i - cross_table_start, "GrandCrossTableArgument") - } - _ => (0, "Unknown"), - } -} - #[cfg(test)] mod master_table_tests { use ndarray::s; diff --git a/triton-vm/src/table/op_stack_table.rs b/triton-vm/src/table/op_stack_table.rs index 2cc91b4b..0c9e6639 100644 --- a/triton-vm/src/table/op_stack_table.rs +++ b/triton-vm/src/table/op_stack_table.rs @@ -339,3 +339,97 @@ impl OpStackTable { } } } + +#[cfg(test)] +pub mod tests { + use super::*; + use num_traits::Zero; + + pub fn constraints_evaluate_to_zero( + master_base_trace_table: ArrayView2, + master_ext_trace_table: ArrayView2, + challenges: &Challenges, + ) -> bool { + let zero = XFieldElement::zero(); + assert_eq!( + master_base_trace_table.nrows(), + master_ext_trace_table.nrows() + ); + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtOpStackTable::initial_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![..1, ..]), + master_ext_trace_table.slice(s![..1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Initial constraint {constraint_idx} failed." + ); + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtOpStackTable::consistency_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 1, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Consistency constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtOpStackTable::transition_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() - 1 { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 2, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 2, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Transition constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtOpStackTable::terminal_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), + master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Terminal constraint {constraint_idx} failed." + ); + } + + true + } +} diff --git a/triton-vm/src/table/processor_table.rs b/triton-vm/src/table/processor_table.rs index 21fb0291..54a514b1 100644 --- a/triton-vm/src/table/processor_table.rs +++ b/triton-vm/src/table/processor_table.rs @@ -3467,3 +3467,97 @@ mod constraint_polynomial_tests { } } } + +#[cfg(test)] +pub mod tests { + use super::*; + + pub fn constraints_evaluate_to_zero( + master_base_trace_table: ArrayView2, + master_ext_trace_table: ArrayView2, + challenges: &Challenges, + ) -> bool { + let zero = XFieldElement::zero(); + assert_eq!( + master_base_trace_table.nrows(), + master_ext_trace_table.nrows() + ); + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtProcessorTable::initial_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![..1, ..]), + master_ext_trace_table.slice(s![..1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Initial constraint {constraint_idx} failed." + ); + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtProcessorTable::consistency_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 1, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Consistency constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtProcessorTable::transition_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() - 1 { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 2, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 2, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Transition constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtProcessorTable::terminal_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), + master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Terminal constraint {constraint_idx} failed." + ); + } + + true + } +} diff --git a/triton-vm/src/table/program_table.rs b/triton-vm/src/table/program_table.rs index b12d8289..00e88f30 100644 --- a/triton-vm/src/table/program_table.rs +++ b/triton-vm/src/table/program_table.rs @@ -216,3 +216,96 @@ impl ProgramTable { instruction_lookup_log_derivative; } } + +#[cfg(test)] +pub mod tests { + use super::*; + + pub fn constraints_evaluate_to_zero( + master_base_trace_table: ArrayView2, + master_ext_trace_table: ArrayView2, + challenges: &Challenges, + ) -> bool { + let zero = XFieldElement::zero(); + assert_eq!( + master_base_trace_table.nrows(), + master_ext_trace_table.nrows() + ); + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtProgramTable::initial_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![..1, ..]), + master_ext_trace_table.slice(s![..1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Initial constraint {constraint_idx} failed." + ); + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtProgramTable::consistency_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 1, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Consistency constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in + ExtProgramTable::transition_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() - 1 { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 2, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 2, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Transition constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtProgramTable::terminal_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), + master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Terminal constraint {constraint_idx} failed." + ); + } + + true + } +} diff --git a/triton-vm/src/table/ram_table.rs b/triton-vm/src/table/ram_table.rs index 2c65c038..ae902998 100644 --- a/triton-vm/src/table/ram_table.rs +++ b/triton-vm/src/table/ram_table.rs @@ -541,3 +541,94 @@ impl ExtRamTable { vec![bezout_relation_holds] } } + +#[cfg(test)] +pub mod tests { + use super::*; + + pub fn constraints_evaluate_to_zero( + master_base_trace_table: ArrayView2, + master_ext_trace_table: ArrayView2, + challenges: &Challenges, + ) -> bool { + let zero = XFieldElement::zero(); + assert_eq!( + master_base_trace_table.nrows(), + master_ext_trace_table.nrows() + ); + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtRamTable::initial_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![..1, ..]), + master_ext_trace_table.slice(s![..1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Initial constraint {constraint_idx} failed." + ); + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtRamTable::consistency_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 1, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Consistency constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtRamTable::transition_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() - 1 { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 2, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 2, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Transition constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtRamTable::terminal_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), + master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Terminal constraint {constraint_idx} failed." + ); + } + + true + } +} diff --git a/triton-vm/src/table/u32_table.rs b/triton-vm/src/table/u32_table.rs index 7f1efdab..f9691633 100644 --- a/triton-vm/src/table/u32_table.rs +++ b/triton-vm/src/table/u32_table.rs @@ -576,3 +576,94 @@ impl U32Table { } } } + +#[cfg(test)] +pub mod tests { + use super::*; + + pub fn constraints_evaluate_to_zero( + master_base_trace_table: ArrayView2, + master_ext_trace_table: ArrayView2, + challenges: &Challenges, + ) -> bool { + let zero = XFieldElement::zero(); + assert_eq!( + master_base_trace_table.nrows(), + master_ext_trace_table.nrows() + ); + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtU32Table::initial_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![..1, ..]), + master_ext_trace_table.slice(s![..1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Initial constraint {constraint_idx} failed." + ); + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtU32Table::consistency_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 1, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 1, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Consistency constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtU32Table::transition_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + for row_idx in 0..master_base_trace_table.nrows() - 1 { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![row_idx..row_idx + 2, ..]), + master_ext_trace_table.slice(s![row_idx..row_idx + 2, ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Transition constraint {constraint_idx} failed on row {row_idx}." + ); + } + } + + let circuit_builder = ConstraintCircuitBuilder::new(); + for (constraint_idx, constraint) in ExtU32Table::terminal_constraints(&circuit_builder) + .into_iter() + .map(|constraint_monad| constraint_monad.consume()) + .enumerate() + { + let evaluated_constraint = constraint.evaluate( + master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), + master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + challenges, + ); + assert_eq!( + zero, evaluated_constraint, + "Terminal constraint {constraint_idx} failed." + ); + } + + true + } +} From f5c807d9bf4d91556e22079a795dfab7b820b7da Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 9 May 2023 14:53:40 +0200 Subject: [PATCH 21/72] remove some superfluous casts in generated code --- constraint-evaluation-generator/src/main.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 898ce53e..0cff4c6b 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -335,7 +335,7 @@ impl Quotientable for {table_mod_name} {{ fn initial_quotient_degree_bounds( interpolant_degree: Degree, ) -> Vec {{ - let zerofier_degree = 1 as Degree; + let zerofier_degree = 1; [{initial_constraints_degrees}].to_vec() }} @@ -361,7 +361,7 @@ impl Quotientable for {table_mod_name} {{ fn terminal_quotient_degree_bounds( interpolant_degree: Degree, ) -> Vec {{ - let zerofier_degree = 1 as Degree; + let zerofier_degree = 1; [{terminal_constraints_degrees}].to_vec() }} }} @@ -418,8 +418,11 @@ fn turn_circuits_into_string( let degree_bounds_string = base_constraints .iter() .chain(ext_constraints.iter()) - .map(|circuit| circuit.degree()) - .map(|degree| format!("interpolant_degree * {degree} as Degree - zerofier_degree")) + .map(|circuit| match circuit.degree() { + d if d > 1 => format!("interpolant_degree * {d} - zerofier_degree"), + d if d == 1 => "interpolant_degree - zerofier_degree".to_string(), + _ => unreachable!("Constraint degree must be positive"), + }) .join(",\n"); let build_constraint_evaluation_code = |constraints: &[&ConstraintCircuit]| { From 6c8456144d5c67d26413fd3254365133319133d5 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 10 May 2023 10:56:36 +0200 Subject: [PATCH 22/72] put all constraints in one file --- constraint-evaluation-generator/src/main.rs | 247 ++--- triton-vm/src/stark.rs | 523 +++-------- triton-vm/src/table/constraints.rs | 24 +- .../constraints/cascade_table_constraints.rs | 14 - .../cross_table_argument_constraints.rs | 14 - .../constraints/hash_table_constraints.rs | 14 - .../jump_stack_table_constraints.rs | 14 - .../constraints/lookup_table_constraints.rs | 14 - .../constraints/op_stack_table_constraints.rs | 14 - .../processor_table_constraints.rs | 14 - .../constraints/program_table_constraints.rs | 14 - .../constraints/ram_table_constraints.rs | 14 - .../constraints/u32_table_constraints.rs | 14 - triton-vm/src/table/hash_table.rs | 95 +- triton-vm/src/table/master_table.rs | 874 +----------------- 15 files changed, 255 insertions(+), 1648 deletions(-) delete mode 100644 triton-vm/src/table/constraints/cascade_table_constraints.rs delete mode 100644 triton-vm/src/table/constraints/cross_table_argument_constraints.rs delete mode 100644 triton-vm/src/table/constraints/hash_table_constraints.rs delete mode 100644 triton-vm/src/table/constraints/jump_stack_table_constraints.rs delete mode 100644 triton-vm/src/table/constraints/lookup_table_constraints.rs delete mode 100644 triton-vm/src/table/constraints/op_stack_table_constraints.rs delete mode 100644 triton-vm/src/table/constraints/processor_table_constraints.rs delete mode 100644 triton-vm/src/table/constraints/program_table_constraints.rs delete mode 100644 triton-vm/src/table/constraints/ram_table_constraints.rs delete mode 100644 triton-vm/src/table/constraints/u32_table_constraints.rs diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 0cff4c6b..28c4c1e5 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -22,163 +22,94 @@ use triton_vm::table::ram_table::ExtRamTable; use triton_vm::table::u32_table::ExtU32Table; fn main() { - let (table_name_snake, table_name_camel) = construct_needed_table_identifiers(&["program"]); - let source_code = gen( - &table_name_snake, - &table_name_camel, - &mut build_fold_circuitify(&ExtProgramTable::initial_constraints), - &mut build_fold_circuitify(&ExtProgramTable::consistency_constraints), - &mut build_fold_circuitify(&ExtProgramTable::transition_constraints), - &mut build_fold_circuitify(&ExtProgramTable::terminal_constraints), - ); - write(&table_name_snake, source_code); - - let (table_name_snake, table_name_camel) = construct_needed_table_identifiers(&["processor"]); - let source_code = gen( - &table_name_snake, - &table_name_camel, - &mut build_fold_circuitify(&ExtProcessorTable::initial_constraints), - &mut build_fold_circuitify(&ExtProcessorTable::consistency_constraints), - &mut build_fold_circuitify(&ExtProcessorTable::transition_constraints), - &mut build_fold_circuitify(&ExtProcessorTable::terminal_constraints), - ); - write(&table_name_snake, source_code); - - let (table_name_snake, table_name_camel) = construct_needed_table_identifiers(&["op", "stack"]); - let source_code = gen( - &table_name_snake, - &table_name_camel, - &mut build_fold_circuitify(&ExtOpStackTable::initial_constraints), - &mut build_fold_circuitify(&ExtOpStackTable::consistency_constraints), - &mut build_fold_circuitify(&ExtOpStackTable::transition_constraints), - &mut build_fold_circuitify(&ExtOpStackTable::terminal_constraints), - ); - write(&table_name_snake, source_code); - - let (table_name_snake, table_name_camel) = construct_needed_table_identifiers(&["ram"]); - let source_code = gen( - &table_name_snake, - &table_name_camel, - &mut build_fold_circuitify(&ExtRamTable::initial_constraints), - &mut build_fold_circuitify(&ExtRamTable::consistency_constraints), - &mut build_fold_circuitify(&ExtRamTable::transition_constraints), - &mut build_fold_circuitify(&ExtRamTable::terminal_constraints), - ); - write(&table_name_snake, source_code); - - let (table_name_snake, table_name_camel) = - construct_needed_table_identifiers(&["jump", "stack"]); - let source_code = gen( - &table_name_snake, - &table_name_camel, - &mut build_fold_circuitify(&ExtJumpStackTable::initial_constraints), - &mut build_fold_circuitify(&ExtJumpStackTable::consistency_constraints), - &mut build_fold_circuitify(&ExtJumpStackTable::transition_constraints), - &mut build_fold_circuitify(&ExtJumpStackTable::terminal_constraints), - ); - write(&table_name_snake, source_code); - - let (table_name_snake, table_name_camel) = construct_needed_table_identifiers(&["hash"]); - let source_code = gen( - &table_name_snake, - &table_name_camel, - &mut build_fold_circuitify(&ExtHashTable::initial_constraints), - &mut build_fold_circuitify(&ExtHashTable::consistency_constraints), - &mut build_fold_circuitify(&ExtHashTable::transition_constraints), - &mut build_fold_circuitify(&ExtHashTable::terminal_constraints), - ); - write(&table_name_snake, source_code); - - let (table_name_snake, table_name_camel) = construct_needed_table_identifiers(&["cascade"]); - let source_code = gen( - &table_name_snake, - &table_name_camel, - &mut build_fold_circuitify(&ExtCascadeTable::initial_constraints), - &mut build_fold_circuitify(&ExtCascadeTable::consistency_constraints), - &mut build_fold_circuitify(&ExtCascadeTable::transition_constraints), - &mut build_fold_circuitify(&ExtCascadeTable::terminal_constraints), - ); - write(&table_name_snake, source_code); - - let (table_name_snake, table_name_camel) = construct_needed_table_identifiers(&["lookup"]); - let source_code = gen( - &table_name_snake, - &table_name_camel, - &mut build_fold_circuitify(&ExtLookupTable::initial_constraints), - &mut build_fold_circuitify(&ExtLookupTable::consistency_constraints), - &mut build_fold_circuitify(&ExtLookupTable::transition_constraints), - &mut build_fold_circuitify(&ExtLookupTable::terminal_constraints), - ); - write(&table_name_snake, source_code); - - let (table_name_snake, table_name_camel) = construct_needed_table_identifiers(&["u32"]); - let source_code = gen( - &table_name_snake, - &table_name_camel, - &mut build_fold_circuitify(&ExtU32Table::initial_constraints), - &mut build_fold_circuitify(&ExtU32Table::consistency_constraints), - &mut build_fold_circuitify(&ExtU32Table::transition_constraints), - &mut build_fold_circuitify(&ExtU32Table::terminal_constraints), - ); - write(&table_name_snake, source_code); - - let table_name_snake = "cross_table_argument"; - let table_name_camel = "GrandCrossTableArg"; - let source_code = gen( - table_name_snake, - table_name_camel, - &mut build_fold_circuitify(&GrandCrossTableArg::initial_constraints), - &mut build_fold_circuitify(&GrandCrossTableArg::consistency_constraints), - &mut build_fold_circuitify(&GrandCrossTableArg::transition_constraints), - &mut build_fold_circuitify(&GrandCrossTableArg::terminal_constraints), + let circuit_builder = ConstraintCircuitBuilder::new(); + let initial_constraints = vec![ + ExtProgramTable::initial_constraints(&circuit_builder), + ExtProcessorTable::initial_constraints(&circuit_builder), + ExtOpStackTable::initial_constraints(&circuit_builder), + ExtRamTable::initial_constraints(&circuit_builder), + ExtJumpStackTable::initial_constraints(&circuit_builder), + ExtHashTable::initial_constraints(&circuit_builder), + ExtCascadeTable::initial_constraints(&circuit_builder), + ExtLookupTable::initial_constraints(&circuit_builder), + ExtU32Table::initial_constraints(&circuit_builder), + GrandCrossTableArg::initial_constraints(&circuit_builder), + ] + .concat(); + + let circuit_builder = ConstraintCircuitBuilder::new(); + let consistency_constraints = vec![ + ExtProgramTable::consistency_constraints(&circuit_builder), + ExtProcessorTable::consistency_constraints(&circuit_builder), + ExtOpStackTable::consistency_constraints(&circuit_builder), + ExtRamTable::consistency_constraints(&circuit_builder), + ExtJumpStackTable::consistency_constraints(&circuit_builder), + ExtHashTable::consistency_constraints(&circuit_builder), + ExtCascadeTable::consistency_constraints(&circuit_builder), + ExtLookupTable::consistency_constraints(&circuit_builder), + ExtU32Table::consistency_constraints(&circuit_builder), + GrandCrossTableArg::consistency_constraints(&circuit_builder), + ] + .concat(); + + let circuit_builder = ConstraintCircuitBuilder::new(); + let transition_constraints = vec![ + ExtProgramTable::transition_constraints(&circuit_builder), + ExtProcessorTable::transition_constraints(&circuit_builder), + ExtOpStackTable::transition_constraints(&circuit_builder), + ExtRamTable::transition_constraints(&circuit_builder), + ExtJumpStackTable::transition_constraints(&circuit_builder), + ExtHashTable::transition_constraints(&circuit_builder), + ExtCascadeTable::transition_constraints(&circuit_builder), + ExtLookupTable::transition_constraints(&circuit_builder), + ExtU32Table::transition_constraints(&circuit_builder), + GrandCrossTableArg::transition_constraints(&circuit_builder), + ] + .concat(); + + let circuit_builder = ConstraintCircuitBuilder::new(); + let terminal_constraints = vec![ + ExtProgramTable::terminal_constraints(&circuit_builder), + ExtProcessorTable::terminal_constraints(&circuit_builder), + ExtOpStackTable::terminal_constraints(&circuit_builder), + ExtRamTable::terminal_constraints(&circuit_builder), + ExtJumpStackTable::terminal_constraints(&circuit_builder), + ExtHashTable::terminal_constraints(&circuit_builder), + ExtCascadeTable::terminal_constraints(&circuit_builder), + ExtLookupTable::terminal_constraints(&circuit_builder), + ExtU32Table::terminal_constraints(&circuit_builder), + GrandCrossTableArg::terminal_constraints(&circuit_builder), + ] + .concat(); + + let mut initial_constraints = fold_and_consume(initial_constraints); + let mut consistency_constraints = fold_and_consume(consistency_constraints); + let mut transition_constraints = fold_and_consume(transition_constraints); + let mut terminal_constraints = fold_and_consume(terminal_constraints); + + let code = gen( + &mut initial_constraints, + &mut consistency_constraints, + &mut transition_constraints, + &mut terminal_constraints, ); - write(table_name_snake, source_code); + + std::fs::write("triton-vm/src/table/constraints.rs", code) + .expect("Writing to disk has failed."); if let Err(fmt_failed) = Command::new("cargo").arg("fmt").output() { println!("cargo fmt failed: {fmt_failed}"); } } -/// Get the constraints defined in the given function, perform constant folding, and return -/// them as a vector of `ConstraintCircuit`s. -fn build_fold_circuitify( - circuit_monad_function: &dyn Fn( - &ConstraintCircuitBuilder, - ) -> Vec>, +fn fold_and_consume( + mut constraints: Vec>, ) -> Vec> { - let circuit_builder = ConstraintCircuitBuilder::new(); - let mut constraints = circuit_monad_function(&circuit_builder); ConstraintCircuitMonad::constant_folding(&mut constraints); - constraints - .into_iter() - .map(|circuit| circuit.consume()) - .collect() -} - -fn construct_needed_table_identifiers(table_name_constituents: &[&str]) -> (String, String) { - let table_name_snake = format!("{}_table", table_name_constituents.join("_")); - let title_case = table_name_constituents - .iter() - .map(|part| { - let (first_char, rest) = part.split_at(1); - let first_char_upper = first_char.to_uppercase(); - format!("{first_char_upper}{rest}") - }) - .collect_vec(); - let table_name_camel = format!("Ext{}Table", title_case.iter().join("")); - (table_name_snake, table_name_camel) -} - -fn write(table_name_snake: &str, rust_source_code: String) { - let output_filename = - format!("triton-vm/src/table/constraints/{table_name_snake}_constraints.rs"); - - std::fs::write(output_filename, rust_source_code).expect("Write Rust source code"); + constraints.into_iter().map(|c| c.consume()).collect() } fn gen( - table_name_snake: &str, - table_mod_name: &str, initial_constraint_circuits: &mut [ConstraintCircuit], consistency_constraint_circuits: &mut [ConstraintCircuit], transition_constraint_circuits: &mut [ConstraintCircuit], @@ -190,22 +121,22 @@ fn gen( let num_terminal_constraints = terminal_constraint_circuits.len(); let ( - initial_constraints_degrees, + initial_constraint_degrees, initial_constraint_strings_bfe, initial_constraint_strings_xfe, ) = turn_circuits_into_string(initial_constraint_circuits); let ( - consistency_constraints_degrees, + consistency_constraint_degrees, consistency_constraint_strings_bfe, consistency_constraint_strings_xfe, ) = turn_circuits_into_string(consistency_constraint_circuits); let ( - transition_constraints_degrees, + transition_constraint_degrees, transition_constraint_strings_bfe, transition_constraint_strings_xfe, ) = turn_circuits_into_string(transition_constraint_circuits); let ( - terminal_constraints_degrees, + terminal_constraint_degrees, terminal_constraint_strings_bfe, terminal_constraint_strings_xfe, ) = turn_circuits_into_string(terminal_constraint_circuits); @@ -221,12 +152,12 @@ use crate::table::challenges::Challenges; use crate::table::challenges::ChallengeId::*; use crate::table::extension_table::Evaluable; use crate::table::extension_table::Quotientable; -use crate::table::{table_name_snake}::{table_mod_name}; +use crate::table::master_table::MasterExtTable; // This file has been auto-generated. Any modifications _will_ be lost. // To re-generate, execute: // `cargo run --bin constraint-evaluation-generator` -impl Evaluable for {table_mod_name} {{ +impl Evaluable for MasterExtTable {{ #[inline] #[allow(unused_variables)] fn evaluate_initial_constraints( @@ -270,7 +201,7 @@ impl Evaluable for {table_mod_name} {{ }} }} -impl Evaluable for {table_mod_name} {{ +impl Evaluable for MasterExtTable {{ #[inline] #[allow(unused_variables)] fn evaluate_initial_constraints( @@ -314,7 +245,7 @@ impl Evaluable for {table_mod_name} {{ }} }} -impl Quotientable for {table_mod_name} {{ +impl Quotientable for MasterExtTable {{ fn num_initial_quotients() -> usize {{ {num_initial_constraints} }} @@ -336,7 +267,7 @@ impl Quotientable for {table_mod_name} {{ interpolant_degree: Degree, ) -> Vec {{ let zerofier_degree = 1; - [{initial_constraints_degrees}].to_vec() + [{initial_constraint_degrees}].to_vec() }} #[allow(unused_variables)] @@ -345,7 +276,7 @@ impl Quotientable for {table_mod_name} {{ padded_height: usize, ) -> Vec {{ let zerofier_degree = padded_height as Degree; - [{consistency_constraints_degrees}].to_vec() + [{consistency_constraint_degrees}].to_vec() }} #[allow(unused_variables)] @@ -354,7 +285,7 @@ impl Quotientable for {table_mod_name} {{ padded_height: usize, ) -> Vec {{ let zerofier_degree = padded_height as Degree - 1; - [{transition_constraints_degrees}].to_vec() + [{transition_constraint_degrees}].to_vec() }} #[allow(unused_variables)] @@ -362,7 +293,7 @@ impl Quotientable for {table_mod_name} {{ interpolant_degree: Degree, ) -> Vec {{ let zerofier_degree = 1; - [{terminal_constraints_degrees}].to_vec() + [{terminal_constraint_degrees}].to_vec() }} }} " @@ -520,7 +451,7 @@ fn declare_single_node_with_visit_count( requested_visited_count, scope, ); - return [out_left, out_right].join("\n"); + return [out_left, out_right].join(""); } // Declare a new binding. diff --git a/triton-vm/src/stark.rs b/triton-vm/src/stark.rs index 4c2821f8..c1c1dadf 100644 --- a/triton-vm/src/stark.rs +++ b/triton-vm/src/stark.rs @@ -42,6 +42,7 @@ use crate::proof::Proof; use crate::proof_item::ProofItem; use crate::proof_stream::ProofStream; use crate::table::challenges::Challenges; +use crate::table::extension_table::Evaluable; use crate::table::master_table::*; use crate::vm::AlgebraicExecutionTrace; @@ -235,8 +236,8 @@ impl Stark { // Create quotient codeword. This is a part of the combination codeword. To reduce the // amount of hashing necessary, the quotient codeword is linearly summed instead of // hashed prior to committing to it. - let quotient_combination_weights = - Array1::from(proof_stream.sample_scalars(num_all_table_quotients())); + let quotient_combination_weights = proof_stream.sample_scalars(num_quotients()); + let quotient_combination_weights = Array1::from(quotient_combination_weights); assert_eq!( quotient_combination_weights.len(), master_quotient_table.ncols() @@ -247,9 +248,10 @@ impl Stark { let quotient_codeword = weighted_codewords.sum_axis(Axis(1)); assert_eq!(quotient_domain.length, quotient_codeword.len()); + prof_stop!(maybe_profiler, "linearly combine quotient codewords"); + #[cfg(debug_assertions)] Self::debug_check_degree("ient_codeword.to_vec(), quotient_domain, max_degree); - prof_stop!(maybe_profiler, "linearly combine quotient codewords"); prof_start!(maybe_profiler, "commit to quotient codeword"); prof_start!(maybe_profiler, "LDE", "LDE"); @@ -598,8 +600,8 @@ impl Stark { let extension_tree_merkle_root = proof_stream.dequeue(true)?.as_merkle_root()?; // Sample weights for quotient codeword, which is a part of the combination codeword. // See corresponding part in the prover for a more detailed explanation. - let quot_codeword_weights = - Array1::from(proof_stream.sample_scalars(num_all_table_quotients())); + let quot_codeword_weights = proof_stream.sample_scalars(num_quotients()); + let quot_codeword_weights = Array1::from(quot_codeword_weights); let quotient_codeword_merkle_root = proof_stream.dequeue(true)?.as_merkle_root()?; prof_stop!(maybe_profiler, "Fiat-Shamir 1"); @@ -633,24 +635,24 @@ impl Stark { prof_stop!(maybe_profiler, "zerofiers"); prof_start!(maybe_profiler, "evaluate AIR", "AIR"); - let evaluated_initial_constraints = evaluate_all_initial_constraints( + let evaluated_initial_constraints = MasterExtTable::evaluate_initial_constraints( out_of_domain_curr_base_row.view(), out_of_domain_curr_ext_row.view(), &challenges, ); - let evaluated_consistency_constraints = evaluate_all_consistency_constraints( + let evaluated_consistency_constraints = MasterExtTable::evaluate_consistency_constraints( out_of_domain_curr_base_row.view(), out_of_domain_curr_ext_row.view(), &challenges, ); - let evaluated_transition_constraints = evaluate_all_transition_constraints( + let evaluated_transition_constraints = MasterExtTable::evaluate_transition_constraints( out_of_domain_curr_base_row.view(), out_of_domain_curr_ext_row.view(), out_of_domain_next_base_row.view(), out_of_domain_next_ext_row.view(), &challenges, ); - let evaluated_terminal_constraints = evaluate_all_terminal_constraints( + let evaluated_terminal_constraints = MasterExtTable::evaluate_terminal_constraints( out_of_domain_curr_base_row.view(), out_of_domain_curr_ext_row.view(), &challenges, @@ -658,7 +660,7 @@ impl Stark { prof_stop!(maybe_profiler, "evaluate AIR"); prof_start!(maybe_profiler, "divide"); - let mut quotient_summands = Vec::with_capacity(num_all_table_quotients()); + let mut quotient_summands = Vec::with_capacity(num_quotients()); for (evaluated_constraints_category, zerofier_inverse) in [ (evaluated_initial_constraints, initial_zerofier_inv), (evaluated_consistency_constraints, consistency_zerofier_inv), @@ -907,10 +909,12 @@ pub(crate) mod triton_stark_tests { use crate::shared_tests::*; use crate::table::cascade_table; use crate::table::cascade_table::ExtCascadeTable; + use crate::table::challenges::ChallengeId::LookupTablePublicTerminal; use crate::table::challenges::ChallengeId::StandardInputIndeterminate; use crate::table::challenges::ChallengeId::StandardInputTerminal; use crate::table::challenges::ChallengeId::StandardOutputIndeterminate; use crate::table::challenges::ChallengeId::StandardOutputTerminal; + use crate::table::constraint_circuit::ConstraintCircuitBuilder; use crate::table::cross_table_argument::CrossTableArg; use crate::table::cross_table_argument::EvalArg; use crate::table::cross_table_argument::GrandCrossTableArg; @@ -924,6 +928,7 @@ pub(crate) mod triton_stark_tests { use crate::table::lookup_table::ExtLookupTable; use crate::table::master_table::all_degrees_with_origin; use crate::table::master_table::MasterExtTable; + use crate::table::master_table::TableId::LookupTable; use crate::table::master_table::TableId::ProcessorTable; use crate::table::op_stack_table; use crate::table::op_stack_table::ExtOpStackTable; @@ -933,6 +938,7 @@ pub(crate) mod triton_stark_tests { use crate::table::program_table::ExtProgramTable; use crate::table::ram_table; use crate::table::ram_table::ExtRamTable; + use crate::table::table_column::LookupExtTableColumn::PublicEvaluationArgument; use crate::table::table_column::MasterBaseTableColumn; use crate::table::table_column::MasterExtTableColumn; use crate::table::table_column::ProcessorBaseTableColumn; @@ -1179,40 +1185,52 @@ pub(crate) mod triton_stark_tests { let mut code_collection = small_tasm_test_programs(); code_collection.append(&mut property_based_test_programs()); + let zero = XFieldElement::zero(); + let circuit_builder = ConstraintCircuitBuilder::new(); + let terminal_constraints = GrandCrossTableArg::terminal_constraints(&circuit_builder); + let terminal_constraints = terminal_constraints + .into_iter() + .map(|c| c.consume()) + .collect_vec(); + for (code_idx, code_with_input) in code_collection.into_iter().enumerate() { println!("Checking Grand Cross-Table Argument for TASM snippet {code_idx}."); let code = code_with_input.source_code; let input = code_with_input.input; let secret_input = code_with_input.secret_input.clone(); - let (_, _, _, master_base_table, master_ext_table, all_challenges) = + let (_, _, _, master_base_table, master_ext_table, challenges) = parse_simulate_pad_extend(&code, input, secret_input); let processor_table = master_ext_table.table(ProcessorTable); let processor_table_last_row = processor_table.slice(s![-1, ..]); assert_eq!( - all_challenges.get_challenge(StandardInputTerminal), + challenges.get_challenge(StandardInputTerminal), processor_table_last_row[InputTableEvalArg.ext_table_index()], "The input terminal must match for TASM snippet #{code_idx}." ); assert_eq!( - all_challenges.get_challenge(StandardOutputTerminal), + challenges.get_challenge(StandardOutputTerminal), processor_table_last_row[OutputTableEvalArg.ext_table_index()], "The output terminal must match for TASM snippet #{code_idx}." ); + let lookup_table = master_ext_table.table(LookupTable); + let lookup_table_last_row = lookup_table.slice(s![-1, ..]); + assert_eq!( + challenges.get_challenge(LookupTablePublicTerminal), + lookup_table_last_row[PublicEvaluationArgument.ext_table_index()], + "The lookup's terminal must match for TASM snippet #{code_idx}." + ); + let master_base_trace_table = master_base_table.trace_table(); let master_ext_trace_table = master_ext_table.trace_table(); - let last_master_base_row = master_base_trace_table.slice(s![-1, ..]).map(|e| e.lift()); - let last_master_base_row = last_master_base_row.view(); - let last_master_ext_row = master_ext_trace_table.slice(s![-1, ..]); - let evaluated_terminal_constraints = GrandCrossTableArg::evaluate_terminal_constraints( - last_master_base_row, - last_master_ext_row, - &all_challenges, - ); - for (i, evaluation) in evaluated_terminal_constraints.iter().enumerate() { - assert!( - evaluation.is_zero(), + let last_master_base_row = master_base_trace_table.slice(s![-1.., ..]); + let last_master_ext_row = master_ext_trace_table.slice(s![-1.., ..]); + + for (i, constraint) in terminal_constraints.iter().enumerate() { + assert_eq!( + zero, + constraint.evaluate(last_master_base_row, last_master_ext_row, &challenges), "Terminal constraint {i} must evaluate to 0 for snippet #{code_idx}." ); } @@ -1228,40 +1246,10 @@ pub(crate) mod triton_stark_tests { let br = base_row.view(); let er = ext_row.view(); - ExtProgramTable::evaluate_initial_constraints(br, er, &challenges); - ExtProgramTable::evaluate_consistency_constraints(br, er, &challenges); - ExtProgramTable::evaluate_transition_constraints(br, er, br, er, &challenges); - ExtProgramTable::evaluate_terminal_constraints(br, er, &challenges); - - ExtProcessorTable::evaluate_initial_constraints(br, er, &challenges); - ExtProcessorTable::evaluate_consistency_constraints(br, er, &challenges); - ExtProcessorTable::evaluate_transition_constraints(br, er, br, er, &challenges); - ExtProcessorTable::evaluate_terminal_constraints(br, er, &challenges); - - ExtOpStackTable::evaluate_initial_constraints(br, er, &challenges); - ExtOpStackTable::evaluate_consistency_constraints(br, er, &challenges); - ExtOpStackTable::evaluate_transition_constraints(br, er, br, er, &challenges); - ExtOpStackTable::evaluate_terminal_constraints(br, er, &challenges); - - ExtRamTable::evaluate_initial_constraints(br, er, &challenges); - ExtRamTable::evaluate_consistency_constraints(br, er, &challenges); - ExtRamTable::evaluate_transition_constraints(br, er, br, er, &challenges); - ExtRamTable::evaluate_terminal_constraints(br, er, &challenges); - - ExtJumpStackTable::evaluate_initial_constraints(br, er, &challenges); - ExtJumpStackTable::evaluate_consistency_constraints(br, er, &challenges); - ExtJumpStackTable::evaluate_transition_constraints(br, er, br, er, &challenges); - ExtJumpStackTable::evaluate_terminal_constraints(br, er, &challenges); - - ExtHashTable::evaluate_initial_constraints(br, er, &challenges); - ExtHashTable::evaluate_consistency_constraints(br, er, &challenges); - ExtHashTable::evaluate_transition_constraints(br, er, br, er, &challenges); - ExtHashTable::evaluate_terminal_constraints(br, er, &challenges); - - ExtU32Table::evaluate_initial_constraints(br, er, &challenges); - ExtU32Table::evaluate_consistency_constraints(br, er, &challenges); - ExtU32Table::evaluate_transition_constraints(br, er, br, er, &challenges); - ExtU32Table::evaluate_terminal_constraints(br, er, &challenges); + MasterExtTable::evaluate_initial_constraints(br, er, &challenges); + MasterExtTable::evaluate_consistency_constraints(br, er, &challenges); + MasterExtTable::evaluate_transition_constraints(br, er, br, er, &challenges); + MasterExtTable::evaluate_terminal_constraints(br, er, &challenges); } #[test] @@ -1278,54 +1266,62 @@ pub(crate) mod triton_stark_tests { "u32 table", "cross-table arg", ]; + let circuit_builder = ConstraintCircuitBuilder::new(); let all_init = [ - ExtProgramTable::num_initial_quotients(), - ExtProcessorTable::num_initial_quotients(), - ExtOpStackTable::num_initial_quotients(), - ExtRamTable::num_initial_quotients(), - ExtJumpStackTable::num_initial_quotients(), - ExtHashTable::num_initial_quotients(), - ExtCascadeTable::num_initial_quotients(), - ExtLookupTable::num_initial_quotients(), - ExtU32Table::num_initial_quotients(), - GrandCrossTableArg::num_initial_quotients(), - ]; + ExtProgramTable::initial_constraints(&circuit_builder), + ExtProcessorTable::initial_constraints(&circuit_builder), + ExtOpStackTable::initial_constraints(&circuit_builder), + ExtRamTable::initial_constraints(&circuit_builder), + ExtJumpStackTable::initial_constraints(&circuit_builder), + ExtHashTable::initial_constraints(&circuit_builder), + ExtCascadeTable::initial_constraints(&circuit_builder), + ExtLookupTable::initial_constraints(&circuit_builder), + ExtU32Table::initial_constraints(&circuit_builder), + GrandCrossTableArg::initial_constraints(&circuit_builder), + ] + .map(|vec| vec.len()); + let circuit_builder = ConstraintCircuitBuilder::new(); let all_cons = [ - ExtProgramTable::num_consistency_quotients(), - ExtProcessorTable::num_consistency_quotients(), - ExtOpStackTable::num_consistency_quotients(), - ExtRamTable::num_consistency_quotients(), - ExtJumpStackTable::num_consistency_quotients(), - ExtHashTable::num_consistency_quotients(), - ExtCascadeTable::num_consistency_quotients(), - ExtLookupTable::num_consistency_quotients(), - ExtU32Table::num_consistency_quotients(), - GrandCrossTableArg::num_consistency_quotients(), - ]; + ExtProgramTable::consistency_constraints(&circuit_builder), + ExtProcessorTable::consistency_constraints(&circuit_builder), + ExtOpStackTable::consistency_constraints(&circuit_builder), + ExtRamTable::consistency_constraints(&circuit_builder), + ExtJumpStackTable::consistency_constraints(&circuit_builder), + ExtHashTable::consistency_constraints(&circuit_builder), + ExtCascadeTable::consistency_constraints(&circuit_builder), + ExtLookupTable::consistency_constraints(&circuit_builder), + ExtU32Table::consistency_constraints(&circuit_builder), + GrandCrossTableArg::consistency_constraints(&circuit_builder), + ] + .map(|vec| vec.len()); + let circuit_builder = ConstraintCircuitBuilder::new(); let all_trans = [ - ExtProgramTable::num_transition_quotients(), - ExtProcessorTable::num_transition_quotients(), - ExtOpStackTable::num_transition_quotients(), - ExtRamTable::num_transition_quotients(), - ExtJumpStackTable::num_transition_quotients(), - ExtHashTable::num_transition_quotients(), - ExtCascadeTable::num_transition_quotients(), - ExtLookupTable::num_transition_quotients(), - ExtU32Table::num_transition_quotients(), - GrandCrossTableArg::num_transition_quotients(), - ]; + ExtProgramTable::transition_constraints(&circuit_builder), + ExtProcessorTable::transition_constraints(&circuit_builder), + ExtOpStackTable::transition_constraints(&circuit_builder), + ExtRamTable::transition_constraints(&circuit_builder), + ExtJumpStackTable::transition_constraints(&circuit_builder), + ExtHashTable::transition_constraints(&circuit_builder), + ExtCascadeTable::transition_constraints(&circuit_builder), + ExtLookupTable::transition_constraints(&circuit_builder), + ExtU32Table::transition_constraints(&circuit_builder), + GrandCrossTableArg::transition_constraints(&circuit_builder), + ] + .map(|vec| vec.len()); + let circuit_builder = ConstraintCircuitBuilder::new(); let all_term = [ - ExtProgramTable::num_terminal_quotients(), - ExtProcessorTable::num_terminal_quotients(), - ExtOpStackTable::num_terminal_quotients(), - ExtRamTable::num_terminal_quotients(), - ExtJumpStackTable::num_terminal_quotients(), - ExtHashTable::num_terminal_quotients(), - ExtCascadeTable::num_terminal_quotients(), - ExtLookupTable::num_terminal_quotients(), - ExtU32Table::num_terminal_quotients(), - GrandCrossTableArg::num_terminal_quotients(), - ]; + ExtProgramTable::terminal_constraints(&circuit_builder), + ExtProcessorTable::terminal_constraints(&circuit_builder), + ExtOpStackTable::terminal_constraints(&circuit_builder), + ExtRamTable::terminal_constraints(&circuit_builder), + ExtJumpStackTable::terminal_constraints(&circuit_builder), + ExtHashTable::terminal_constraints(&circuit_builder), + ExtCascadeTable::terminal_constraints(&circuit_builder), + ExtLookupTable::terminal_constraints(&circuit_builder), + ExtU32Table::terminal_constraints(&circuit_builder), + GrandCrossTableArg::terminal_constraints(&circuit_builder), + ] + .map(|vec| vec.len()); let num_total_init: usize = all_init.iter().sum(); let num_total_cons: usize = all_cons.iter().sum(); @@ -1368,327 +1364,36 @@ pub(crate) mod triton_stark_tests { let er = ext_row.view(); assert_eq!( - ExtProgramTable::num_initial_quotients(), - ExtProgramTable::evaluate_initial_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtProgramTable::num_initial_quotients(), - ExtProgramTable::initial_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtProcessorTable::num_initial_quotients(), - ExtProcessorTable::evaluate_initial_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtProcessorTable::num_initial_quotients(), - ExtProcessorTable::initial_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtOpStackTable::num_initial_quotients(), - ExtOpStackTable::evaluate_initial_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtOpStackTable::num_initial_quotients(), - ExtOpStackTable::initial_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtRamTable::num_initial_quotients(), - ExtRamTable::evaluate_initial_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtRamTable::num_initial_quotients(), - ExtRamTable::initial_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtJumpStackTable::num_initial_quotients(), - ExtJumpStackTable::evaluate_initial_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtJumpStackTable::num_initial_quotients(), - ExtJumpStackTable::initial_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtHashTable::num_initial_quotients(), - ExtHashTable::evaluate_initial_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtHashTable::num_initial_quotients(), - ExtHashTable::initial_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtCascadeTable::num_initial_quotients(), - ExtCascadeTable::evaluate_initial_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtCascadeTable::num_initial_quotients(), - ExtCascadeTable::initial_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtLookupTable::num_initial_quotients(), - ExtLookupTable::evaluate_initial_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtLookupTable::num_initial_quotients(), - ExtLookupTable::initial_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtU32Table::num_initial_quotients(), - ExtU32Table::evaluate_initial_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtU32Table::num_initial_quotients(), - ExtU32Table::initial_quotient_degree_bounds(id).len() - ); - assert_eq!( - GrandCrossTableArg::num_initial_quotients(), - GrandCrossTableArg::evaluate_initial_constraints(br, er, &challenges).len(), - ); - assert_eq!( - GrandCrossTableArg::num_initial_quotients(), - GrandCrossTableArg::initial_quotient_degree_bounds(id).len() - ); - - assert_eq!( - ExtProgramTable::num_consistency_quotients(), - ExtProgramTable::evaluate_consistency_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtProgramTable::num_consistency_quotients(), - ExtProgramTable::consistency_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtProcessorTable::num_consistency_quotients(), - ExtProcessorTable::evaluate_consistency_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtProcessorTable::num_consistency_quotients(), - ExtProcessorTable::consistency_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtOpStackTable::num_consistency_quotients(), - ExtOpStackTable::evaluate_consistency_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtOpStackTable::num_consistency_quotients(), - ExtOpStackTable::consistency_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtRamTable::num_consistency_quotients(), - ExtRamTable::evaluate_consistency_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtRamTable::num_consistency_quotients(), - ExtRamTable::consistency_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtJumpStackTable::num_consistency_quotients(), - ExtJumpStackTable::evaluate_consistency_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtJumpStackTable::num_consistency_quotients(), - ExtJumpStackTable::consistency_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtHashTable::num_consistency_quotients(), - ExtHashTable::evaluate_consistency_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtHashTable::num_consistency_quotients(), - ExtHashTable::consistency_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtCascadeTable::num_consistency_quotients(), - ExtCascadeTable::evaluate_consistency_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtCascadeTable::num_consistency_quotients(), - ExtCascadeTable::consistency_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtLookupTable::num_consistency_quotients(), - ExtLookupTable::evaluate_consistency_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtLookupTable::num_consistency_quotients(), - ExtLookupTable::consistency_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtU32Table::num_consistency_quotients(), - ExtU32Table::evaluate_consistency_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtU32Table::num_consistency_quotients(), - ExtU32Table::consistency_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - GrandCrossTableArg::num_consistency_quotients(), - GrandCrossTableArg::evaluate_consistency_constraints(br, er, &challenges).len(), - ); - assert_eq!( - GrandCrossTableArg::num_consistency_quotients(), - GrandCrossTableArg::consistency_quotient_degree_bounds(id, ph).len() - ); - - assert_eq!( - ExtProgramTable::num_transition_quotients(), - ExtProgramTable::evaluate_transition_constraints(br, er, br, er, &challenges).len(), - ); - assert_eq!( - ExtProgramTable::num_transition_quotients(), - ExtProgramTable::transition_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtProcessorTable::num_transition_quotients(), - ExtProcessorTable::evaluate_transition_constraints(br, er, br, er, &challenges).len(), - ); - assert_eq!( - ExtProcessorTable::num_transition_quotients(), - ExtProcessorTable::transition_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtOpStackTable::num_transition_quotients(), - ExtOpStackTable::evaluate_transition_constraints(br, er, br, er, &challenges).len(), - ); - assert_eq!( - ExtOpStackTable::num_transition_quotients(), - ExtOpStackTable::transition_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtRamTable::num_transition_quotients(), - ExtRamTable::evaluate_transition_constraints(br, er, br, er, &challenges).len(), - ); - assert_eq!( - ExtRamTable::num_transition_quotients(), - ExtRamTable::transition_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtJumpStackTable::num_transition_quotients(), - ExtJumpStackTable::evaluate_transition_constraints(br, er, br, er, &challenges).len(), - ); - assert_eq!( - ExtJumpStackTable::num_transition_quotients(), - ExtJumpStackTable::transition_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtHashTable::num_transition_quotients(), - ExtHashTable::evaluate_transition_constraints(br, er, br, er, &challenges).len(), - ); - assert_eq!( - ExtHashTable::num_transition_quotients(), - ExtHashTable::transition_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtCascadeTable::num_transition_quotients(), - ExtCascadeTable::evaluate_transition_constraints(br, er, br, er, &challenges).len(), - ); - assert_eq!( - ExtCascadeTable::num_transition_quotients(), - ExtCascadeTable::transition_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtLookupTable::num_transition_quotients(), - ExtLookupTable::evaluate_transition_constraints(br, er, br, er, &challenges).len(), - ); - assert_eq!( - ExtLookupTable::num_transition_quotients(), - ExtLookupTable::transition_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - ExtU32Table::num_transition_quotients(), - ExtU32Table::evaluate_transition_constraints(br, er, br, er, &challenges).len(), - ); - assert_eq!( - ExtU32Table::num_transition_quotients(), - ExtU32Table::transition_quotient_degree_bounds(id, ph).len() - ); - assert_eq!( - GrandCrossTableArg::num_transition_quotients(), - GrandCrossTableArg::evaluate_transition_constraints(br, er, br, er, &challenges).len(), - ); - assert_eq!( - GrandCrossTableArg::num_transition_quotients(), - GrandCrossTableArg::transition_quotient_degree_bounds(id, ph).len() - ); - - assert_eq!( - ExtProgramTable::num_terminal_quotients(), - ExtProgramTable::evaluate_terminal_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtProgramTable::num_terminal_quotients(), - ExtProgramTable::terminal_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtProcessorTable::num_terminal_quotients(), - ExtProcessorTable::evaluate_terminal_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtProcessorTable::num_terminal_quotients(), - ExtProcessorTable::terminal_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtOpStackTable::num_terminal_quotients(), - ExtOpStackTable::evaluate_terminal_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtOpStackTable::num_terminal_quotients(), - ExtOpStackTable::terminal_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtRamTable::num_terminal_quotients(), - ExtRamTable::evaluate_terminal_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtRamTable::num_terminal_quotients(), - ExtRamTable::terminal_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtJumpStackTable::num_terminal_quotients(), - ExtJumpStackTable::evaluate_terminal_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtJumpStackTable::num_terminal_quotients(), - ExtJumpStackTable::terminal_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtHashTable::num_terminal_quotients(), - ExtHashTable::evaluate_terminal_constraints(br, er, &challenges).len(), - ); - assert_eq!( - ExtHashTable::num_terminal_quotients(), - ExtHashTable::terminal_quotient_degree_bounds(id).len() - ); - assert_eq!( - ExtCascadeTable::num_terminal_quotients(), - ExtCascadeTable::evaluate_terminal_constraints(br, er, &challenges).len(), + MasterExtTable::num_initial_quotients(), + MasterExtTable::evaluate_initial_constraints(br, er, &challenges).len(), ); assert_eq!( - ExtCascadeTable::num_terminal_quotients(), - ExtCascadeTable::terminal_quotient_degree_bounds(id).len() + MasterExtTable::num_initial_quotients(), + MasterExtTable::initial_quotient_degree_bounds(id).len() ); assert_eq!( - ExtLookupTable::num_terminal_quotients(), - ExtLookupTable::evaluate_terminal_constraints(br, er, &challenges).len(), + MasterExtTable::num_consistency_quotients(), + MasterExtTable::evaluate_consistency_constraints(br, er, &challenges).len(), ); assert_eq!( - ExtLookupTable::num_terminal_quotients(), - ExtLookupTable::terminal_quotient_degree_bounds(id).len() + MasterExtTable::num_consistency_quotients(), + MasterExtTable::consistency_quotient_degree_bounds(id, ph).len() ); assert_eq!( - ExtU32Table::num_terminal_quotients(), - ExtU32Table::evaluate_terminal_constraints(br, er, &challenges).len(), + MasterExtTable::num_transition_quotients(), + MasterExtTable::evaluate_transition_constraints(br, er, br, er, &challenges).len(), ); assert_eq!( - ExtU32Table::num_terminal_quotients(), - ExtU32Table::terminal_quotient_degree_bounds(id).len() + MasterExtTable::num_transition_quotients(), + MasterExtTable::transition_quotient_degree_bounds(id, ph).len() ); assert_eq!( - GrandCrossTableArg::num_terminal_quotients(), - GrandCrossTableArg::evaluate_terminal_constraints(br, er, &challenges).len(), + MasterExtTable::num_terminal_quotients(), + MasterExtTable::evaluate_terminal_constraints(br, er, &challenges).len(), ); assert_eq!( - GrandCrossTableArg::num_terminal_quotients(), - GrandCrossTableArg::terminal_quotient_degree_bounds(id).len() + MasterExtTable::num_terminal_quotients(), + MasterExtTable::terminal_quotient_degree_bounds(id).len() ); } diff --git a/triton-vm/src/table/constraints.rs b/triton-vm/src/table/constraints.rs index a4638a01..74be2fb2 100644 --- a/triton-vm/src/table/constraints.rs +++ b/triton-vm/src/table/constraints.rs @@ -1,10 +1,14 @@ -pub mod cascade_table_constraints; -pub mod cross_table_argument_constraints; -pub mod hash_table_constraints; -pub mod jump_stack_table_constraints; -pub mod lookup_table_constraints; -pub mod op_stack_table_constraints; -pub mod processor_table_constraints; -pub mod program_table_constraints; -pub mod ram_table_constraints; -pub mod u32_table_constraints; +use twenty_first::shared_math::b_field_element::BFieldElement; +use twenty_first::shared_math::x_field_element::XFieldElement; + +use crate::table::extension_table::Evaluable; +use crate::table::extension_table::Quotientable; +use crate::table::master_table::MasterExtTable; + +// This file is a placeholder for auto-generated code +// Run `cargo run --bin constraint-evaluation-generator` +// to fill in this file with optimized constraints. +impl Evaluable for MasterExtTable {} +impl Evaluable for MasterExtTable {} + +impl Quotientable for MasterExtTable {} diff --git a/triton-vm/src/table/constraints/cascade_table_constraints.rs b/triton-vm/src/table/constraints/cascade_table_constraints.rs deleted file mode 100644 index c916acbf..00000000 --- a/triton-vm/src/table/constraints/cascade_table_constraints.rs +++ /dev/null @@ -1,14 +0,0 @@ -use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::x_field_element::XFieldElement; - -use crate::table::cascade_table::ExtCascadeTable; -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; - -// This file is a placeholder for auto-generated code -// Run `cargo run --bin constraint-evaluation-generator` -// to fill in this file with optimized constraints. -impl Evaluable for ExtCascadeTable {} -impl Evaluable for ExtCascadeTable {} - -impl Quotientable for ExtCascadeTable {} diff --git a/triton-vm/src/table/constraints/cross_table_argument_constraints.rs b/triton-vm/src/table/constraints/cross_table_argument_constraints.rs deleted file mode 100644 index dc58f432..00000000 --- a/triton-vm/src/table/constraints/cross_table_argument_constraints.rs +++ /dev/null @@ -1,14 +0,0 @@ -use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::x_field_element::XFieldElement; - -use crate::table::cross_table_argument::GrandCrossTableArg; -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; - -// This file is a placeholder for auto-generated code -// Run `cargo run --bin constraint-evaluation-generator` -// to fill in this file with optimized constraints. -impl Evaluable for GrandCrossTableArg {} -impl Evaluable for GrandCrossTableArg {} - -impl Quotientable for GrandCrossTableArg {} diff --git a/triton-vm/src/table/constraints/hash_table_constraints.rs b/triton-vm/src/table/constraints/hash_table_constraints.rs deleted file mode 100644 index 0768a402..00000000 --- a/triton-vm/src/table/constraints/hash_table_constraints.rs +++ /dev/null @@ -1,14 +0,0 @@ -use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::x_field_element::XFieldElement; - -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; -use crate::table::hash_table::ExtHashTable; - -// This file is a placeholder for auto-generated code -// Run `cargo run --bin constraint-evaluation-generator` -// to fill in this file with optimized constraints. -impl Evaluable for ExtHashTable {} -impl Evaluable for ExtHashTable {} - -impl Quotientable for ExtHashTable {} diff --git a/triton-vm/src/table/constraints/jump_stack_table_constraints.rs b/triton-vm/src/table/constraints/jump_stack_table_constraints.rs deleted file mode 100644 index 8cc1cc7c..00000000 --- a/triton-vm/src/table/constraints/jump_stack_table_constraints.rs +++ /dev/null @@ -1,14 +0,0 @@ -use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::x_field_element::XFieldElement; - -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; -use crate::table::jump_stack_table::ExtJumpStackTable; - -// This file is a placeholder for auto-generated code -// Run `cargo run --bin constraint-evaluation-generator` -// to fill in this file with optimized constraints. -impl Evaluable for ExtJumpStackTable {} -impl Evaluable for ExtJumpStackTable {} - -impl Quotientable for ExtJumpStackTable {} diff --git a/triton-vm/src/table/constraints/lookup_table_constraints.rs b/triton-vm/src/table/constraints/lookup_table_constraints.rs deleted file mode 100644 index a559c5dc..00000000 --- a/triton-vm/src/table/constraints/lookup_table_constraints.rs +++ /dev/null @@ -1,14 +0,0 @@ -use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::x_field_element::XFieldElement; - -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; -use crate::table::lookup_table::ExtLookupTable; - -// This file is a placeholder for auto-generated code -// Run `cargo run --bin constraint-evaluation-generator` -// to fill in this file with optimized constraints. -impl Evaluable for ExtLookupTable {} -impl Evaluable for ExtLookupTable {} - -impl Quotientable for ExtLookupTable {} diff --git a/triton-vm/src/table/constraints/op_stack_table_constraints.rs b/triton-vm/src/table/constraints/op_stack_table_constraints.rs deleted file mode 100644 index 85b81f0f..00000000 --- a/triton-vm/src/table/constraints/op_stack_table_constraints.rs +++ /dev/null @@ -1,14 +0,0 @@ -use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::x_field_element::XFieldElement; - -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; -use crate::table::op_stack_table::ExtOpStackTable; - -// This file is a placeholder for auto-generated code -// Run `cargo run --bin constraint-evaluation-generator` -// to fill in this file with optimized constraints. -impl Evaluable for ExtOpStackTable {} -impl Evaluable for ExtOpStackTable {} - -impl Quotientable for ExtOpStackTable {} diff --git a/triton-vm/src/table/constraints/processor_table_constraints.rs b/triton-vm/src/table/constraints/processor_table_constraints.rs deleted file mode 100644 index ce9ae2bd..00000000 --- a/triton-vm/src/table/constraints/processor_table_constraints.rs +++ /dev/null @@ -1,14 +0,0 @@ -use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::x_field_element::XFieldElement; - -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; -use crate::table::processor_table::ExtProcessorTable; - -// This file is a placeholder for auto-generated code -// Run `cargo run --bin constraint-evaluation-generator` -// to fill in this file with optimized constraints. -impl Evaluable for ExtProcessorTable {} -impl Evaluable for ExtProcessorTable {} - -impl Quotientable for ExtProcessorTable {} diff --git a/triton-vm/src/table/constraints/program_table_constraints.rs b/triton-vm/src/table/constraints/program_table_constraints.rs deleted file mode 100644 index 29b63848..00000000 --- a/triton-vm/src/table/constraints/program_table_constraints.rs +++ /dev/null @@ -1,14 +0,0 @@ -use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::x_field_element::XFieldElement; - -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; -use crate::table::program_table::ExtProgramTable; - -// This file is a placeholder for auto-generated code -// Run `cargo run --bin constraint-evaluation-generator` -// to fill in this file with optimized constraints. -impl Evaluable for ExtProgramTable {} -impl Evaluable for ExtProgramTable {} - -impl Quotientable for ExtProgramTable {} diff --git a/triton-vm/src/table/constraints/ram_table_constraints.rs b/triton-vm/src/table/constraints/ram_table_constraints.rs deleted file mode 100644 index 0b2f3beb..00000000 --- a/triton-vm/src/table/constraints/ram_table_constraints.rs +++ /dev/null @@ -1,14 +0,0 @@ -use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::x_field_element::XFieldElement; - -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; -use crate::table::ram_table::ExtRamTable; - -// This file is a placeholder for auto-generated code -// Run `cargo run --bin constraint-evaluation-generator` -// to fill in this file with optimized constraints. -impl Evaluable for ExtRamTable {} -impl Evaluable for ExtRamTable {} - -impl Quotientable for ExtRamTable {} diff --git a/triton-vm/src/table/constraints/u32_table_constraints.rs b/triton-vm/src/table/constraints/u32_table_constraints.rs deleted file mode 100644 index b33300ef..00000000 --- a/triton-vm/src/table/constraints/u32_table_constraints.rs +++ /dev/null @@ -1,14 +0,0 @@ -use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::x_field_element::XFieldElement; - -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; -use crate::table::u32_table::ExtU32Table; - -// This file is a placeholder for auto-generated code -// Run `cargo run --bin constraint-evaluation-generator` -// to fill in this file with optimized constraints. -impl Evaluable for ExtU32Table {} -impl Evaluable for ExtU32Table {} - -impl Quotientable for ExtU32Table {} diff --git a/triton-vm/src/table/hash_table.rs b/triton-vm/src/table/hash_table.rs index 2692e45b..30e5da0f 100644 --- a/triton-vm/src/table/hash_table.rs +++ b/triton-vm/src/table/hash_table.rs @@ -1679,97 +1679,6 @@ impl HashTable { } } -#[cfg(test)] -mod constraint_tests { - use num_traits::Zero; - - use crate::stark::triton_stark_tests::parse_simulate_pad_extend; - use crate::table::extension_table::Evaluable; - use crate::table::master_table::MasterTable; - - use super::*; - - #[test] - fn hash_table_satisfies_constraints_test() { - let source_code = "hash hash hash halt"; - let (_, _, _, master_base_table, master_ext_table, challenges) = - parse_simulate_pad_extend(source_code, vec![], vec![]); - assert_eq!( - master_base_table.master_base_matrix.nrows(), - master_ext_table.master_ext_matrix.nrows() - ); - let master_base_trace_table = master_base_table.trace_table(); - let master_ext_trace_table = master_ext_table.trace_table(); - assert_eq!( - master_base_trace_table.nrows(), - master_ext_trace_table.nrows() - ); - - let num_rows = master_base_trace_table.nrows(); - let first_base_row = master_base_trace_table.row(0).map(|e| e.lift()); - let first_base_row = first_base_row.view(); - let first_ext_row = master_ext_trace_table.row(0); - for (idx, v) in - ExtHashTable::evaluate_initial_constraints(first_base_row, first_ext_row, &challenges) - .iter() - .enumerate() - { - assert!(v.is_zero(), "Initial constraint {idx} failed."); - } - - for row_idx in 0..num_rows { - let base_row = master_base_trace_table.row(row_idx).map(|e| e.lift()); - let base_row = base_row.view(); - let ext_row = master_ext_trace_table.row(row_idx); - for (constraint_idx, v) in - ExtHashTable::evaluate_consistency_constraints(base_row, ext_row, &challenges) - .iter() - .enumerate() - { - assert!( - v.is_zero(), - "consistency constraint {constraint_idx} failed in row {row_idx}" - ); - } - } - - for row_idx in 0..num_rows - 1 { - let base_row = master_base_trace_table.row(row_idx).map(|e| e.lift()); - let base_row = base_row.view(); - let ext_row = master_ext_trace_table.row(row_idx); - let next_base_row = master_base_trace_table.row(row_idx + 1).map(|e| e.lift()); - let next_base_row = next_base_row.view(); - let next_ext_row = master_ext_trace_table.row(row_idx + 1); - for (constraint_idx, v) in ExtHashTable::evaluate_transition_constraints( - base_row, - ext_row, - next_base_row, - next_ext_row, - &challenges, - ) - .iter() - .enumerate() - { - assert!( - v.is_zero(), - "transition constraint {constraint_idx} failed in row {row_idx}", - ); - } - } - - let last_base_row = master_base_trace_table.row(num_rows - 1).map(|e| e.lift()); - let last_base_row = last_base_row.view(); - let last_ext_row = master_ext_trace_table.row(num_rows - 1); - for (idx, v) in - ExtHashTable::evaluate_terminal_constraints(last_base_row, last_ext_row, &challenges) - .iter() - .enumerate() - { - assert!(v.is_zero(), "Terminal constraint {idx} failed."); - } - } -} - #[cfg(test)] pub mod tests { use super::*; @@ -1847,8 +1756,8 @@ pub mod tests { .enumerate() { let evaluated_constraint = constraint.evaluate( - master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), - master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + master_base_trace_table.slice(s![-1..0, ..]), + master_ext_trace_table.slice(s![-1..0, ..]), challenges, ); assert_eq!( diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index 76432c36..2896edb3 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -41,29 +41,17 @@ use crate::arithmetic_domain::ArithmeticDomain; use crate::stark::MTMaker; use crate::stark::StarkHasher; use crate::table::cascade_table::CascadeTable; -use crate::table::cascade_table::ExtCascadeTable; use crate::table::challenges::Challenges; -use crate::table::cross_table_argument::GrandCrossTableArg; use crate::table::degree_lowering_table::DegreeLoweringTable; -use crate::table::degree_lowering_table::ExtDegreeLoweringTable; use crate::table::extension_table::DegreeWithOrigin; -use crate::table::extension_table::Evaluable; use crate::table::extension_table::Quotientable; -use crate::table::hash_table::ExtHashTable; use crate::table::hash_table::HashTable; -use crate::table::jump_stack_table::ExtJumpStackTable; use crate::table::jump_stack_table::JumpStackTable; -use crate::table::lookup_table::ExtLookupTable; use crate::table::lookup_table::LookupTable; -use crate::table::op_stack_table::ExtOpStackTable; use crate::table::op_stack_table::OpStackTable; -use crate::table::processor_table::ExtProcessorTable; use crate::table::processor_table::ProcessorTable; -use crate::table::program_table::ExtProgramTable; use crate::table::program_table::ProgramTable; -use crate::table::ram_table::ExtRamTable; use crate::table::ram_table::RamTable; -use crate::table::u32_table::ExtU32Table; use crate::table::u32_table::U32Table; use crate::table::*; use crate::vm::AlgebraicExecutionTrace; @@ -734,20 +722,7 @@ pub fn all_degrees_with_origin( interpolant_degree: Degree, padded_height: usize, ) -> Vec { - let id = interpolant_degree; - let ph = padded_height; - [ - ExtProgramTable::all_degrees_with_origin("program table", id, ph), - ExtProcessorTable::all_degrees_with_origin("processor table", id, ph), - ExtOpStackTable::all_degrees_with_origin("op stack table", id, ph), - ExtRamTable::all_degrees_with_origin("ram table", id, ph), - ExtJumpStackTable::all_degrees_with_origin("jump stack table", id, ph), - ExtHashTable::all_degrees_with_origin("hash table", id, ph), - ExtCascadeTable::all_degrees_with_origin("cascade table", id, ph), - ExtLookupTable::all_degrees_with_origin("lookup table", id, ph), - ExtU32Table::all_degrees_with_origin("u32 table", id, ph), - ] - .concat() + MasterExtTable::all_degrees_with_origin("master table", interpolant_degree, padded_height) } pub fn max_degree_with_origin( @@ -760,153 +735,6 @@ pub fn max_degree_with_origin( .unwrap_or_default() } -pub fn num_all_table_quotients() -> usize { - num_all_initial_quotients() - + num_all_consistency_quotients() - + num_all_transition_quotients() - + num_all_terminal_quotients() -} - -pub fn num_all_initial_quotients() -> usize { - ExtProgramTable::num_initial_quotients() - + ExtProcessorTable::num_initial_quotients() - + ExtOpStackTable::num_initial_quotients() - + ExtRamTable::num_initial_quotients() - + ExtJumpStackTable::num_initial_quotients() - + ExtHashTable::num_initial_quotients() - + ExtCascadeTable::num_initial_quotients() - + ExtLookupTable::num_initial_quotients() - + ExtU32Table::num_initial_quotients() - + ExtDegreeLoweringTable::num_initial_quotients() -} - -pub fn num_all_consistency_quotients() -> usize { - ExtProgramTable::num_consistency_quotients() - + ExtProcessorTable::num_consistency_quotients() - + ExtOpStackTable::num_consistency_quotients() - + ExtRamTable::num_consistency_quotients() - + ExtJumpStackTable::num_consistency_quotients() - + ExtHashTable::num_consistency_quotients() - + ExtCascadeTable::num_consistency_quotients() - + ExtLookupTable::num_consistency_quotients() - + ExtU32Table::num_consistency_quotients() - + ExtDegreeLoweringTable::num_consistency_quotients() -} - -pub fn num_all_transition_quotients() -> usize { - ExtProgramTable::num_transition_quotients() - + ExtProcessorTable::num_transition_quotients() - + ExtOpStackTable::num_transition_quotients() - + ExtRamTable::num_transition_quotients() - + ExtJumpStackTable::num_transition_quotients() - + ExtHashTable::num_transition_quotients() - + ExtCascadeTable::num_transition_quotients() - + ExtLookupTable::num_transition_quotients() - + ExtU32Table::num_transition_quotients() - + ExtDegreeLoweringTable::num_transition_quotients() -} - -pub fn num_all_terminal_quotients() -> usize { - ExtProgramTable::num_terminal_quotients() - + ExtProcessorTable::num_terminal_quotients() - + ExtOpStackTable::num_terminal_quotients() - + ExtRamTable::num_terminal_quotients() - + ExtJumpStackTable::num_terminal_quotients() - + ExtHashTable::num_terminal_quotients() - + ExtCascadeTable::num_terminal_quotients() - + ExtLookupTable::num_terminal_quotients() - + ExtU32Table::num_terminal_quotients() - + GrandCrossTableArg::num_terminal_quotients() - + ExtDegreeLoweringTable::num_terminal_quotients() -} - -pub fn all_initial_quotient_degree_bounds(interpolant_degree: Degree) -> Vec { - [ - ExtProgramTable::initial_quotient_degree_bounds(interpolant_degree), - ExtProcessorTable::initial_quotient_degree_bounds(interpolant_degree), - ExtOpStackTable::initial_quotient_degree_bounds(interpolant_degree), - ExtRamTable::initial_quotient_degree_bounds(interpolant_degree), - ExtJumpStackTable::initial_quotient_degree_bounds(interpolant_degree), - ExtHashTable::initial_quotient_degree_bounds(interpolant_degree), - ExtCascadeTable::initial_quotient_degree_bounds(interpolant_degree), - ExtLookupTable::initial_quotient_degree_bounds(interpolant_degree), - ExtU32Table::initial_quotient_degree_bounds(interpolant_degree), - ExtDegreeLoweringTable::initial_quotient_degree_bounds(interpolant_degree), - ] - .concat() -} - -pub fn all_consistency_quotient_degree_bounds( - interpolant_degree: Degree, - padded_height: usize, -) -> Vec { - [ - ExtProgramTable::consistency_quotient_degree_bounds(interpolant_degree, padded_height), - ExtProcessorTable::consistency_quotient_degree_bounds(interpolant_degree, padded_height), - ExtOpStackTable::consistency_quotient_degree_bounds(interpolant_degree, padded_height), - ExtRamTable::consistency_quotient_degree_bounds(interpolant_degree, padded_height), - ExtJumpStackTable::consistency_quotient_degree_bounds(interpolant_degree, padded_height), - ExtHashTable::consistency_quotient_degree_bounds(interpolant_degree, padded_height), - ExtCascadeTable::consistency_quotient_degree_bounds(interpolant_degree, padded_height), - ExtLookupTable::consistency_quotient_degree_bounds(interpolant_degree, padded_height), - ExtU32Table::consistency_quotient_degree_bounds(interpolant_degree, padded_height), - ExtDegreeLoweringTable::consistency_quotient_degree_bounds( - interpolant_degree, - padded_height, - ), - ] - .concat() -} - -pub fn all_transition_quotient_degree_bounds( - interpolant_degree: Degree, - padded_height: usize, -) -> Vec { - [ - ExtProgramTable::transition_quotient_degree_bounds(interpolant_degree, padded_height), - ExtProcessorTable::transition_quotient_degree_bounds(interpolant_degree, padded_height), - ExtOpStackTable::transition_quotient_degree_bounds(interpolant_degree, padded_height), - ExtRamTable::transition_quotient_degree_bounds(interpolant_degree, padded_height), - ExtJumpStackTable::transition_quotient_degree_bounds(interpolant_degree, padded_height), - ExtHashTable::transition_quotient_degree_bounds(interpolant_degree, padded_height), - ExtCascadeTable::transition_quotient_degree_bounds(interpolant_degree, padded_height), - ExtLookupTable::transition_quotient_degree_bounds(interpolant_degree, padded_height), - ExtU32Table::transition_quotient_degree_bounds(interpolant_degree, padded_height), - ExtDegreeLoweringTable::transition_quotient_degree_bounds( - interpolant_degree, - padded_height, - ), - ] - .concat() -} - -pub fn all_terminal_quotient_degree_bounds(interpolant_degree: Degree) -> Vec { - [ - ExtProgramTable::terminal_quotient_degree_bounds(interpolant_degree), - ExtProcessorTable::terminal_quotient_degree_bounds(interpolant_degree), - ExtOpStackTable::terminal_quotient_degree_bounds(interpolant_degree), - ExtRamTable::terminal_quotient_degree_bounds(interpolant_degree), - ExtJumpStackTable::terminal_quotient_degree_bounds(interpolant_degree), - ExtHashTable::terminal_quotient_degree_bounds(interpolant_degree), - ExtCascadeTable::terminal_quotient_degree_bounds(interpolant_degree), - ExtLookupTable::terminal_quotient_degree_bounds(interpolant_degree), - ExtU32Table::terminal_quotient_degree_bounds(interpolant_degree), - ExtDegreeLoweringTable::terminal_quotient_degree_bounds(interpolant_degree), - GrandCrossTableArg::terminal_quotient_degree_bounds(interpolant_degree), - ] - .concat() -} - -pub fn all_quotient_degree_bounds(interpolant_degree: Degree, padded_height: usize) -> Vec { - [ - all_initial_quotient_degree_bounds(interpolant_degree), - all_consistency_quotient_degree_bounds(interpolant_degree, padded_height), - all_transition_quotient_degree_bounds(interpolant_degree, padded_height), - all_terminal_quotient_degree_bounds(interpolant_degree), - ] - .concat() -} - pub fn initial_quotient_zerofier_inverse( quotient_domain: ArithmeticDomain, ) -> Array1 { @@ -968,537 +796,6 @@ pub fn terminal_quotient_zerofier_inverse( BFieldElement::batch_inversion(zerofier_codeword).into() } -pub fn fill_all_initial_quotients( - master_base_table: ArrayView2, - master_ext_table: ArrayView2, - quot_table: &mut ArrayViewMut2, - zerofier_inverse: ArrayView1, - challenges: &Challenges, -) { - // The order of the quotient tables is not actually important. However, it must be consistent - // between prover and verifier, and the shapes must check out. - let program_section_start = 0; - let program_section_end = program_section_start + ExtProgramTable::num_initial_quotients(); - let processor_section_start = program_section_end; - let processor_section_end = - processor_section_start + ExtProcessorTable::num_initial_quotients(); - let op_stack_section_start = processor_section_end; - let op_stack_section_end = op_stack_section_start + ExtOpStackTable::num_initial_quotients(); - let ram_section_start = op_stack_section_end; - let ram_section_end = ram_section_start + ExtRamTable::num_initial_quotients(); - let jump_stack_section_start = ram_section_end; - let jump_stack_section_end = - jump_stack_section_start + ExtJumpStackTable::num_initial_quotients(); - let hash_section_start = jump_stack_section_end; - let hash_section_end = hash_section_start + ExtHashTable::num_initial_quotients(); - let cascade_section_start = hash_section_end; - let cascade_section_end = cascade_section_start + ExtCascadeTable::num_initial_quotients(); - let lookup_section_start = cascade_section_end; - let lookup_section_end = lookup_section_start + ExtLookupTable::num_initial_quotients(); - let u32_section_start = lookup_section_end; - let u32_section_end = u32_section_start + ExtU32Table::num_initial_quotients(); - let degree_lowering_section_start = u32_section_end; - let degree_lowering_section_end = - degree_lowering_section_start + ExtDegreeLoweringTable::num_initial_quotients(); - - let mut program_quot_table = - quot_table.slice_mut(s![.., program_section_start..program_section_end]); - ExtProgramTable::fill_initial_quotients( - master_base_table, - master_ext_table, - &mut program_quot_table, - zerofier_inverse, - challenges, - ); - let mut processor_quot_table = - quot_table.slice_mut(s![.., processor_section_start..processor_section_end]); - ExtProcessorTable::fill_initial_quotients( - master_base_table, - master_ext_table, - &mut processor_quot_table, - zerofier_inverse, - challenges, - ); - let mut op_stack_quot_table = - quot_table.slice_mut(s![.., op_stack_section_start..op_stack_section_end]); - ExtOpStackTable::fill_initial_quotients( - master_base_table, - master_ext_table, - &mut op_stack_quot_table, - zerofier_inverse, - challenges, - ); - let mut ram_quot_table = quot_table.slice_mut(s![.., ram_section_start..ram_section_end]); - ExtRamTable::fill_initial_quotients( - master_base_table, - master_ext_table, - &mut ram_quot_table, - zerofier_inverse, - challenges, - ); - let mut jump_stack_quot_table = - quot_table.slice_mut(s![.., jump_stack_section_start..jump_stack_section_end]); - ExtJumpStackTable::fill_initial_quotients( - master_base_table, - master_ext_table, - &mut jump_stack_quot_table, - zerofier_inverse, - challenges, - ); - let mut hash_quot_table = quot_table.slice_mut(s![.., hash_section_start..hash_section_end]); - ExtHashTable::fill_initial_quotients( - master_base_table, - master_ext_table, - &mut hash_quot_table, - zerofier_inverse, - challenges, - ); - let mut cascade_quot_table = - quot_table.slice_mut(s![.., cascade_section_start..cascade_section_end]); - ExtCascadeTable::fill_initial_quotients( - master_base_table, - master_ext_table, - &mut cascade_quot_table, - zerofier_inverse, - challenges, - ); - let mut lookup_quot_table = - quot_table.slice_mut(s![.., lookup_section_start..lookup_section_end]); - ExtLookupTable::fill_initial_quotients( - master_base_table, - master_ext_table, - &mut lookup_quot_table, - zerofier_inverse, - challenges, - ); - let mut u32_quot_table = quot_table.slice_mut(s![.., u32_section_start..u32_section_end]); - ExtU32Table::fill_initial_quotients( - master_base_table, - master_ext_table, - &mut u32_quot_table, - zerofier_inverse, - challenges, - ); - let mut degree_lowering_quot_table = quot_table.slice_mut(s![ - .., - degree_lowering_section_start..degree_lowering_section_end - ]); - ExtDegreeLoweringTable::fill_initial_quotients( - master_base_table, - master_ext_table, - &mut degree_lowering_quot_table, - zerofier_inverse, - challenges, - ); -} - -pub fn fill_all_consistency_quotients( - master_base_table: ArrayView2, - master_ext_table: ArrayView2, - quot_table: &mut ArrayViewMut2, - zerofier_inverse: ArrayView1, - challenges: &Challenges, -) { - // The order of the quotient tables is not actually important. However, it must be consistent - // between prover and verifier, and the shapes must check out. - let program_section_start = 0; - let program_section_end = program_section_start + ExtProgramTable::num_consistency_quotients(); - let processor_section_start = program_section_end; - let processor_section_end = - processor_section_start + ExtProcessorTable::num_consistency_quotients(); - let op_stack_section_start = processor_section_end; - let op_stack_section_end = - op_stack_section_start + ExtOpStackTable::num_consistency_quotients(); - let ram_section_start = op_stack_section_end; - let ram_section_end = ram_section_start + ExtRamTable::num_consistency_quotients(); - let jump_stack_section_start = ram_section_end; - let jump_stack_section_end = - jump_stack_section_start + ExtJumpStackTable::num_consistency_quotients(); - let hash_section_start = jump_stack_section_end; - let hash_section_end = hash_section_start + ExtHashTable::num_consistency_quotients(); - let cascade_section_start = hash_section_end; - let cascade_section_end = cascade_section_start + ExtCascadeTable::num_consistency_quotients(); - let lookup_section_start = cascade_section_end; - let lookup_section_end = lookup_section_start + ExtLookupTable::num_consistency_quotients(); - let u32_section_start = lookup_section_end; - let u32_section_end = u32_section_start + ExtU32Table::num_consistency_quotients(); - let degree_lowering_section_start = u32_section_end; - let degree_lowering_section_end = - degree_lowering_section_start + ExtDegreeLoweringTable::num_consistency_quotients(); - - let mut program_quot_table = - quot_table.slice_mut(s![.., program_section_start..program_section_end]); - ExtProgramTable::fill_consistency_quotients( - master_base_table, - master_ext_table, - &mut program_quot_table, - zerofier_inverse, - challenges, - ); - let mut processor_quot_table = - quot_table.slice_mut(s![.., processor_section_start..processor_section_end]); - ExtProcessorTable::fill_consistency_quotients( - master_base_table, - master_ext_table, - &mut processor_quot_table, - zerofier_inverse, - challenges, - ); - let mut op_stack_quot_table = - quot_table.slice_mut(s![.., op_stack_section_start..op_stack_section_end]); - ExtOpStackTable::fill_consistency_quotients( - master_base_table, - master_ext_table, - &mut op_stack_quot_table, - zerofier_inverse, - challenges, - ); - let mut ram_quot_table = quot_table.slice_mut(s![.., ram_section_start..ram_section_end]); - ExtRamTable::fill_consistency_quotients( - master_base_table, - master_ext_table, - &mut ram_quot_table, - zerofier_inverse, - challenges, - ); - let mut jump_stack_quot_table = - quot_table.slice_mut(s![.., jump_stack_section_start..jump_stack_section_end]); - ExtJumpStackTable::fill_consistency_quotients( - master_base_table, - master_ext_table, - &mut jump_stack_quot_table, - zerofier_inverse, - challenges, - ); - let mut hash_quot_table = quot_table.slice_mut(s![.., hash_section_start..hash_section_end]); - ExtHashTable::fill_consistency_quotients( - master_base_table, - master_ext_table, - &mut hash_quot_table, - zerofier_inverse, - challenges, - ); - let mut cascade_quot_table = - quot_table.slice_mut(s![.., cascade_section_start..cascade_section_end]); - ExtCascadeTable::fill_consistency_quotients( - master_base_table, - master_ext_table, - &mut cascade_quot_table, - zerofier_inverse, - challenges, - ); - let mut lookup_quot_table = - quot_table.slice_mut(s![.., lookup_section_start..lookup_section_end]); - ExtLookupTable::fill_consistency_quotients( - master_base_table, - master_ext_table, - &mut lookup_quot_table, - zerofier_inverse, - challenges, - ); - let mut u32_quot_table = quot_table.slice_mut(s![.., u32_section_start..u32_section_end]); - ExtU32Table::fill_consistency_quotients( - master_base_table, - master_ext_table, - &mut u32_quot_table, - zerofier_inverse, - challenges, - ); - let mut degree_lowering_quot_table = quot_table.slice_mut(s![ - .., - degree_lowering_section_start..degree_lowering_section_end - ]); - ExtDegreeLoweringTable::fill_consistency_quotients( - master_base_table, - master_ext_table, - &mut degree_lowering_quot_table, - zerofier_inverse, - challenges, - ); -} - -pub fn fill_all_transition_quotients( - master_base_table: ArrayView2, - master_ext_table: ArrayView2, - quot_table: &mut ArrayViewMut2, - zerofier_inverse: ArrayView1, - challenges: &Challenges, - trace_domain: ArithmeticDomain, - quotient_domain: ArithmeticDomain, -) { - // The order of the quotient tables is not actually important. However, it must be consistent - // between prover and verifier, and the shapes must check out. - let program_section_start = 0; - let program_section_end = program_section_start + ExtProgramTable::num_transition_quotients(); - let processor_section_start = program_section_end; - let processor_section_end = - processor_section_start + ExtProcessorTable::num_transition_quotients(); - let op_stack_section_start = processor_section_end; - let op_stack_section_end = op_stack_section_start + ExtOpStackTable::num_transition_quotients(); - let ram_section_start = op_stack_section_end; - let ram_section_end = ram_section_start + ExtRamTable::num_transition_quotients(); - let jump_stack_section_start = ram_section_end; - let jump_stack_section_end = - jump_stack_section_start + ExtJumpStackTable::num_transition_quotients(); - let hash_section_start = jump_stack_section_end; - let hash_section_end = hash_section_start + ExtHashTable::num_transition_quotients(); - let cascade_section_start = hash_section_end; - let cascade_section_end = cascade_section_start + ExtCascadeTable::num_transition_quotients(); - let lookup_section_start = cascade_section_end; - let lookup_section_end = lookup_section_start + ExtLookupTable::num_transition_quotients(); - let u32_section_start = lookup_section_end; - let u32_section_end = u32_section_start + ExtU32Table::num_transition_quotients(); - let degree_lowering_section_start = u32_section_end; - let degree_lowering_section_end = - degree_lowering_section_start + ExtDegreeLoweringTable::num_transition_quotients(); - - let mut program_quot_table = - quot_table.slice_mut(s![.., program_section_start..program_section_end]); - ExtProgramTable::fill_transition_quotients( - master_base_table, - master_ext_table, - &mut program_quot_table, - zerofier_inverse, - challenges, - trace_domain, - quotient_domain, - ); - let mut processor_quot_table = - quot_table.slice_mut(s![.., processor_section_start..processor_section_end]); - ExtProcessorTable::fill_transition_quotients( - master_base_table, - master_ext_table, - &mut processor_quot_table, - zerofier_inverse, - challenges, - trace_domain, - quotient_domain, - ); - let mut op_stack_quot_table = - quot_table.slice_mut(s![.., op_stack_section_start..op_stack_section_end]); - ExtOpStackTable::fill_transition_quotients( - master_base_table, - master_ext_table, - &mut op_stack_quot_table, - zerofier_inverse, - challenges, - trace_domain, - quotient_domain, - ); - let mut ram_quot_table = quot_table.slice_mut(s![.., ram_section_start..ram_section_end]); - ExtRamTable::fill_transition_quotients( - master_base_table, - master_ext_table, - &mut ram_quot_table, - zerofier_inverse, - challenges, - trace_domain, - quotient_domain, - ); - let mut jump_stack_quot_table = - quot_table.slice_mut(s![.., jump_stack_section_start..jump_stack_section_end]); - ExtJumpStackTable::fill_transition_quotients( - master_base_table, - master_ext_table, - &mut jump_stack_quot_table, - zerofier_inverse, - challenges, - trace_domain, - quotient_domain, - ); - let mut hash_quot_table = quot_table.slice_mut(s![.., hash_section_start..hash_section_end]); - ExtHashTable::fill_transition_quotients( - master_base_table, - master_ext_table, - &mut hash_quot_table, - zerofier_inverse, - challenges, - trace_domain, - quotient_domain, - ); - let mut cascade_quot_table = - quot_table.slice_mut(s![.., cascade_section_start..cascade_section_end]); - ExtCascadeTable::fill_transition_quotients( - master_base_table, - master_ext_table, - &mut cascade_quot_table, - zerofier_inverse, - challenges, - trace_domain, - quotient_domain, - ); - let mut lookup_quot_table = - quot_table.slice_mut(s![.., lookup_section_start..lookup_section_end]); - ExtLookupTable::fill_transition_quotients( - master_base_table, - master_ext_table, - &mut lookup_quot_table, - zerofier_inverse, - challenges, - trace_domain, - quotient_domain, - ); - let mut u32_quot_table = quot_table.slice_mut(s![.., u32_section_start..u32_section_end]); - ExtU32Table::fill_transition_quotients( - master_base_table, - master_ext_table, - &mut u32_quot_table, - zerofier_inverse, - challenges, - trace_domain, - quotient_domain, - ); - let mut degree_lowering_quot_table = quot_table.slice_mut(s![ - .., - degree_lowering_section_start..degree_lowering_section_end - ]); - ExtDegreeLoweringTable::fill_transition_quotients( - master_base_table, - master_ext_table, - &mut degree_lowering_quot_table, - zerofier_inverse, - challenges, - trace_domain, - quotient_domain, - ); -} - -pub fn fill_all_terminal_quotients( - master_base_table: ArrayView2, - master_ext_table: ArrayView2, - quot_table: &mut ArrayViewMut2, - zerofier_inverse: ArrayView1, - challenges: &Challenges, -) { - // The order of the quotient tables is not actually important. However, it must be consistent - // between prover and verifier, and the shapes must check out. - let program_section_start = 0; - let program_section_end = program_section_start + ExtProgramTable::num_terminal_quotients(); - let processor_section_start = program_section_end; - let processor_section_end = - processor_section_start + ExtProcessorTable::num_terminal_quotients(); - let op_stack_section_start = processor_section_end; - let op_stack_section_end = op_stack_section_start + ExtOpStackTable::num_terminal_quotients(); - let ram_section_start = op_stack_section_end; - let ram_section_end = ram_section_start + ExtRamTable::num_terminal_quotients(); - let jump_stack_section_start = ram_section_end; - let jump_stack_section_end = - jump_stack_section_start + ExtJumpStackTable::num_terminal_quotients(); - let hash_section_start = jump_stack_section_end; - let hash_section_end = hash_section_start + ExtHashTable::num_terminal_quotients(); - let cascade_section_start = hash_section_end; - let cascade_section_end = cascade_section_start + ExtCascadeTable::num_terminal_quotients(); - let lookup_section_start = cascade_section_end; - let lookup_section_end = lookup_section_start + ExtLookupTable::num_terminal_quotients(); - let u32_section_start = lookup_section_end; - let u32_section_end = u32_section_start + ExtU32Table::num_terminal_quotients(); - let degree_lowering_section_start = u32_section_end; - let degree_lowering_section_end = - degree_lowering_section_start + ExtDegreeLoweringTable::num_terminal_quotients(); - let cross_table_section_start = degree_lowering_section_end; - let cross_table_section_end = - cross_table_section_start + GrandCrossTableArg::num_terminal_quotients(); - - let mut program_quot_table = - quot_table.slice_mut(s![.., program_section_start..program_section_end]); - ExtProgramTable::fill_terminal_quotients( - master_base_table, - master_ext_table, - &mut program_quot_table, - zerofier_inverse, - challenges, - ); - let mut processor_quot_table = - quot_table.slice_mut(s![.., processor_section_start..processor_section_end]); - ExtProcessorTable::fill_terminal_quotients( - master_base_table, - master_ext_table, - &mut processor_quot_table, - zerofier_inverse, - challenges, - ); - let mut op_stack_quot_table = - quot_table.slice_mut(s![.., op_stack_section_start..op_stack_section_end]); - ExtOpStackTable::fill_terminal_quotients( - master_base_table, - master_ext_table, - &mut op_stack_quot_table, - zerofier_inverse, - challenges, - ); - let mut ram_quot_table = quot_table.slice_mut(s![.., ram_section_start..ram_section_end]); - ExtRamTable::fill_terminal_quotients( - master_base_table, - master_ext_table, - &mut ram_quot_table, - zerofier_inverse, - challenges, - ); - let mut jump_stack_quot_table = - quot_table.slice_mut(s![.., jump_stack_section_start..jump_stack_section_end]); - ExtJumpStackTable::fill_terminal_quotients( - master_base_table, - master_ext_table, - &mut jump_stack_quot_table, - zerofier_inverse, - challenges, - ); - let mut hash_quot_table = quot_table.slice_mut(s![.., hash_section_start..hash_section_end]); - ExtHashTable::fill_terminal_quotients( - master_base_table, - master_ext_table, - &mut hash_quot_table, - zerofier_inverse, - challenges, - ); - let mut cascade_quot_table = - quot_table.slice_mut(s![.., cascade_section_start..cascade_section_end]); - ExtCascadeTable::fill_terminal_quotients( - master_base_table, - master_ext_table, - &mut cascade_quot_table, - zerofier_inverse, - challenges, - ); - let mut lookup_quot_table = - quot_table.slice_mut(s![.., lookup_section_start..lookup_section_end]); - ExtLookupTable::fill_terminal_quotients( - master_base_table, - master_ext_table, - &mut lookup_quot_table, - zerofier_inverse, - challenges, - ); - let mut u32_quot_table = quot_table.slice_mut(s![.., u32_section_start..u32_section_end]); - ExtU32Table::fill_terminal_quotients( - master_base_table, - master_ext_table, - &mut u32_quot_table, - zerofier_inverse, - challenges, - ); - let mut degree_lowering_quot_table = quot_table.slice_mut(s![ - .., - degree_lowering_section_start..degree_lowering_section_end - ]); - ExtDegreeLoweringTable::fill_terminal_quotients( - master_base_table, - master_ext_table, - &mut degree_lowering_quot_table, - zerofier_inverse, - challenges, - ); - let mut cross_table_argument_quot_table = - quot_table.slice_mut(s![.., cross_table_section_start..cross_table_section_end]); - GrandCrossTableArg::fill_terminal_quotients( - master_base_table, - master_ext_table, - &mut cross_table_argument_quot_table, - zerofier_inverse, - challenges, - ); -} - /// Computes an array containing all quotients – the Master Quotient Table. Each column corresponds /// to a different quotient. The quotients are ordered by category – initial, consistency, /// transition, and then terminal. Within each category, the quotients follow the canonical order @@ -1526,65 +823,52 @@ pub fn all_quotients( ); prof_start!(maybe_profiler, "malloc"); - let num_columns = num_all_table_quotients(); - let mut all_quotients = Array2::zeros([quotient_domain.length, num_columns]); + let mut quotient_table = Array2::zeros([quotient_domain.length, num_quotients()]); prof_stop!(maybe_profiler, "malloc"); - let initial_quotient_section_start = 0; - let initial_quotient_section_end = initial_quotient_section_start + num_all_initial_quotients(); - let consistency_quotient_section_start = initial_quotient_section_end; - let consistency_quotient_section_end = - consistency_quotient_section_start + num_all_consistency_quotients(); - let transition_quotient_section_start = consistency_quotient_section_end; - let transition_quotient_section_end = - transition_quotient_section_start + num_all_transition_quotients(); - let terminal_quotient_section_start = transition_quotient_section_end; - let terminal_quotient_section_end = - terminal_quotient_section_start + num_all_terminal_quotients(); + let init_section_start = 0; + let init_section_end = init_section_start + MasterExtTable::num_initial_quotients(); + let cons_section_start = init_section_end; + let cons_section_end = cons_section_start + MasterExtTable::num_consistency_quotients(); + let tran_section_start = cons_section_end; + let tran_section_end = tran_section_start + MasterExtTable::num_transition_quotients(); + let term_section_start = tran_section_end; + let term_section_end = term_section_start + MasterExtTable::num_terminal_quotients(); + + let (mut init_quot_table, mut cons_quot_table, mut tran_quot_table, mut term_quot_table) = + quotient_table.multi_slice_mut(( + s![.., init_section_start..init_section_end], + s![.., cons_section_start..cons_section_end], + s![.., tran_section_start..tran_section_end], + s![.., term_section_start..term_section_end], + )); prof_start!(maybe_profiler, "initial"); - let mut initial_quot_table = all_quotients.slice_mut(s![ - .., - initial_quotient_section_start..initial_quotient_section_end - ]); - let initial_quotient_zerofier_inverse = initial_quotient_zerofier_inverse(quotient_domain); - fill_all_initial_quotients( + MasterExtTable::fill_initial_quotients( quotient_domain_master_base_table, quotient_domain_master_ext_table, - &mut initial_quot_table, - initial_quotient_zerofier_inverse.view(), + &mut init_quot_table, + initial_quotient_zerofier_inverse(quotient_domain).view(), challenges, ); prof_stop!(maybe_profiler, "initial"); prof_start!(maybe_profiler, "consistency"); - let mut consistency_quotients = all_quotients.slice_mut(s![ - .., - consistency_quotient_section_start..consistency_quotient_section_end - ]); - let consistency_quotient_zerofier_inverse = - consistency_quotient_zerofier_inverse(trace_domain, quotient_domain); - fill_all_consistency_quotients( + MasterExtTable::fill_consistency_quotients( quotient_domain_master_base_table, quotient_domain_master_ext_table, - &mut consistency_quotients, - consistency_quotient_zerofier_inverse.view(), + &mut cons_quot_table, + consistency_quotient_zerofier_inverse(trace_domain, quotient_domain).view(), challenges, ); prof_stop!(maybe_profiler, "consistency"); prof_start!(maybe_profiler, "transition"); - let mut transition_quotients = all_quotients.slice_mut(s![ - .., - transition_quotient_section_start..transition_quotient_section_end - ]); - let transition_quotient_zerofier_inverse = - transition_quotient_zerofier_inverse(trace_domain, quotient_domain); - fill_all_transition_quotients( + MasterExtTable::fill_transition_quotients( quotient_domain_master_base_table, quotient_domain_master_ext_table, - &mut transition_quotients, - transition_quotient_zerofier_inverse.view(), + &mut tran_quot_table, + transition_quotient_zerofier_inverse(trace_domain, quotient_domain).view(), challenges, trace_domain, quotient_domain, @@ -1592,109 +876,23 @@ pub fn all_quotients( prof_stop!(maybe_profiler, "transition"); prof_start!(maybe_profiler, "terminal"); - let mut terminal_quot_table = all_quotients.slice_mut(s![ - .., - terminal_quotient_section_start..terminal_quotient_section_end - ]); - let initial_quotient_zerofier_inverse = - terminal_quotient_zerofier_inverse(trace_domain, quotient_domain); - fill_all_terminal_quotients( + MasterExtTable::fill_terminal_quotients( quotient_domain_master_base_table, quotient_domain_master_ext_table, - &mut terminal_quot_table, - initial_quotient_zerofier_inverse.view(), + &mut term_quot_table, + terminal_quotient_zerofier_inverse(trace_domain, quotient_domain).view(), challenges, ); prof_stop!(maybe_profiler, "terminal"); - all_quotients + quotient_table } -pub fn evaluate_all_initial_constraints( - base_row: ArrayView1, - ext_row: ArrayView1, - challenges: &Challenges, -) -> Vec { - [ - ExtProgramTable::evaluate_initial_constraints(base_row, ext_row, challenges), - ExtProcessorTable::evaluate_initial_constraints(base_row, ext_row, challenges), - ExtOpStackTable::evaluate_initial_constraints(base_row, ext_row, challenges), - ExtRamTable::evaluate_initial_constraints(base_row, ext_row, challenges), - ExtJumpStackTable::evaluate_initial_constraints(base_row, ext_row, challenges), - ExtHashTable::evaluate_initial_constraints(base_row, ext_row, challenges), - ExtCascadeTable::evaluate_initial_constraints(base_row, ext_row, challenges), - ExtLookupTable::evaluate_initial_constraints(base_row, ext_row, challenges), - ExtU32Table::evaluate_initial_constraints(base_row, ext_row, challenges), - ExtDegreeLoweringTable::evaluate_initial_constraints(base_row, ext_row, challenges), - ] - .concat() -} - -pub fn evaluate_all_consistency_constraints( - base_row: ArrayView1, - ext_row: ArrayView1, - challenges: &Challenges, -) -> Vec { - [ - ExtProgramTable::evaluate_consistency_constraints(base_row, ext_row, challenges), - ExtProcessorTable::evaluate_consistency_constraints(base_row, ext_row, challenges), - ExtOpStackTable::evaluate_consistency_constraints(base_row, ext_row, challenges), - ExtRamTable::evaluate_consistency_constraints(base_row, ext_row, challenges), - ExtJumpStackTable::evaluate_consistency_constraints(base_row, ext_row, challenges), - ExtHashTable::evaluate_consistency_constraints(base_row, ext_row, challenges), - ExtCascadeTable::evaluate_consistency_constraints(base_row, ext_row, challenges), - ExtLookupTable::evaluate_consistency_constraints(base_row, ext_row, challenges), - ExtU32Table::evaluate_consistency_constraints(base_row, ext_row, challenges), - ExtDegreeLoweringTable::evaluate_consistency_constraints(base_row, ext_row, challenges), - ] - .concat() -} - -pub fn evaluate_all_transition_constraints( - current_base_row: ArrayView1, - current_ext_row: ArrayView1, - next_base_row: ArrayView1, - next_ext_row: ArrayView1, - challenges: &Challenges, -) -> Vec { - let cbr = current_base_row; - let cer = current_ext_row; - let nbr = next_base_row; - let ner = next_ext_row; - [ - ExtProgramTable::evaluate_transition_constraints(cbr, cer, nbr, ner, challenges), - ExtProcessorTable::evaluate_transition_constraints(cbr, cer, nbr, ner, challenges), - ExtOpStackTable::evaluate_transition_constraints(cbr, cer, nbr, ner, challenges), - ExtRamTable::evaluate_transition_constraints(cbr, cer, nbr, ner, challenges), - ExtJumpStackTable::evaluate_transition_constraints(cbr, cer, nbr, ner, challenges), - ExtHashTable::evaluate_transition_constraints(cbr, cer, nbr, ner, challenges), - ExtCascadeTable::evaluate_transition_constraints(cbr, cer, nbr, ner, challenges), - ExtLookupTable::evaluate_transition_constraints(cbr, cer, nbr, ner, challenges), - ExtU32Table::evaluate_transition_constraints(cbr, cer, nbr, ner, challenges), - ExtDegreeLoweringTable::evaluate_transition_constraints(cbr, cer, nbr, ner, challenges), - ] - .concat() -} - -pub fn evaluate_all_terminal_constraints( - base_row: ArrayView1, - ext_row: ArrayView1, - challenges: &Challenges, -) -> Vec { - [ - ExtProgramTable::evaluate_terminal_constraints(base_row, ext_row, challenges), - ExtProcessorTable::evaluate_terminal_constraints(base_row, ext_row, challenges), - ExtOpStackTable::evaluate_terminal_constraints(base_row, ext_row, challenges), - ExtRamTable::evaluate_terminal_constraints(base_row, ext_row, challenges), - ExtJumpStackTable::evaluate_terminal_constraints(base_row, ext_row, challenges), - ExtHashTable::evaluate_terminal_constraints(base_row, ext_row, challenges), - ExtCascadeTable::evaluate_terminal_constraints(base_row, ext_row, challenges), - ExtLookupTable::evaluate_terminal_constraints(base_row, ext_row, challenges), - ExtU32Table::evaluate_terminal_constraints(base_row, ext_row, challenges), - ExtDegreeLoweringTable::evaluate_terminal_constraints(base_row, ext_row, challenges), - GrandCrossTableArg::evaluate_terminal_constraints(base_row, ext_row, challenges), - ] - .concat() +pub fn num_quotients() -> usize { + MasterExtTable::num_initial_quotients() + + MasterExtTable::num_consistency_quotients() + + MasterExtTable::num_transition_quotients() + + MasterExtTable::num_terminal_quotients() } pub fn randomized_padded_trace_len(padded_height: usize, num_trace_randomizers: usize) -> usize { From 2d7e09f6fb26c7f0e14380fa4107a0af7781bf32 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 10 May 2023 11:58:52 +0200 Subject: [PATCH 23/72] simplify index arithmetic in slicing for last row --- triton-vm/src/table/cascade_table.rs | 4 ++-- triton-vm/src/table/hash_table.rs | 4 ++-- triton-vm/src/table/jump_stack_table.rs | 4 ++-- triton-vm/src/table/lookup_table.rs | 4 ++-- triton-vm/src/table/op_stack_table.rs | 4 ++-- triton-vm/src/table/processor_table.rs | 4 ++-- triton-vm/src/table/program_table.rs | 4 ++-- triton-vm/src/table/ram_table.rs | 4 ++-- triton-vm/src/table/u32_table.rs | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/triton-vm/src/table/cascade_table.rs b/triton-vm/src/table/cascade_table.rs index 33061565..ae4a8a48 100644 --- a/triton-vm/src/table/cascade_table.rs +++ b/triton-vm/src/table/cascade_table.rs @@ -395,8 +395,8 @@ pub mod tests { .enumerate() { let evaluated_constraint = constraint.evaluate( - master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), - master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + master_base_trace_table.slice(s![-1.., ..]), + master_ext_trace_table.slice(s![-1.., ..]), challenges, ); assert_eq!( diff --git a/triton-vm/src/table/hash_table.rs b/triton-vm/src/table/hash_table.rs index 30e5da0f..3eacc848 100644 --- a/triton-vm/src/table/hash_table.rs +++ b/triton-vm/src/table/hash_table.rs @@ -1756,8 +1756,8 @@ pub mod tests { .enumerate() { let evaluated_constraint = constraint.evaluate( - master_base_trace_table.slice(s![-1..0, ..]), - master_ext_trace_table.slice(s![-1..0, ..]), + master_base_trace_table.slice(s![-1.., ..]), + master_ext_trace_table.slice(s![-1.., ..]), challenges, ); assert_eq!( diff --git a/triton-vm/src/table/jump_stack_table.rs b/triton-vm/src/table/jump_stack_table.rs index eeb22ecc..cbb08728 100644 --- a/triton-vm/src/table/jump_stack_table.rs +++ b/triton-vm/src/table/jump_stack_table.rs @@ -467,8 +467,8 @@ pub mod tests { .enumerate() { let evaluated_constraint = constraint.evaluate( - master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), - master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + master_base_trace_table.slice(s![-1.., ..]), + master_ext_trace_table.slice(s![-1.., ..]), challenges, ); assert_eq!( diff --git a/triton-vm/src/table/lookup_table.rs b/triton-vm/src/table/lookup_table.rs index 7d2d8c8c..7c16167f 100644 --- a/triton-vm/src/table/lookup_table.rs +++ b/triton-vm/src/table/lookup_table.rs @@ -352,8 +352,8 @@ pub mod tests { .enumerate() { let evaluated_constraint = constraint.evaluate( - master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), - master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + master_base_trace_table.slice(s![-1.., ..]), + master_ext_trace_table.slice(s![-1.., ..]), challenges, ); assert_eq!( diff --git a/triton-vm/src/table/op_stack_table.rs b/triton-vm/src/table/op_stack_table.rs index 0c9e6639..5c1d3f3c 100644 --- a/triton-vm/src/table/op_stack_table.rs +++ b/triton-vm/src/table/op_stack_table.rs @@ -420,8 +420,8 @@ pub mod tests { .enumerate() { let evaluated_constraint = constraint.evaluate( - master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), - master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + master_base_trace_table.slice(s![-1.., ..]), + master_ext_trace_table.slice(s![-1.., ..]), challenges, ); assert_eq!( diff --git a/triton-vm/src/table/processor_table.rs b/triton-vm/src/table/processor_table.rs index 54a514b1..b9648dcd 100644 --- a/triton-vm/src/table/processor_table.rs +++ b/triton-vm/src/table/processor_table.rs @@ -3548,8 +3548,8 @@ pub mod tests { .enumerate() { let evaluated_constraint = constraint.evaluate( - master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), - master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + master_base_trace_table.slice(s![-1.., ..]), + master_ext_trace_table.slice(s![-1.., ..]), challenges, ); assert_eq!( diff --git a/triton-vm/src/table/program_table.rs b/triton-vm/src/table/program_table.rs index 00e88f30..8b73f246 100644 --- a/triton-vm/src/table/program_table.rs +++ b/triton-vm/src/table/program_table.rs @@ -296,8 +296,8 @@ pub mod tests { .enumerate() { let evaluated_constraint = constraint.evaluate( - master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), - master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + master_base_trace_table.slice(s![-1.., ..]), + master_ext_trace_table.slice(s![-1.., ..]), challenges, ); assert_eq!( diff --git a/triton-vm/src/table/ram_table.rs b/triton-vm/src/table/ram_table.rs index ae902998..651e3f6b 100644 --- a/triton-vm/src/table/ram_table.rs +++ b/triton-vm/src/table/ram_table.rs @@ -619,8 +619,8 @@ pub mod tests { .enumerate() { let evaluated_constraint = constraint.evaluate( - master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), - master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + master_base_trace_table.slice(s![-1.., ..]), + master_ext_trace_table.slice(s![-1.., ..]), challenges, ); assert_eq!( diff --git a/triton-vm/src/table/u32_table.rs b/triton-vm/src/table/u32_table.rs index f9691633..4c2b8d3d 100644 --- a/triton-vm/src/table/u32_table.rs +++ b/triton-vm/src/table/u32_table.rs @@ -654,8 +654,8 @@ pub mod tests { .enumerate() { let evaluated_constraint = constraint.evaluate( - master_base_trace_table.slice(s![master_base_trace_table.nrows() - 1.., ..]), - master_ext_trace_table.slice(s![master_ext_trace_table.nrows() - 1.., ..]), + master_base_trace_table.slice(s![-1.., ..]), + master_ext_trace_table.slice(s![-1.., ..]), challenges, ); assert_eq!( From 8607d123bf02ffcf04dcbee6b881a3bbabff3bbe Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 10 May 2023 12:10:58 +0200 Subject: [PATCH 24/72] remove now-superfluous `impl Evaluable` for degree-lowering table --- triton-vm/src/table/degree_lowering_table.rs | 156 +------------------ 1 file changed, 4 insertions(+), 152 deletions(-) diff --git a/triton-vm/src/table/degree_lowering_table.rs b/triton-vm/src/table/degree_lowering_table.rs index e3e8956b..97d03b27 100644 --- a/triton-vm/src/table/degree_lowering_table.rs +++ b/triton-vm/src/table/degree_lowering_table.rs @@ -1,5 +1,4 @@ use ndarray::s; -use ndarray::ArrayView1; use ndarray::ArrayView2; use ndarray::ArrayViewMut2; use ndarray::Axis; @@ -9,12 +8,8 @@ use strum_macros::Display; use strum_macros::EnumCount as EnumCountMacro; use strum_macros::EnumIter; use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::mpolynomial::Degree; use twenty_first::shared_math::x_field_element::XFieldElement; -use crate::table::challenges::Challenges; -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; use crate::table::master_table::NUM_BASE_COLUMNS; use crate::table::master_table::NUM_EXT_COLUMNS; @@ -25,21 +20,20 @@ pub const FULL_WIDTH: usize = BASE_WIDTH + EXT_WIDTH; #[repr(usize)] #[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] pub enum DegreeLoweringBaseTableColumn { - Col1, + /// To be replaced by generated code. Needed to keep the type-checker happy. + STANDIN, } #[repr(usize)] #[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] pub enum DegreeLoweringExtTableColumn { - XCol1, + /// To be replaced by generated code. Needed to keep the type-checker happy. + STANDIN, } #[derive(Debug, Clone)] pub struct DegreeLoweringTable {} -#[derive(Debug, Clone)] -pub struct ExtDegreeLoweringTable {} - impl DegreeLoweringTable { pub fn fill_deterministic_base_columns(master_base_table: &mut ArrayViewMut2) { assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); @@ -86,145 +80,3 @@ impl DegreeLoweringTable { }); } } - -impl Evaluable for ExtDegreeLoweringTable { - fn evaluate_initial_constraints( - _base_row: ArrayView1, - _ext_row: ArrayView1, - _challenges: &Challenges, - ) -> Vec { - vec![] - } - - fn evaluate_consistency_constraints( - base_row: ArrayView1, - ext_row: ArrayView1, - _challenges: &Challenges, - ) -> Vec { - let deterministic_base_section_start = NUM_BASE_COLUMNS - BASE_WIDTH; - let deterministic_ext_section_start = NUM_EXT_COLUMNS - EXT_WIDTH; - - let b_dummy_0 = base_row[deterministic_base_section_start] - base_row[0] * base_row[1]; - let x_dummy_0 = ext_row[deterministic_ext_section_start] - base_row[0] * ext_row[0]; - - let base_constraints = [b_dummy_0]; - let ext_constraints = [x_dummy_0]; - - base_constraints - .map(|b| b.lift()) - .into_iter() - .chain(ext_constraints) - .collect() - } - - fn evaluate_transition_constraints( - _current_base_row: ArrayView1, - _current_ext_row: ArrayView1, - _next_base_row: ArrayView1, - _next_ext_row: ArrayView1, - _challenges: &Challenges, - ) -> Vec { - vec![] - } - - fn evaluate_terminal_constraints( - _base_row: ArrayView1, - _ext_row: ArrayView1, - _challenges: &Challenges, - ) -> Vec { - vec![] - } -} - -impl Evaluable for ExtDegreeLoweringTable { - fn evaluate_initial_constraints( - _base_row: ArrayView1, - _ext_row: ArrayView1, - _challenges: &Challenges, - ) -> Vec { - vec![] - } - - fn evaluate_consistency_constraints( - base_row: ArrayView1, - ext_row: ArrayView1, - _challenges: &Challenges, - ) -> Vec { - let deterministic_base_section_start = NUM_BASE_COLUMNS - BASE_WIDTH; - let deterministic_ext_section_start = NUM_EXT_COLUMNS - EXT_WIDTH; - - let b_dummy_0 = base_row[deterministic_base_section_start] - base_row[0] * base_row[1]; - let x_dummy_0 = ext_row[deterministic_ext_section_start] - base_row[0] * ext_row[0]; - - let base_constraints = [b_dummy_0]; - let ext_constraints = [x_dummy_0]; - - base_constraints - .into_iter() - .chain(ext_constraints) - .collect() - } - - fn evaluate_transition_constraints( - _current_base_row: ArrayView1, - _current_ext_row: ArrayView1, - _next_base_row: ArrayView1, - _next_ext_row: ArrayView1, - _challenges: &Challenges, - ) -> Vec { - vec![] - } - - fn evaluate_terminal_constraints( - _base_row: ArrayView1, - _ext_row: ArrayView1, - _challenges: &Challenges, - ) -> Vec { - vec![] - } -} - -impl Quotientable for ExtDegreeLoweringTable { - fn num_initial_quotients() -> usize { - 0 - } - - fn num_consistency_quotients() -> usize { - 2 - } - - fn num_transition_quotients() -> usize { - 0 - } - - fn num_terminal_quotients() -> usize { - 0 - } - - fn initial_quotient_degree_bounds(_interpolant_degree: Degree) -> Vec { - vec![] - } - - fn consistency_quotient_degree_bounds( - interpolant_degree: Degree, - padded_height: usize, - ) -> Vec { - let zerofier_degree = padded_height as Degree; - [ - interpolant_degree * 2 - zerofier_degree, - interpolant_degree * 2 - zerofier_degree, - ] - .to_vec() - } - - fn transition_quotient_degree_bounds( - _interpolant_degree: Degree, - _padded_height: usize, - ) -> Vec { - vec![] - } - - fn terminal_quotient_degree_bounds(_interpolant_degree: Degree) -> Vec { - vec![] - } -} From 75c5b99f939480aa107952cf0726e947b15bfbb5 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 10 May 2023 14:16:59 +0200 Subject: [PATCH 25/72] communicate ID uniqueness test of `ConstraintCircuit`s more clearly Also, add more leniency when refreshing the visit counters. --- constraint-evaluation-generator/src/main.rs | 4 +- triton-vm/src/table/constraint_circuit.rs | 68 ++++++++++++--------- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 28c4c1e5..37d2bb35 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -315,11 +315,9 @@ fn turn_circuits_into_string( } // Sanity check: all node IDs must be unique. + // This also ounts the number of times each node is referenced. ConstraintCircuit::assert_has_unique_ids(constraint_circuits); - // Count number of times each node is referenced. - ConstraintCircuit::traverse_multiple(constraint_circuits); - // Get all unique reference counts. let mut visited_counters = constraint_circuits .iter() diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 97ae6967..446ae6b8 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -324,46 +324,49 @@ impl Display for ConstraintCircuit { impl ConstraintCircuit { /// Increment `visited_counter` by one for each reachable node - fn traverse_single(&mut self) { + fn increment_visit_count_for_tree(&mut self) { self.visited_counter += 1; if let BinaryOperation(_, lhs, rhs) = self.expression.borrow_mut() { - lhs.as_ref().borrow_mut().traverse_single(); - rhs.as_ref().borrow_mut().traverse_single(); + lhs.as_ref().borrow_mut().increment_visit_count_for_tree(); + rhs.as_ref().borrow_mut().increment_visit_count_for_tree(); } } - /// Count how many times each reachable node is reached when traversing from - /// the starting points that are given as input. The result is stored in the - /// `visited_counter` field in each node. - pub fn traverse_multiple(ccs: &mut [ConstraintCircuit]) { + /// Count how often each node is referenced when traversing from the given starting points. + /// The result is stored in the `visited_counter` field of each node. + pub fn refresh_visit_counters(ccs: &mut [ConstraintCircuit]) { for cc in ccs.iter_mut() { - assert!( - cc.visited_counter.is_zero(), - "visited counter must be zero before starting count" - ); - cc.traverse_single(); + cc.reset_visit_count_for_tree(); + } + for cc in ccs.iter_mut() { + cc.increment_visit_count_for_tree(); } } /// Reset the visited counters for the entire subtree - fn reset_visited_counters(&mut self) { + fn reset_visit_count_for_tree(&mut self) { self.visited_counter = 0; if let BinaryOperation(_, lhs, rhs) = &self.expression { - lhs.as_ref().borrow_mut().reset_visited_counters(); - rhs.as_ref().borrow_mut().reset_visited_counters(); + lhs.as_ref().borrow_mut().reset_visit_count_for_tree(); + rhs.as_ref().borrow_mut().reset_visit_count_for_tree(); } } /// Verify that all IDs in the subtree are unique. Panics otherwise. fn inner_has_unique_ids(&mut self, ids: &mut HashMap>) { - let node_with_repeated_id = ids.insert(self.id, self.clone()); - assert!( - !self.visited_counter.is_zero() || node_with_repeated_id.is_none(), - "ID = {} was repeated. Self was: {self:?}, other node: {:?}", - self.id, - node_with_repeated_id.unwrap(), - ); + let self_id = self.id; + + // Try to detect duplicate IDs only once for this node. + let maybe_other_node = if self.visited_counter == 0 { + ids.insert(self_id, self.clone()) + } else { + None + }; + if let Some(other) = maybe_other_node { + panic!("ID {self_id} was repeated. Self: {self:?}. Other: {other:?}."); + } + self.visited_counter += 1; if let BinaryOperation(_, lhs, rhs) = &self.expression { lhs.as_ref().borrow_mut().inner_has_unique_ids(ids); @@ -372,15 +375,20 @@ impl ConstraintCircuit { } /// Verify that a multicircuit has unique IDs. Panics otherwise. + /// Also determines how often each node is referenced and stores the result in the + /// `visited_counter` field of each node. + /// This makes this function very similar to + /// [`refresh_visit_counters`](ConstraintCircuit::refresh_visit_counters), + /// but it also checks for duplicate IDs. pub fn assert_has_unique_ids(constraints: &mut [ConstraintCircuit]) { let mut ids: HashMap> = HashMap::new(); - + // The inner uniqueness checks relies on visit counters being 0 for unseen nodes. + // Hence, they are reset here. for circuit in constraints.iter_mut() { - circuit.inner_has_unique_ids(&mut ids); + circuit.reset_visit_count_for_tree(); } - for circuit in constraints.iter_mut() { - circuit.reset_visited_counters(); + circuit.inner_has_unique_ids(&mut ids); } } @@ -1094,7 +1102,7 @@ mod constraint_circuit_tests { } for constraint in constraints.iter_mut() { - ConstraintCircuit::reset_visited_counters(constraint); + ConstraintCircuit::reset_visit_count_for_tree(constraint); } counter @@ -1223,7 +1231,11 @@ mod constraint_circuit_tests { let digest_prior = hasher0.finish(); // Increase visited counter and verify digest is unchanged - circuit.circuit.as_ref().borrow_mut().traverse_single(); + circuit + .circuit + .as_ref() + .borrow_mut() + .increment_visit_count_for_tree(); let mut hasher1 = DefaultHasher::new(); circuit.hash(&mut hasher1); let digest_after = hasher1.finish(); From 679a86d56cfffe37270c2578cda0e90c63158b75 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 10 May 2023 14:27:56 +0200 Subject: [PATCH 26/72] remove some dead code & comments --- triton-vm/src/table/constraint_circuit.rs | 95 ++++------------------- 1 file changed, 17 insertions(+), 78 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 446ae6b8..9ed1f3b8 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -392,54 +392,6 @@ impl ConstraintCircuit { } } - /// Return max degree after evaluating the circuit with an input of specified degree - pub fn symbolic_degree_bound( - &self, - max_base_degrees: &[Degree], - max_ext_degrees: &[Degree], - ) -> Degree { - match &self.expression { - BinaryOperation(binop, lhs, rhs) => { - let lhs_degree = lhs - .borrow() - .symbolic_degree_bound(max_base_degrees, max_ext_degrees); - let rhs_degree = rhs - .borrow() - .symbolic_degree_bound(max_base_degrees, max_ext_degrees); - match binop { - BinOp::Add | BinOp::Sub => cmp::max(lhs_degree, rhs_degree), - BinOp::Mul => { - // If either operand is zero, product is zero so degree is -1 - if lhs_degree == -1 || rhs_degree == -1 { - -1 - } else { - lhs_degree + rhs_degree - } - } - } - } - Input(input) => match input.is_base_table_column() { - true => max_base_degrees[input.base_col_index()], - false => max_ext_degrees[input.ext_col_index()], - }, - XConstant(xfe) => { - if xfe.is_zero() { - -1 - } else { - 0 - } - } - BConstant(bfe) => { - if bfe.is_zero() { - -1 - } else { - 0 - } - } - Challenge(_) => 0, - } - } - /// Return degree of the multivariate polynomial represented by this circuit pub fn degree(&self) -> Degree { match &self.expression { @@ -448,30 +400,21 @@ impl ConstraintCircuit { let rhs_degree = rhs.borrow().degree(); match binop { BinOp::Add | BinOp::Sub => cmp::max(lhs_degree, rhs_degree), - BinOp::Mul => { - if lhs_degree == -1 || rhs_degree == -1 { - -1 - } else { - lhs_degree + rhs_degree - } - } + BinOp::Mul => match lhs_degree == -1 || rhs_degree == -1 { + true => -1, + false => lhs_degree + rhs_degree, + }, } } Input(_) => 1, - XConstant(xfe) => { - if xfe.is_zero() { - -1 - } else { - 0 - } - } - BConstant(bfe) => { - if bfe.is_zero() { - -1 - } else { - 0 - } - } + XConstant(xfe) => match xfe.is_zero() { + true => -1, + false => 0, + }, + BConstant(bfe) => match bfe.is_zero() { + true => -1, + false => 0, + }, Challenge(_) => 0, } } @@ -481,17 +424,14 @@ impl ConstraintCircuit { // Maybe this could be solved smarter with dynamic programming // but we probably don't need that as our circuits aren't too big. match &self.expression { - // The highest number will always be in a leaf so we only - // need to check those. BinaryOperation(_, lhs, rhs) => { let lhs_counters = lhs.as_ref().borrow().get_all_visited_counters(); let rhs_counters = rhs.as_ref().borrow().get_all_visited_counters(); - let own_counter = self.visited_counter; - let mut all = vec![vec![own_counter], lhs_counters, rhs_counters].concat(); - all.sort_unstable(); - all.dedup(); - all.reverse(); - all + let own_counter = vec![self.visited_counter]; + let mut all_counters = vec![own_counter, lhs_counters, rhs_counters].concat(); + all_counters.sort_unstable(); + all_counters.dedup(); + all_counters } _ => vec![self.visited_counter], } @@ -894,7 +834,6 @@ impl ConstraintCircuitMonad { }; } } - return None; } None } From 1e2b1d351125f2e736eb99b731d6a0eb16d16b8d Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 10 May 2023 15:03:08 +0200 Subject: [PATCH 27/72] update makefile to reflect recent directory structure changes --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4db957b0..c24d4fac 100644 --- a/Makefile +++ b/Makefile @@ -55,8 +55,8 @@ build-constraints: cargo run --bin constraint-evaluation-generator clean-constraints: - git restore --staged triton-vm/src/table/constraints/ - git restore triton-vm/src/table/constraints/ + git restore --staged triton-vm/src/table/constraints.rs + git restore triton-vm/src/table/constraints.rs fmt-only: cargo fmt $(FMT_ARGS) From 73cd1ecdea91d00b671dd71e3aa4026d1eefa234 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 10 May 2023 18:15:57 +0200 Subject: [PATCH 28/72] start writing degree-lowering function --- constraint-evaluation-generator/src/main.rs | 51 +++--- triton-vm/src/table/constraint_circuit.rs | 171 ++++++++++++++++++++ 2 files changed, 190 insertions(+), 32 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 37d2bb35..3a1ed5a4 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -23,7 +23,7 @@ use triton_vm::table::u32_table::ExtU32Table; fn main() { let circuit_builder = ConstraintCircuitBuilder::new(); - let initial_constraints = vec![ + let mut initial_constraints = vec![ ExtProgramTable::initial_constraints(&circuit_builder), ExtProcessorTable::initial_constraints(&circuit_builder), ExtOpStackTable::initial_constraints(&circuit_builder), @@ -38,7 +38,7 @@ fn main() { .concat(); let circuit_builder = ConstraintCircuitBuilder::new(); - let consistency_constraints = vec![ + let mut consistency_constraints = vec![ ExtProgramTable::consistency_constraints(&circuit_builder), ExtProcessorTable::consistency_constraints(&circuit_builder), ExtOpStackTable::consistency_constraints(&circuit_builder), @@ -53,7 +53,7 @@ fn main() { .concat(); let circuit_builder = ConstraintCircuitBuilder::new(); - let transition_constraints = vec![ + let mut transition_constraints = vec![ ExtProgramTable::transition_constraints(&circuit_builder), ExtProcessorTable::transition_constraints(&circuit_builder), ExtOpStackTable::transition_constraints(&circuit_builder), @@ -68,7 +68,7 @@ fn main() { .concat(); let circuit_builder = ConstraintCircuitBuilder::new(); - let terminal_constraints = vec![ + let mut terminal_constraints = vec![ ExtProgramTable::terminal_constraints(&circuit_builder), ExtProcessorTable::terminal_constraints(&circuit_builder), ExtOpStackTable::terminal_constraints(&circuit_builder), @@ -82,12 +82,17 @@ fn main() { ] .concat(); - let mut initial_constraints = fold_and_consume(initial_constraints); - let mut consistency_constraints = fold_and_consume(consistency_constraints); - let mut transition_constraints = fold_and_consume(transition_constraints); - let mut terminal_constraints = fold_and_consume(terminal_constraints); + ConstraintCircuitMonad::constant_folding(&mut initial_constraints); + ConstraintCircuitMonad::constant_folding(&mut consistency_constraints); + ConstraintCircuitMonad::constant_folding(&mut transition_constraints); + ConstraintCircuitMonad::constant_folding(&mut terminal_constraints); - let code = gen( + let mut initial_constraints = consume(initial_constraints); + let mut consistency_constraints = consume(consistency_constraints); + let mut transition_constraints = consume(transition_constraints); + let mut terminal_constraints = consume(terminal_constraints); + + let code = generate_constraint_code( &mut initial_constraints, &mut consistency_constraints, &mut transition_constraints, @@ -102,14 +107,14 @@ fn main() { } } -fn fold_and_consume( - mut constraints: Vec>, +/// Consumes every `ConstraintCircuitMonad`, returning their corresponding `ConstraintCircuit`s. +fn consume( + constraints: Vec>, ) -> Vec> { - ConstraintCircuitMonad::constant_folding(&mut constraints); constraints.into_iter().map(|c| c.consume()).collect() } -fn gen( +fn generate_constraint_code( initial_constraint_circuits: &mut [ConstraintCircuit], consistency_constraint_circuits: &mut [ConstraintCircuit], transition_constraint_circuits: &mut [ConstraintCircuit], @@ -339,7 +344,7 @@ fn turn_circuits_into_string( let (base_constraints, ext_constraints): (Vec<_>, Vec<_>) = constraint_circuits .iter() - .partition(|constraint| is_bfield_element(constraint)); + .partition(|constraint| constraint.evaluates_to_base_element()); // The order of the constraints' degrees must match the order of the constraints. // Hence, listing the degrees is only possible after the partition into base and extension @@ -488,24 +493,6 @@ fn print_xfe(xfe: &XFieldElement) -> String { format!("XFieldElement::new([{coeff_0}, {coeff_1}, {coeff_2}])") } -/// Recursively check whether a node is composed of only BFieldElements, i.e., only uses -/// 1. inputs from base rows, -/// 2. constants from the B-field, and -/// 3. binary operations on BFieldElements. -fn is_bfield_element(circuit: &ConstraintCircuit) -> bool { - match &circuit.expression { - CircuitExpression::BConstant(_) => true, - CircuitExpression::XConstant(_) => false, - CircuitExpression::Input(indicator) => indicator.is_base_table_column(), - CircuitExpression::Challenge(_) => false, - CircuitExpression::BinaryOperation(_, lhs, rhs) => { - let lhs = lhs.as_ref().borrow(); - let rhs = rhs.as_ref().borrow(); - is_bfield_element(&lhs) && is_bfield_element(&rhs) - } - } -} - /// Recursively construct the code for evaluating a single node. fn evaluate_single_node( requested_visited_count: usize, diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 9ed1f3b8..6cdd9b5f 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -6,6 +6,7 @@ //! constraint polynomials, with each root corresponding to a different constraint polynomial. //! Because the graph has multiple roots, it is called a “multitree.” +use itertools::Itertools; use std::borrow::BorrowMut; use std::cell::RefCell; use std::cmp; @@ -69,6 +70,9 @@ pub trait InputIndicator: Debug + Clone + Copy + Hash + PartialEq + Eq + Display fn base_col_index(&self) -> usize; fn ext_col_index(&self) -> usize; + fn base_table_input(index: usize) -> Self; + fn ext_table_input(index: usize) -> Self; + fn evaluate( &self, base_table: ArrayView2, @@ -118,6 +122,14 @@ impl InputIndicator for SingleRowIndicator { } } + fn base_table_input(index: usize) -> Self { + Self::BaseRow(index) + } + + fn ext_table_input(index: usize) -> Self { + Self::ExtRow(index) + } + fn evaluate( &self, base_table: ArrayView2, @@ -177,6 +189,17 @@ impl InputIndicator for DualRowIndicator { } } + fn base_table_input(index: usize) -> Self { + // It seems that the choice between `CurrentBaseRow` and `NextBaseRow` is arbitrary: + // any transition constraint polynomial is evaluated on both the current and the next row. + // Hence, both rows are in scope. + Self::CurrentBaseRow(index) + } + + fn ext_table_input(index: usize) -> Self { + Self::CurrentExtRow(index) + } + fn evaluate( &self, base_table: ArrayView2, @@ -474,6 +497,24 @@ impl ConstraintCircuit { } } + /// Recursively check whether this node is composed of only BFieldElements, i.e., only uses + /// 1. inputs from base rows, + /// 2. constants from the B-field, and + /// 3. binary operations on BFieldElements. + pub fn evaluates_to_base_element(&self) -> bool { + match &self.expression { + BConstant(_) => true, + XConstant(_) => false, + Input(indicator) => indicator.is_base_table_column(), + Challenge(_) => false, + BinaryOperation(_, lhs, rhs) => { + let lhs = lhs.as_ref().borrow(); + let rhs = rhs.as_ref().borrow(); + lhs.evaluates_to_base_element() && rhs.evaluates_to_base_element() + } + } + } + pub fn evaluate( &self, base_table: ArrayView2, @@ -896,6 +937,136 @@ impl ConstraintCircuitMonad { } } } + + /// Lowers the degree of a given multicircuit to the target degree. + /// This is achieved by introducing additional variables and constraints. + /// The appropriate substitutions are applied to the given multicircuit. + /// The target degree must be greater than 1. + /// + /// The new constraints are returned as two vector of ConstraintCircuitMonads: + /// the first corresponds to base columns and constraints, + /// the second to extension columns and constraints. + /// + /// Each returned constraint is guaranteed to correspond to some + /// `CircuitExpression::BinaryOperation(BinOp::Sub, lhs, rhs)` where + /// - `lhs` is the new variable, and + /// - `rhs` is the (sub)circuit replaced by `lhs`. + /// These can then be used to construct new columns, + /// as well as derivation rules for filling those new columns. + /// + /// The number of base and extension columns used by the multicircuit have to be provided. + /// The uniqueness of the new columns' indices depends on these provided values. + pub fn lower_to_degree( + multicircuit: &mut [ConstraintCircuitMonad], + target_degree: Degree, + num_base_cols: usize, + num_ext_cols: usize, + ) -> ( + Vec>, + Vec>, + ) { + if target_degree <= 1 { + panic!("Target degree must be greater than 1. Got {target_degree}."); + } + + let mut base_constraints = vec![]; + let mut ext_constraints = vec![]; + + if multicircuit.is_empty() { + return (base_constraints, ext_constraints); + } + + let builder = multicircuit[0].builder.clone(); + + while multicircuit + .iter() + .map(|circuit| circuit.circuit.as_ref().borrow().degree()) + .max() + .unwrap_or(0) + > target_degree + { + // Filter for all nodes with degree > target degree. + let all_nodes = builder.all_nodes.as_ref().borrow(); + let high_degree_nodes = all_nodes + .iter() + .filter(|node| node.circuit.as_ref().borrow().degree() > target_degree) + .collect_vec(); + + // Of those nodes, get all the children with degree <= target degree. + let mut barely_low_degree_nodes = vec![]; + for node in high_degree_nodes.iter() { + // Constants are always of degree <= 1 and hence not high degree nodes. + // Addition and subtraction don't affect the degree, and hence uninteresting. + if let BinaryOperation(BinOp::Mul, lhs, rhs) = + &node.circuit.as_ref().borrow().expression + { + if lhs.as_ref().borrow().degree() <= target_degree { + barely_low_degree_nodes.push(lhs.clone()); + } + if rhs.as_ref().borrow().degree() <= target_degree { + barely_low_degree_nodes.push(rhs.clone()); + } + } + } + let barely_low_degree_nodes = barely_low_degree_nodes; + + // If the resulting list is empty, there is no way forward. PANIC TIME! + if barely_low_degree_nodes.is_empty() { + panic!("Could not lower degree of circuit to target degree. This is a bug."); + } + + // Of the remaining nodes, pick the one occurring the most often. + let mut low_deg_node_occurrences = HashMap::new(); + for node in barely_low_degree_nodes.iter() { + *low_deg_node_occurrences + .entry(node.as_ref().borrow().id) + .or_insert(0) += 1; + } + let (&chosen_node_id, _) = low_deg_node_occurrences + .iter() + .max_by_key(|(_, &count)| count) + .unwrap(); + let chosen_node = barely_low_degree_nodes + .into_iter() + .find(|node| node.as_ref().borrow().id == chosen_node_id) + .unwrap(); + + // Create a new variable. + let new_var_is_base_col = chosen_node.as_ref().borrow().evaluates_to_base_element(); + let new_col_idx = match new_var_is_base_col { + true => num_base_cols + base_constraints.len(), + false => num_ext_cols + ext_constraints.len(), + }; + let new_input_indicator = match new_var_is_base_col { + true => II::base_table_input(new_col_idx), + false => II::ext_table_input(new_col_idx), + }; + let new_variable = builder.input(new_input_indicator); + let new_circuit = new_variable.circuit.clone(); + + // Substitute the chosen circuit with a new variable. + new_variable.replace_references(chosen_node_id, new_circuit); + + // Create a new constraint: new_var - chosen_node + let chosen_node_monad = ConstraintCircuitMonad { + circuit: chosen_node, + builder: builder.clone(), + }; + let new_constraint = new_variable - chosen_node_monad; + + // Put the new constraint into the appropriate return vector. + match new_var_is_base_col { + true => base_constraints.push(new_constraint), + false => ext_constraints.push(new_constraint), + } + + // todo: treat a change of the root explicitly. + // Iterate through the list of roots, check if one of them is the chosen node. + // If so, replace it with the new variable. + } + + (base_constraints, ext_constraints) + } } #[derive(Debug, Clone)] From eeabeacbf5e337386ca96e856374045df6b45473 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 11 May 2023 08:36:31 +0200 Subject: [PATCH 29/72] seed table constraint property tests for reproducibility --- triton-vm/src/table/constraint_circuit.rs | 141 +++++++++++----------- 1 file changed, 69 insertions(+), 72 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 6cdd9b5f..609805fc 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -1169,8 +1169,11 @@ mod constraint_circuit_tests { use itertools::Itertools; use ndarray::Array2; use rand::random; + use rand::rngs::StdRng; use rand::thread_rng; + use rand::Rng; use rand::RngCore; + use rand::SeedableRng; use twenty_first::shared_math::other::random_elements; use crate::table::cascade_table::ExtCascadeTable; @@ -1188,34 +1191,36 @@ mod constraint_circuit_tests { use super::*; - fn node_counter_inner( - constraint: &mut ConstraintCircuit, - counter: &mut usize, - ) { - if constraint.visited_counter == 0 { - *counter += 1; - constraint.visited_counter = 1; - - if let BinaryOperation(_, lhs, rhs) = &constraint.expression { - node_counter_inner(&mut lhs.as_ref().borrow_mut(), counter); - node_counter_inner(&mut rhs.as_ref().borrow_mut(), counter); + /// Returns the number of unvisited nodes in the subtree of the given node, which includes + /// the node itself. Increments the visit counter of each visited node. + fn count_nodes_inner(constraint: &mut ConstraintCircuit) -> usize { + let num_unvisited_self = match constraint.visited_counter { + 0 => 1, + _ => 0, + }; + constraint.visited_counter += 1; + let num_unvisited_children = match &constraint.expression { + BinaryOperation(_, lhs, rhs) => { + let num_left = count_nodes_inner(&mut lhs.as_ref().borrow_mut()); + let num_right = count_nodes_inner(&mut rhs.as_ref().borrow_mut()); + num_left + num_right } - } - } - - /// Count the total number of nodes in call constraints - fn node_counter(constraints: &mut [ConstraintCircuit]) -> usize { - let mut counter = 0; + _ => 0, + }; - for constraint in constraints.iter_mut() { - node_counter_inner(constraint, &mut counter); - } + num_unvisited_self + num_unvisited_children + } + /// Count the total number of unique nodes in the given multicircuit. + /// Also refreshes the visit counter for each node, similar to + /// [`refresh_visit_counters`](ConstraintCircuit::refresh_visit_counters). + fn count_nodes(constraints: &mut [ConstraintCircuit]) -> usize { + // The uniqueness of nodes is determined by their visit count. + // To ensure a correct node count, the visit count must be reset before counting nodes. for constraint in constraints.iter_mut() { ConstraintCircuit::reset_visit_count_for_tree(constraint); } - - counter + constraints.iter_mut().map(|c| count_nodes_inner(c)).sum() } fn random_circuit_builder() -> ( @@ -1661,53 +1666,39 @@ mod constraint_circuit_tests { /// and its sub-circuits is unique. /// It is used to identify redundant constraints or sub-circuits. /// The employed method is the Schwartz-Zippel lemma. - fn evaluate_and_assert_uniqueness( + fn evaluate_assert_unique( constraint: &ConstraintCircuit, challenges: &Challenges, - base_table: ArrayView2, - ext_table: ArrayView2, - evaluated_values: &mut HashMap)>, + base_rows: ArrayView2, + ext_rows: ArrayView2, + values: &mut HashMap)>, ) -> XFieldElement { let value = match &constraint.expression { - BConstant(bfe) => bfe.lift(), - XConstant(xfe) => xfe.to_owned(), - Input(s) => s.evaluate(base_table, ext_table), - Challenge(cid) => challenges.get_challenge(*cid), BinaryOperation(binop, lhs, rhs) => { - let lhs = evaluate_and_assert_uniqueness( - &lhs.as_ref().borrow(), - challenges, - base_table, - ext_table, - evaluated_values, - ); - let rhs = evaluate_and_assert_uniqueness( - &rhs.as_ref().borrow(), - challenges, - base_table, - ext_table, - evaluated_values, - ); + let lhs = lhs.as_ref().borrow(); + let rhs = rhs.as_ref().borrow(); + let lhs = evaluate_assert_unique(&lhs, challenges, base_rows, ext_rows, values); + let rhs = evaluate_assert_unique(&rhs, challenges, base_rows, ext_rows, values); match binop { BinOp::Add => lhs + rhs, BinOp::Sub => lhs - rhs, BinOp::Mul => lhs * rhs, } } + _ => constraint.evaluate(base_rows, ext_rows, challenges), }; let own_id = constraint.id.to_owned(); - let maybe_entry = evaluated_values.insert(value, (own_id, constraint.clone())); - if let Some((collided_circuit_id, collided_circuit)) = maybe_entry { - if collided_circuit_id != own_id { - panic!( - "Circuit ID {collided_circuit_id} and circuit ID {own_id} are not unique. \ - Collision on:\n\ - ID {collided_circuit_id} – {collided_circuit}\n\ - ID {own_id} – {constraint}\n\ - Value was {value}.", - ); - } + let maybe_entry = values.insert(value, (own_id, constraint.clone())); + if let Some((other_id, other_circuit)) = maybe_entry { + assert_eq!( + own_id, other_id, + "Circuit ID {other_id} and circuit ID {own_id} are not unique. \ + Collision on:\n\ + ID {other_id} – {other_circuit}\n\ + ID {own_id} – {constraint}\n\ + Both evaluate to {value}.", + ); } value @@ -1721,31 +1712,37 @@ mod constraint_circuit_tests { ) -> Vec>, table_name: &str, ) { - let challenges = Challenges::placeholder(&[], &[]); + let seed = random(); + let mut rng = StdRng::seed_from_u64(seed); + println!("seed: {seed}"); + + let challenges: [XFieldElement; Challenges::num_challenges_to_sample()] = rng.gen(); + let challenges = challenges.to_vec(); + let challenges = Challenges::new(challenges, &[], &[]); + let circuit_builder = ConstraintCircuitBuilder::new(); let mut constraints = constraint_builder(&circuit_builder); ConstraintCircuitMonad::constant_folding(&mut constraints); let mut constraints = constraints.into_iter().map(|c| c.consume()).collect_vec(); ConstraintCircuit::assert_has_unique_ids(&mut constraints); - - let base_table_shape = [2, master_table::NUM_BASE_COLUMNS]; - let ext_table_shape = [2, master_table::NUM_EXT_COLUMNS]; - let base_table = Array2::from_shape_simple_fn(base_table_shape, random::); - let ext_table = Array2::from_shape_simple_fn(ext_table_shape, random::); - - let mut evaluated_values = HashMap::new(); - for constraint in constraints.iter() { - evaluate_and_assert_uniqueness( - constraint, - &challenges, - base_table.view(), - ext_table.view(), - &mut evaluated_values, - ); + let num_total_nodes = count_nodes(&mut constraints); + let constraints = constraints; + + let num_rows = 2; + let base_shape = [num_rows, master_table::NUM_BASE_COLUMNS]; + let ext_shape = [num_rows, master_table::NUM_EXT_COLUMNS]; + let base_rows = Array2::from_shape_simple_fn(base_shape, || rng.gen::()); + let ext_rows = Array2::from_shape_simple_fn(ext_shape, || rng.gen::()); + let base_rows = base_rows.view(); + let ext_rows = ext_rows.view(); + + let mut values = HashMap::new(); + for c in constraints.iter() { + evaluate_assert_unique(c, &challenges, base_rows, ext_rows, &mut values); } - println!("nodes in {table_name}: {}", node_counter(&mut constraints)); let circuit_degree = constraints.iter().map(|c| c.degree()).max().unwrap_or(-1); + println!("nodes in {table_name}: {num_total_nodes}"); println!("Max degree constraint for {table_name} table: {circuit_degree}"); } From d169ef855ca6d624b7166e191fd234873b8c146e Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 11 May 2023 12:44:28 +0200 Subject: [PATCH 30/72] avoid dynamic borrow rule violation by scoping node search --- triton-vm/src/table/constraint_circuit.rs | 177 ++++++++++++++-------- 1 file changed, 110 insertions(+), 67 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 609805fc..8422d223 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -6,7 +6,6 @@ //! constraint polynomials, with each root corresponding to a different constraint polynomial. //! Because the graph has multiple roots, it is called a “multitree.” -use itertools::Itertools; use std::borrow::BorrowMut; use std::cell::RefCell; use std::cmp; @@ -957,14 +956,11 @@ impl ConstraintCircuitMonad { /// The number of base and extension columns used by the multicircuit have to be provided. /// The uniqueness of the new columns' indices depends on these provided values. pub fn lower_to_degree( - multicircuit: &mut [ConstraintCircuitMonad], + multicircuit: &mut [Self], target_degree: Degree, num_base_cols: usize, num_ext_cols: usize, - ) -> ( - Vec>, - Vec>, - ) { + ) -> (Vec, Vec) { if target_degree <= 1 { panic!("Target degree must be greater than 1. Got {target_degree}."); } @@ -978,74 +974,27 @@ impl ConstraintCircuitMonad { let builder = multicircuit[0].builder.clone(); - while multicircuit - .iter() - .map(|circuit| circuit.circuit.as_ref().borrow().degree()) - .max() - .unwrap_or(0) - > target_degree - { - // Filter for all nodes with degree > target degree. - let all_nodes = builder.all_nodes.as_ref().borrow(); - let high_degree_nodes = all_nodes - .iter() - .filter(|node| node.circuit.as_ref().borrow().degree() > target_degree) - .collect_vec(); - - // Of those nodes, get all the children with degree <= target degree. - let mut barely_low_degree_nodes = vec![]; - for node in high_degree_nodes.iter() { - // Constants are always of degree <= 1 and hence not high degree nodes. - // Addition and subtraction don't affect the degree, and hence uninteresting. - if let BinaryOperation(BinOp::Mul, lhs, rhs) = - &node.circuit.as_ref().borrow().expression - { - if lhs.as_ref().borrow().degree() <= target_degree { - barely_low_degree_nodes.push(lhs.clone()); - } - if rhs.as_ref().borrow().degree() <= target_degree { - barely_low_degree_nodes.push(rhs.clone()); - } - } - } - let barely_low_degree_nodes = barely_low_degree_nodes; - - // If the resulting list is empty, there is no way forward. PANIC TIME! - if barely_low_degree_nodes.is_empty() { - panic!("Could not lower degree of circuit to target degree. This is a bug."); - } - - // Of the remaining nodes, pick the one occurring the most often. - let mut low_deg_node_occurrences = HashMap::new(); - for node in barely_low_degree_nodes.iter() { - *low_deg_node_occurrences - .entry(node.as_ref().borrow().id) - .or_insert(0) += 1; - } - let (&chosen_node_id, _) = low_deg_node_occurrences - .iter() - .max_by_key(|(_, &count)| count) - .unwrap(); - let chosen_node = barely_low_degree_nodes - .into_iter() - .find(|node| node.as_ref().borrow().id == chosen_node_id) - .unwrap(); + while Self::multicircuit_degree(multicircuit) > target_degree { + let chosen_node = Self::pick_node_to_substitute(target_degree, &builder); + dbg!(chosen_node.as_ref().borrow().degree()); // Create a new variable. - let new_var_is_base_col = chosen_node.as_ref().borrow().evaluates_to_base_element(); - let new_col_idx = match new_var_is_base_col { + let chosen_node_id = chosen_node.as_ref().borrow().id; + dbg!(chosen_node_id); + let chosen_node_is_base_col = chosen_node.as_ref().borrow().evaluates_to_base_element(); + let new_col_idx = match chosen_node_is_base_col { true => num_base_cols + base_constraints.len(), false => num_ext_cols + ext_constraints.len(), }; - let new_input_indicator = match new_var_is_base_col { + let new_input_indicator = match chosen_node_is_base_col { true => II::base_table_input(new_col_idx), false => II::ext_table_input(new_col_idx), }; let new_variable = builder.input(new_input_indicator); let new_circuit = new_variable.circuit.clone(); - // Substitute the chosen circuit with a new variable. - new_variable.replace_references(chosen_node_id, new_circuit); + // Substitute the chosen circuit with the new variable. + new_variable.replace_references(chosen_node_id, new_circuit.clone()); // Create a new constraint: new_var - chosen_node let chosen_node_monad = ConstraintCircuitMonad { @@ -1055,18 +1004,82 @@ impl ConstraintCircuitMonad { let new_constraint = new_variable - chosen_node_monad; // Put the new constraint into the appropriate return vector. - match new_var_is_base_col { + match chosen_node_is_base_col { true => base_constraints.push(new_constraint), false => ext_constraints.push(new_constraint), } - // todo: treat a change of the root explicitly. - // Iterate through the list of roots, check if one of them is the chosen node. - // If so, replace it with the new variable. + // Treat roots of the multicircuit explicitly. + for circuit in multicircuit.iter_mut() { + if circuit.circuit.as_ref().borrow().id == chosen_node_id { + circuit.circuit = new_circuit.clone(); + } + } } (base_constraints, ext_constraints) } + + fn pick_node_to_substitute( + target_degree: Degree, + builder: &ConstraintCircuitBuilder, + ) -> Rc>> { + // Filter for all nodes with degree > target degree. + let all_nodes = builder.all_nodes.as_ref().borrow(); + let high_degree_nodes = all_nodes + .iter() + .filter(|node| node.circuit.as_ref().borrow().degree() > target_degree); + + // Of those nodes, get all the children with degree <= target degree. + let mut barely_low_degree_nodes = vec![]; + for node in high_degree_nodes { + // Constant and inputs s are always of degree <= 1 and hence not high degree nodes. + // Addition and subtraction don't affect the degree. Hence, they are uninteresting. + if let BinaryOperation(BinOp::Mul, lhs, rhs) = + &node.circuit.as_ref().borrow().expression + { + if lhs.as_ref().borrow().degree() <= target_degree { + barely_low_degree_nodes.push(lhs.clone()); + } + if rhs.as_ref().borrow().degree() <= target_degree { + barely_low_degree_nodes.push(rhs.clone()); + } + } + } + // Substituting a node of degree 1 is both pointless and can lead to infinite iteration. + barely_low_degree_nodes.retain(|node| node.as_ref().borrow().degree() > 1); + let barely_low_degree_nodes = barely_low_degree_nodes; + + // If the resulting list is empty, there is no way forward. Stop – panic time! + if barely_low_degree_nodes.is_empty() { + panic!("Could not lower degree of circuit to target degree. This is a bug."); + } + + // Of the remaining nodes, pick the one occurring the most often. + let mut low_deg_node_occurrences = HashMap::new(); + for node in barely_low_degree_nodes.iter() { + *low_deg_node_occurrences + .entry(node.as_ref().borrow().id) + .or_insert(0) += 1; + } + let (&chosen_node_id, _) = low_deg_node_occurrences + .iter() + .max_by_key(|(_, &count)| count) + .unwrap(); + barely_low_degree_nodes + .into_iter() + .find(|node| node.as_ref().borrow().id == chosen_node_id) + .unwrap() + } + + /// Returns the maximum degree of all circuits in the multicircuit. + fn multicircuit_degree(multicircuit: &[ConstraintCircuitMonad]) -> Degree { + multicircuit + .iter() + .map(|circuit| circuit.circuit.as_ref().borrow().degree()) + .max() + .unwrap_or(-1) + } } #[derive(Debug, Clone)] @@ -1179,6 +1192,7 @@ mod constraint_circuit_tests { use crate::table::cascade_table::ExtCascadeTable; use crate::table::challenges::ChallengeId::U32Indeterminate; use crate::table::challenges::Challenges; + use crate::table::constraint_circuit::SingleRowIndicator::BaseRow; use crate::table::hash_table::ExtHashTable; use crate::table::jump_stack_table::ExtJumpStackTable; use crate::table::lookup_table::ExtLookupTable; @@ -1853,4 +1867,33 @@ mod constraint_circuit_tests { table_constraints_prop(&tran, "lookup transition"); table_constraints_prop(&term, "lookup terminal"); } + + #[test] + fn simple_degree_lowering_test() { + let builder = ConstraintCircuitBuilder::new(); + let x = builder.input(BaseRow(0)); + let x_pow_3 = x.clone() * x.clone() * x.clone(); + let x_pow_5 = x_pow_3.clone() * x.clone() * x; + let mut multicircuit = [x_pow_5, x_pow_3]; + + let target_degree = 3; + let num_base_cols = 1; + let num_ext_cols = 0; + let (new_base_constraints, new_ext_constraints) = ConstraintCircuitMonad::lower_to_degree( + &mut multicircuit, + target_degree, + num_base_cols, + num_ext_cols, + ); + assert!(new_ext_constraints.is_empty()); + + println!("multicircuit:"); + for circuit in multicircuit.iter() { + println!(" {circuit}"); + } + println!("new base constraints:"); + for constraint in new_base_constraints.iter() { + println!(" {constraint}"); + } + } } From 1dce529d8127f496c4e6c4448d2d9ec067b3f082 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 11 May 2023 13:12:11 +0200 Subject: [PATCH 31/72] move node substitution to the `ConstraintCircuitBuilder` --- triton-vm/src/table/constraint_circuit.rs | 45 ++++++++++++----------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 8422d223..a8643247 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -737,25 +737,6 @@ impl ConstraintCircuitMonad { max_from_hash_map } - fn replace_references(&self, old_id: usize, new: Rc>>) { - for node in self.builder.all_nodes.as_ref().borrow().clone().into_iter() { - if node.circuit.as_ref().borrow().id == old_id { - continue; - } - - if let BinaryOperation(_, ref mut lhs, ref mut rhs) = - node.circuit.as_ref().borrow_mut().expression - { - if lhs.as_ref().borrow().id == old_id { - *lhs = new.clone(); - } - if rhs.as_ref().borrow().id == old_id { - *rhs = new.clone(); - } - } - } - } - fn find_equivalent_expression(&self) -> Option>>> { if let BinaryOperation(binop, lhs, rhs) = &self.circuit.as_ref().borrow().expression { // a + 0 = a ∧ a - 0 = a @@ -912,8 +893,8 @@ impl ConstraintCircuitMonad { if equivalent_circuit.is_some() { let equivalent_circuit = equivalent_circuit.as_ref().unwrap().clone(); - let id_of_node_to_be_deleted = self.circuit.borrow().id; - self.replace_references(id_of_node_to_be_deleted, equivalent_circuit); + let id_to_remove = self.circuit.borrow().id; + self.builder.substitute(id_to_remove, equivalent_circuit); self.builder.all_nodes.as_ref().borrow_mut().remove(self); } @@ -994,7 +975,7 @@ impl ConstraintCircuitMonad { let new_circuit = new_variable.circuit.clone(); // Substitute the chosen circuit with the new variable. - new_variable.replace_references(chosen_node_id, new_circuit.clone()); + builder.substitute(chosen_node_id, new_circuit.clone()); // Create a new constraint: new_var - chosen_node let chosen_node_monad = ConstraintCircuitMonad { @@ -1172,6 +1153,26 @@ impl ConstraintCircuitBuilder { new_node } + + /// Substitute all nodes with ID `old_id` with the given `new` node. + pub fn substitute(&self, old_id: usize, new: Rc>>) { + for node in self.all_nodes.as_ref().borrow().clone().into_iter() { + if node.circuit.as_ref().borrow().id == old_id { + continue; + } + + if let BinaryOperation(_, ref mut lhs, ref mut rhs) = + node.circuit.as_ref().borrow_mut().expression + { + if lhs.as_ref().borrow().id == old_id { + *lhs = new.clone(); + } + if rhs.as_ref().borrow().id == old_id { + *rhs = new.clone(); + } + } + } + } } #[cfg(test)] From f679efe484417b22f9700cf88355931ade1f883c Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 11 May 2023 17:00:18 +0200 Subject: [PATCH 32/72] use (presumably) better heuristic for picking node to substitute --- triton-vm/src/table/constraint_circuit.rs | 57 ++++++++++++++++------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index a8643247..e2d8b27d 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -95,7 +95,7 @@ impl Display for SingleRowIndicator { ExtRow(i) => format!("ext_row[{i}]"), }; - writeln!(f, "{input_indicator}") + write!(f, "{input_indicator}") } } @@ -162,7 +162,7 @@ impl Display for DualRowIndicator { NextExtRow(i) => format!("next_ext_row[{i}]"), }; - writeln!(f, "{input_indicator}") + write!(f, "{input_indicator}") } } @@ -1005,16 +1005,16 @@ impl ConstraintCircuitMonad { target_degree: Degree, builder: &ConstraintCircuitBuilder, ) -> Rc>> { - // Filter for all nodes with degree > target degree. + // Only nodes with degree > target_degree need changing. let all_nodes = builder.all_nodes.as_ref().borrow(); let high_degree_nodes = all_nodes .iter() .filter(|node| node.circuit.as_ref().borrow().degree() > target_degree); - // Of those nodes, get all the children with degree <= target degree. + // Of those nodes, get all the children with degree <= target_degree. let mut barely_low_degree_nodes = vec![]; for node in high_degree_nodes { - // Constant and inputs s are always of degree <= 1 and hence not high degree nodes. + // Constants, inputs, and challenges are of degree <= 1, which is not high. // Addition and subtraction don't affect the degree. Hence, they are uninteresting. if let BinaryOperation(BinOp::Mul, lhs, rhs) = &node.circuit.as_ref().borrow().expression @@ -1027,26 +1027,30 @@ impl ConstraintCircuitMonad { } } } + // Substituting a node of degree 1 is both pointless and can lead to infinite iteration. barely_low_degree_nodes.retain(|node| node.as_ref().borrow().degree() > 1); + let max_degree = barely_low_degree_nodes + .iter() + .map(|node| node.as_ref().borrow().degree()) + .max() + .unwrap_or(-1); + barely_low_degree_nodes.retain(|node| node.as_ref().borrow().degree() == max_degree); let barely_low_degree_nodes = barely_low_degree_nodes; // If the resulting list is empty, there is no way forward. Stop – panic time! - if barely_low_degree_nodes.is_empty() { - panic!("Could not lower degree of circuit to target degree. This is a bug."); - } + assert!( + !barely_low_degree_nodes.is_empty(), + "Could not lower degree of circuit to target degree. This is a bug." + ); // Of the remaining nodes, pick the one occurring the most often. - let mut low_deg_node_occurrences = HashMap::new(); + let mut occurrences = HashMap::new(); for node in barely_low_degree_nodes.iter() { - *low_deg_node_occurrences - .entry(node.as_ref().borrow().id) - .or_insert(0) += 1; + let node_id = node.as_ref().borrow().id; + *occurrences.entry(node_id).or_insert(0) += 1; } - let (&chosen_node_id, _) = low_deg_node_occurrences - .iter() - .max_by_key(|(_, &count)| count) - .unwrap(); + let (&chosen_node_id, _) = occurrences.iter().max_by_key(|(_, &count)| count).unwrap(); barely_low_degree_nodes .into_iter() .find(|node| node.as_ref().borrow().id == chosen_node_id) @@ -1877,16 +1881,33 @@ mod constraint_circuit_tests { let x_pow_5 = x_pow_3.clone() * x.clone() * x; let mut multicircuit = [x_pow_5, x_pow_3]; - let target_degree = 3; + let target_deg = 3; let num_base_cols = 1; let num_ext_cols = 0; let (new_base_constraints, new_ext_constraints) = ConstraintCircuitMonad::lower_to_degree( &mut multicircuit, - target_degree, + target_deg, num_base_cols, num_ext_cols, ); assert!(new_ext_constraints.is_empty()); + assert_eq!(1, new_base_constraints.len()); + + assert!(ConstraintCircuitMonad::multicircuit_degree(&multicircuit) <= target_deg); + assert!(ConstraintCircuitMonad::multicircuit_degree(&new_base_constraints) <= target_deg); + assert!(ConstraintCircuitMonad::multicircuit_degree(&new_ext_constraints) <= target_deg); + + for (i, constraint) in new_base_constraints.iter().enumerate() { + let expression = constraint.circuit.as_ref().borrow().expression.clone(); + let BinaryOperation(BinOp::Sub, lhs, _) = expression else { + panic!("New base constraint {i} must be a subtraction."); + }; + let lhs_degree = lhs.as_ref().borrow().degree(); + assert_eq!( + 1, lhs_degree, + "New base constraint {i} must be a simple substitution." + ); + } println!("multicircuit:"); for circuit in multicircuit.iter() { From 1ce97d47dfa89d6545d2162bfd087035351facdb Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Fri, 12 May 2023 08:24:58 +0200 Subject: [PATCH 33/72] promote node counting in a multicircuit to a public function --- triton-vm/src/table/constraint_circuit.rs | 67 ++++++++++++----------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index e2d8b27d..ba46864e 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -542,6 +542,41 @@ impl ConstraintCircuit { } } } + + /// Returns the number of unvisited nodes in the subtree of the given node, which includes + /// the node itself. Increments the visit counter of each visited node. + fn count_nodes_inner(constraint: &mut ConstraintCircuit) -> usize { + let num_unvisited_self = match constraint.visited_counter { + 0 => 1, + _ => 0, + }; + constraint.visited_counter += 1; + let num_unvisited_children = match &constraint.expression { + BinaryOperation(_, lhs, rhs) => { + let num_left = Self::count_nodes_inner(&mut lhs.as_ref().borrow_mut()); + let num_right = Self::count_nodes_inner(&mut rhs.as_ref().borrow_mut()); + num_left + num_right + } + _ => 0, + }; + + num_unvisited_self + num_unvisited_children + } + + /// Count the total number of unique nodes in the given multicircuit. + /// Also refreshes the visit counter for each node, similar to + /// [`refresh_visit_counters`](ConstraintCircuit::refresh_visit_counters). + pub fn count_nodes(constraints: &mut [ConstraintCircuit]) -> usize { + // The uniqueness of nodes is determined by their visit count. + // To ensure a correct node count, the visit count must be reset before counting nodes. + for constraint in constraints.iter_mut() { + ConstraintCircuit::reset_visit_count_for_tree(constraint); + } + constraints + .iter_mut() + .map(|c| Self::count_nodes_inner(c)) + .sum() + } } /// Constraint expressions, with context needed to ensure that two equal nodes are not added to @@ -1210,38 +1245,6 @@ mod constraint_circuit_tests { use super::*; - /// Returns the number of unvisited nodes in the subtree of the given node, which includes - /// the node itself. Increments the visit counter of each visited node. - fn count_nodes_inner(constraint: &mut ConstraintCircuit) -> usize { - let num_unvisited_self = match constraint.visited_counter { - 0 => 1, - _ => 0, - }; - constraint.visited_counter += 1; - let num_unvisited_children = match &constraint.expression { - BinaryOperation(_, lhs, rhs) => { - let num_left = count_nodes_inner(&mut lhs.as_ref().borrow_mut()); - let num_right = count_nodes_inner(&mut rhs.as_ref().borrow_mut()); - num_left + num_right - } - _ => 0, - }; - - num_unvisited_self + num_unvisited_children - } - - /// Count the total number of unique nodes in the given multicircuit. - /// Also refreshes the visit counter for each node, similar to - /// [`refresh_visit_counters`](ConstraintCircuit::refresh_visit_counters). - fn count_nodes(constraints: &mut [ConstraintCircuit]) -> usize { - // The uniqueness of nodes is determined by their visit count. - // To ensure a correct node count, the visit count must be reset before counting nodes. - for constraint in constraints.iter_mut() { - ConstraintCircuit::reset_visit_count_for_tree(constraint); - } - constraints.iter_mut().map(|c| count_nodes_inner(c)).sum() - } - fn random_circuit_builder() -> ( ConstraintCircuitMonad, ConstraintCircuitBuilder, From 32f56bd912bc5a1bac1d8fd43592859f01d74d2d Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Fri, 12 May 2023 08:49:28 +0200 Subject: [PATCH 34/72] add additional degree-lowering tests --- triton-vm/src/table/constraint_circuit.rs | 241 ++++++++++++++++------ 1 file changed, 174 insertions(+), 67 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index ba46864e..07372501 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -1232,12 +1232,13 @@ mod constraint_circuit_tests { use crate::table::cascade_table::ExtCascadeTable; use crate::table::challenges::ChallengeId::U32Indeterminate; use crate::table::challenges::Challenges; - use crate::table::constraint_circuit::SingleRowIndicator::BaseRow; + use crate::table::constraint_circuit::SingleRowIndicator::*; use crate::table::hash_table::ExtHashTable; use crate::table::jump_stack_table::ExtJumpStackTable; use crate::table::lookup_table::ExtLookupTable; use crate::table::master_table; use crate::table::op_stack_table::ExtOpStackTable; + use crate::table::processor_table; use crate::table::processor_table::ExtProcessorTable; use crate::table::program_table::ExtProgramTable; use crate::table::ram_table::ExtRamTable; @@ -1729,9 +1730,7 @@ mod constraint_circuit_tests { /// Verify that all nodes evaluate to a unique value when given a randomized input. /// If this is not the case two nodes that are not equal evaluate to the same value. fn table_constraints_prop( - constraint_builder: &dyn Fn( - &ConstraintCircuitBuilder, - ) -> Vec>, + constraints: &[ConstraintCircuit], table_name: &str, ) { let seed = random(); @@ -1742,14 +1741,6 @@ mod constraint_circuit_tests { let challenges = challenges.to_vec(); let challenges = Challenges::new(challenges, &[], &[]); - let circuit_builder = ConstraintCircuitBuilder::new(); - let mut constraints = constraint_builder(&circuit_builder); - ConstraintCircuitMonad::constant_folding(&mut constraints); - let mut constraints = constraints.into_iter().map(|c| c.consume()).collect_vec(); - ConstraintCircuit::assert_has_unique_ids(&mut constraints); - let num_total_nodes = count_nodes(&mut constraints); - let constraints = constraints; - let num_rows = 2; let base_shape = [num_rows, master_table::NUM_BASE_COLUMNS]; let ext_shape = [num_rows, master_table::NUM_EXT_COLUMNS]; @@ -1764,16 +1755,37 @@ mod constraint_circuit_tests { } let circuit_degree = constraints.iter().map(|c| c.degree()).max().unwrap_or(-1); - println!("nodes in {table_name}: {num_total_nodes}"); println!("Max degree constraint for {table_name} table: {circuit_degree}"); } + fn build_constraints( + multicircuit_builder: &dyn Fn( + &ConstraintCircuitBuilder, + ) -> Vec>, + ) -> Vec> { + let multicircuit = build_multicircuit(multicircuit_builder); + let mut constraints = multicircuit.into_iter().map(|c| c.consume()).collect_vec(); + ConstraintCircuit::assert_has_unique_ids(&mut constraints); + constraints + } + + fn build_multicircuit( + multicircuit_builder: &dyn Fn( + &ConstraintCircuitBuilder, + ) -> Vec>, + ) -> Vec> { + let circuit_builder = ConstraintCircuitBuilder::new(); + let mut multicircuit = multicircuit_builder(&circuit_builder); + ConstraintCircuitMonad::constant_folding(&mut multicircuit); + multicircuit + } + #[test] fn constant_folding_processor_table_test() { - let init = ExtProcessorTable::initial_constraints; - let cons = ExtProcessorTable::consistency_constraints; - let tran = ExtProcessorTable::transition_constraints; - let term = ExtProcessorTable::terminal_constraints; + let init = build_constraints(&ExtProcessorTable::initial_constraints); + let cons = build_constraints(&ExtProcessorTable::consistency_constraints); + let tran = build_constraints(&ExtProcessorTable::transition_constraints); + let term = build_constraints(&ExtProcessorTable::terminal_constraints); table_constraints_prop(&init, "processor initial"); table_constraints_prop(&cons, "processor consistency"); table_constraints_prop(&tran, "processor transition"); @@ -1782,10 +1794,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_program_table_test() { - let init = ExtProgramTable::initial_constraints; - let cons = ExtProgramTable::consistency_constraints; - let tran = ExtProgramTable::transition_constraints; - let term = ExtProgramTable::terminal_constraints; + let init = build_constraints(&ExtProgramTable::initial_constraints); + let cons = build_constraints(&ExtProgramTable::consistency_constraints); + let tran = build_constraints(&ExtProgramTable::transition_constraints); + let term = build_constraints(&ExtProgramTable::terminal_constraints); table_constraints_prop(&init, "program initial"); table_constraints_prop(&cons, "program consistency"); table_constraints_prop(&tran, "program transition"); @@ -1794,10 +1806,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_jump_stack_table_test() { - let init = ExtJumpStackTable::initial_constraints; - let cons = ExtJumpStackTable::consistency_constraints; - let tran = ExtJumpStackTable::transition_constraints; - let term = ExtJumpStackTable::terminal_constraints; + let init = build_constraints(&ExtJumpStackTable::initial_constraints); + let cons = build_constraints(&ExtJumpStackTable::consistency_constraints); + let tran = build_constraints(&ExtJumpStackTable::transition_constraints); + let term = build_constraints(&ExtJumpStackTable::terminal_constraints); table_constraints_prop(&init, "jump stack initial"); table_constraints_prop(&cons, "jump stack consistency"); table_constraints_prop(&tran, "jump stack transition"); @@ -1806,10 +1818,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_op_stack_table_test() { - let init = ExtOpStackTable::initial_constraints; - let cons = ExtOpStackTable::consistency_constraints; - let tran = ExtOpStackTable::transition_constraints; - let term = ExtOpStackTable::terminal_constraints; + let init = build_constraints(&ExtOpStackTable::initial_constraints); + let cons = build_constraints(&ExtOpStackTable::consistency_constraints); + let tran = build_constraints(&ExtOpStackTable::transition_constraints); + let term = build_constraints(&ExtOpStackTable::terminal_constraints); table_constraints_prop(&init, "op stack initial"); table_constraints_prop(&cons, "op stack consistency"); table_constraints_prop(&tran, "op stack transition"); @@ -1818,10 +1830,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_ram_table_test() { - let init = ExtRamTable::initial_constraints; - let cons = ExtRamTable::consistency_constraints; - let tran = ExtRamTable::transition_constraints; - let term = ExtRamTable::terminal_constraints; + let init = build_constraints(&ExtRamTable::initial_constraints); + let cons = build_constraints(&ExtRamTable::consistency_constraints); + let tran = build_constraints(&ExtRamTable::transition_constraints); + let term = build_constraints(&ExtRamTable::terminal_constraints); table_constraints_prop(&init, "ram initial"); table_constraints_prop(&cons, "ram consistency"); table_constraints_prop(&tran, "ram transition"); @@ -1830,10 +1842,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_hash_table_test() { - let init = ExtHashTable::initial_constraints; - let cons = ExtHashTable::consistency_constraints; - let tran = ExtHashTable::transition_constraints; - let term = ExtHashTable::terminal_constraints; + let init = build_constraints(&ExtHashTable::initial_constraints); + let cons = build_constraints(&ExtHashTable::consistency_constraints); + let tran = build_constraints(&ExtHashTable::transition_constraints); + let term = build_constraints(&ExtHashTable::terminal_constraints); table_constraints_prop(&init, "hash initial"); table_constraints_prop(&cons, "hash consistency"); table_constraints_prop(&tran, "hash transition"); @@ -1842,10 +1854,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_u32_table_test() { - let init = ExtU32Table::initial_constraints; - let cons = ExtU32Table::consistency_constraints; - let tran = ExtU32Table::transition_constraints; - let term = ExtU32Table::terminal_constraints; + let init = build_constraints(&ExtU32Table::initial_constraints); + let cons = build_constraints(&ExtU32Table::consistency_constraints); + let tran = build_constraints(&ExtU32Table::transition_constraints); + let term = build_constraints(&ExtU32Table::terminal_constraints); table_constraints_prop(&init, "u32 initial"); table_constraints_prop(&cons, "u32 consistency"); table_constraints_prop(&tran, "u32 transition"); @@ -1854,10 +1866,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_cascade_table_test() { - let init = ExtCascadeTable::initial_constraints; - let cons = ExtCascadeTable::consistency_constraints; - let tran = ExtCascadeTable::transition_constraints; - let term = ExtCascadeTable::terminal_constraints; + let init = build_constraints(&ExtCascadeTable::initial_constraints); + let cons = build_constraints(&ExtCascadeTable::consistency_constraints); + let tran = build_constraints(&ExtCascadeTable::transition_constraints); + let term = build_constraints(&ExtCascadeTable::terminal_constraints); table_constraints_prop(&init, "cascade initial"); table_constraints_prop(&cons, "cascade consistency"); table_constraints_prop(&tran, "cascade transition"); @@ -1866,10 +1878,10 @@ mod constraint_circuit_tests { #[test] fn constant_folding_lookup_table_test() { - let init = ExtLookupTable::initial_constraints; - let cons = ExtLookupTable::consistency_constraints; - let tran = ExtLookupTable::transition_constraints; - let term = ExtLookupTable::terminal_constraints; + let init = build_constraints(&ExtLookupTable::initial_constraints); + let cons = build_constraints(&ExtLookupTable::consistency_constraints); + let tran = build_constraints(&ExtLookupTable::transition_constraints); + let term = build_constraints(&ExtLookupTable::terminal_constraints); table_constraints_prop(&init, "lookup initial"); table_constraints_prop(&cons, "lookup consistency"); table_constraints_prop(&tran, "lookup transition"); @@ -1879,40 +1891,121 @@ mod constraint_circuit_tests { #[test] fn simple_degree_lowering_test() { let builder = ConstraintCircuitBuilder::new(); - let x = builder.input(BaseRow(0)); - let x_pow_3 = x.clone() * x.clone() * x.clone(); - let x_pow_5 = x_pow_3.clone() * x.clone() * x; + let x = || builder.input(BaseRow(0)); + let x_pow_3 = x() * x() * x(); + let x_pow_5 = x() * x() * x() * x() * x(); let mut multicircuit = [x_pow_5, x_pow_3]; - let target_deg = 3; + let target_degree = 3; let num_base_cols = 1; let num_ext_cols = 0; - let (new_base_constraints, new_ext_constraints) = ConstraintCircuitMonad::lower_to_degree( + let (new_base_constraints, new_ext_constraints) = lower_degree_and_assert_properties( &mut multicircuit, - target_deg, + target_degree, num_base_cols, num_ext_cols, ); + assert!(new_ext_constraints.is_empty()); assert_eq!(1, new_base_constraints.len()); + } - assert!(ConstraintCircuitMonad::multicircuit_degree(&multicircuit) <= target_deg); + #[test] + fn somewhat_simple_degree_lowering_test() { + let builder = ConstraintCircuitBuilder::new(); + let x = |i| builder.input(BaseRow(i)); + let y = |i| builder.input(ExtRow(i)); + let b_con = |i: u64| builder.b_constant(i.into()); + + let constraint_0 = x(0) * x(0) * (x(1) - x(2)) - x(0) * x(2) - b_con(42); + let constraint_1 = x(1) * (x(1) - b_con(5)) * x(2) * (x(2) - b_con(1)); + let constraint_2 = y(0) + * (b_con(2) * x(0) + b_con(3) * x(1) + b_con(4) * x(2)) + * (b_con(4) * x(0) + b_con(8) * x(1) + b_con(16) * x(2)) + - y(1); + + let mut multicircuit = [constraint_0, constraint_1, constraint_2]; + + let target_degree = 2; + let num_base_cols = 3; + let num_ext_cols = 2; + let (new_base_constraints, new_ext_constraints) = lower_degree_and_assert_properties( + &mut multicircuit, + target_degree, + num_base_cols, + num_ext_cols, + ); + + assert!(new_base_constraints.len() <= 3); + assert!(new_ext_constraints.len() <= 1); + } + + #[test] + fn serious_degree_lowering_test() { + let mut multicircuit = build_multicircuit(&ExtProcessorTable::transition_constraints); + let target_degree = 3; + let num_base_cols = processor_table::BASE_WIDTH; + let num_ext_cols = processor_table::EXT_WIDTH; + lower_degree_and_assert_properties( + &mut multicircuit, + target_degree, + num_base_cols, + num_ext_cols, + ); + } + + /// Like [`ConstraintCircuitMonad::lower_to_degree`] with additional assertion of expected + /// properties. Also prints: + /// - the given multicircuit prior to degree lowering + /// - the multicircuit after degree lowering + /// - the new base constraints + /// - the new extension constraints + /// - the numbers of original and new constraints + fn lower_degree_and_assert_properties( + multicircuit: &mut [ConstraintCircuitMonad], + target_deg: Degree, + num_base_cols: usize, + num_ext_cols: usize, + ) -> ( + Vec>, + Vec>, + ) { + let num_constraints = multicircuit.len(); + println!("original multicircuit:"); + for circuit in multicircuit.iter() { + println!(" {circuit}"); + } + + let (new_base_constraints, new_ext_constraints) = ConstraintCircuitMonad::lower_to_degree( + multicircuit, + target_deg, + num_base_cols, + num_ext_cols, + ); + + assert_eq!(num_constraints, multicircuit.len()); + assert!(ConstraintCircuitMonad::multicircuit_degree(multicircuit) <= target_deg); assert!(ConstraintCircuitMonad::multicircuit_degree(&new_base_constraints) <= target_deg); assert!(ConstraintCircuitMonad::multicircuit_degree(&new_ext_constraints) <= target_deg); - for (i, constraint) in new_base_constraints.iter().enumerate() { - let expression = constraint.circuit.as_ref().borrow().expression.clone(); - let BinaryOperation(BinOp::Sub, lhs, _) = expression else { - panic!("New base constraint {i} must be a subtraction."); - }; - let lhs_degree = lhs.as_ref().borrow().degree(); - assert_eq!( - 1, lhs_degree, - "New base constraint {i} must be a simple substitution." - ); + for (constraint_type, constraints) in [ + ("base", &new_base_constraints), + ("ext", &new_ext_constraints), + ] { + for (i, constraint) in constraints.iter().enumerate() { + let expression = constraint.circuit.as_ref().borrow().expression.clone(); + let BinaryOperation(BinOp::Sub, lhs, _) = expression else { + panic!("New {constraint_type} constraint {i} must be a subtraction."); + }; + let lhs_degree = lhs.as_ref().borrow().degree(); + assert_eq!( + 1, lhs_degree, + "New {constraint_type} constraint {i} must be a simple substitution." + ); + } } - println!("multicircuit:"); + println!("new multicircuit:"); for circuit in multicircuit.iter() { println!(" {circuit}"); } @@ -1920,5 +2013,19 @@ mod constraint_circuit_tests { for constraint in new_base_constraints.iter() { println!(" {constraint}"); } + println!("new ext constraints:"); + for constraint in new_ext_constraints.iter() { + println!(" {constraint}"); + } + + let num_new_base_constraints = new_base_constraints.len(); + let num_new_ext_constraints = new_ext_constraints.len(); + println!( + "Started with {num_constraints} constraints. \ + Derived {num_new_base_constraints} new base, \ + {num_new_ext_constraints} new extension constraints." + ); + + (new_base_constraints, new_ext_constraints) } } From 7d2da4d76cafbef5667370b4dc4a66139adcf44f Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 23 May 2023 09:04:34 +0200 Subject: [PATCH 35/72] simplify constant folding for constraint circuits --- triton-vm/src/table/constraint_circuit.rs | 181 +++++++--------------- 1 file changed, 55 insertions(+), 126 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 07372501..efcbdaef 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -32,15 +32,13 @@ use CircuitExpression::*; use crate::table::challenges::ChallengeId; use crate::table::challenges::Challenges; -#[derive(Debug, Clone, Copy, PartialEq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)] pub enum BinOp { Add, Sub, Mul, } -impl Eq for BinOp {} - impl Display for BinOp { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -51,6 +49,19 @@ impl Display for BinOp { } } +impl BinOp { + pub fn operation(&self, lhs: L, rhs: R) -> O + where + L: Add + Sub + Mul, + { + match self { + BinOp::Add => lhs + rhs, + BinOp::Sub => lhs - rhs, + BinOp::Mul => lhs * rhs, + } + } +} + /// Describes the position of a variable in a constraint polynomial in the row layout applicable /// for a certain kind of constraint polynomial. /// @@ -773,138 +784,62 @@ impl ConstraintCircuitMonad { } fn find_equivalent_expression(&self) -> Option>>> { - if let BinaryOperation(binop, lhs, rhs) = &self.circuit.as_ref().borrow().expression { + if let BinaryOperation(op, lhs, rhs) = &self.circuit.as_ref().borrow().expression { // a + 0 = a ∧ a - 0 = a - if matches!(binop, BinOp::Add | BinOp::Sub) && rhs.borrow().is_zero() { - return Some(Rc::clone(lhs)); + if matches!(op, BinOp::Add | BinOp::Sub) && rhs.borrow().is_zero() { + return Some(lhs.clone()); } // 0 + a = a - if *binop == BinOp::Add && lhs.borrow().is_zero() { - return Some(Rc::clone(rhs)); + if op == &BinOp::Add && lhs.borrow().is_zero() { + return Some(rhs.clone()); } - if matches!(binop, BinOp::Mul) { + if op == &BinOp::Mul { // a * 1 = a if rhs.borrow().is_one() { - return Some(Rc::clone(lhs)); + return Some(lhs.clone()); } - // 1 * a = a if lhs.borrow().is_one() { - return Some(Rc::clone(rhs)); + return Some(rhs.clone()); } - // 0 * a = 0 if lhs.borrow().is_zero() { - return Some(Rc::clone(lhs)); + return Some(lhs.clone()); } - // a * 0 = 0 if rhs.borrow().is_zero() { - return Some(Rc::clone(rhs)); - } - } - - // if left and right hand sides are both constants - if let XConstant(lhs_xfe) = lhs.borrow().expression { - if let XConstant(rhs_xfe) = rhs.borrow().expression { - return match binop { - BinOp::Add => Some(Rc::new(RefCell::new( - self.builder - .make_leaf(XConstant(lhs_xfe + rhs_xfe)) - .consume(), - ))), - BinOp::Sub => Some(Rc::new(RefCell::new( - self.builder - .make_leaf(XConstant(lhs_xfe - rhs_xfe)) - .consume(), - ))), - BinOp::Mul => Some(Rc::new(RefCell::new( - self.builder - .make_leaf(XConstant(lhs_xfe * rhs_xfe)) - .consume(), - ))), - }; - } - - if let BConstant(rhs_bfe) = rhs.borrow().expression { - return match binop { - BinOp::Add => Some(Rc::new(RefCell::new( - self.builder - .make_leaf(XConstant(lhs_xfe + rhs_bfe.lift())) - .consume(), - ))), - BinOp::Sub => Some(Rc::new(RefCell::new( - self.builder - .make_leaf(XConstant(lhs_xfe - rhs_bfe.lift())) - .consume(), - ))), - BinOp::Mul => Some(Rc::new(RefCell::new( - self.builder - .make_leaf(XConstant(lhs_xfe * rhs_bfe)) - .consume(), - ))), - }; + return Some(rhs.clone()); } } - if let BConstant(lhs_bfe) = lhs.borrow().expression { - if let XConstant(rhs_xfe) = rhs.borrow().expression { - return match binop { - BinOp::Add => Some(Rc::new(RefCell::new( - self.builder - .make_leaf(XConstant(lhs_bfe.lift() + rhs_xfe)) - .consume(), - ))), - BinOp::Sub => Some(Rc::new(RefCell::new( - self.builder - .make_leaf(XConstant(lhs_bfe.lift() - rhs_xfe)) - .consume(), - ))), - BinOp::Mul => Some(Rc::new(RefCell::new( - self.builder - .make_leaf(XConstant(rhs_xfe * lhs_bfe)) - .consume(), - ))), - }; - } + // if both left and right hand sides are constants, simplify + let maybe_new_const = match (&lhs.borrow().expression, &rhs.borrow().expression) { + (&BConstant(l), &BConstant(r)) => Some(BConstant(op.operation(l, r))), + (&BConstant(l), &XConstant(r)) => Some(XConstant(op.operation(l, r))), + (&XConstant(l), &BConstant(r)) => Some(XConstant(op.operation(l, r))), + (&XConstant(l), &XConstant(r)) => Some(XConstant(op.operation(l, r))), + _ => None, + }; - if let BConstant(rhs_bfe) = rhs.borrow().expression { - return match binop { - BinOp::Add => Some(Rc::new(RefCell::new( - self.builder - .make_leaf(BConstant(lhs_bfe + rhs_bfe)) - .consume(), - ))), - BinOp::Sub => Some(Rc::new(RefCell::new( - self.builder - .make_leaf(BConstant(lhs_bfe - rhs_bfe)) - .consume(), - ))), - BinOp::Mul => Some(Rc::new(RefCell::new( - self.builder - .make_leaf(BConstant(lhs_bfe * lhs_bfe)) - .consume(), - ))), - }; - } + if let Some(new_const) = maybe_new_const { + let new_const = self.builder.make_leaf(new_const).consume(); + let new_const = Rc::new(RefCell::new(new_const)); + return Some(new_const); } } None } /// Apply constant folding to simplify the (sub)tree. - /// If the subtree is a leaf (terminal), no change. + /// If the subtree is a leaf: no change. /// If the subtree is a binary operation on: /// - /// - one constant x one constant => fold - /// - one constant x one expr => can't - /// - one expr x one constant => can't - /// - one expr x one expr => can't + /// - constant x constant => fold + /// - anything else => can't fold /// - /// This operation mutates self and returns true if a change was - /// applied anywhere in the tree. + /// This operation mutates self and returns true if a change was applied anywhere in the tree. fn constant_fold_inner(&mut self) -> (bool, Option>>>) { let mut change_tracker = false; let self_expr = self.circuit.as_ref().borrow().expression.clone(); @@ -1165,32 +1100,26 @@ impl ConstraintCircuitBuilder { } } - let new_id = self.id_counter.as_ref().borrow().to_owned(); + let id = self.id_counter.as_ref().borrow().to_owned(); + let circuit = ConstraintCircuit { + id, + visited_counter: 0, + expression, + }; + let circuit = Rc::new(RefCell::new(circuit)); let new_node = ConstraintCircuitMonad { - circuit: Rc::new(RefCell::new(ConstraintCircuit { - visited_counter: 0usize, - expression, - id: new_id, - })), + circuit, builder: self.clone(), }; - // Check if node already exists, return the existing one if it does - let contained = self.all_nodes.as_ref().borrow().contains(&new_node); - if contained { - let ret0 = &self.all_nodes.as_ref().borrow(); - let ret1 = &(*ret0.get(&new_node).as_ref().unwrap()).clone(); - return ret1.to_owned(); + let mut all_nodes = self.all_nodes.as_ref().borrow_mut(); + if let Some(same_node) = all_nodes.get(&new_node) { + same_node.to_owned() + } else { + *self.id_counter.as_ref().borrow_mut() += 1; + all_nodes.insert(new_node.clone()); + new_node } - - // If node did not already exist, increment counter and insert node into hash set - *self.id_counter.as_ref().borrow_mut() = new_id + 1; - self.all_nodes - .as_ref() - .borrow_mut() - .insert(new_node.clone()); - - new_node } /// Substitute all nodes with ID `old_id` with the given `new` node. From 22ea4159542e7ba3b2212506e11049700fa62094 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 23 May 2023 09:28:18 +0200 Subject: [PATCH 36/72] simplify duplication check when construction binary operation --- triton-vm/src/table/constraint_circuit.rs | 109 +++++++--------------- 1 file changed, 32 insertions(+), 77 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index efcbdaef..58003d6b 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -640,91 +640,46 @@ fn binop( lhs: ConstraintCircuitMonad, rhs: ConstraintCircuitMonad, ) -> ConstraintCircuitMonad { - // Get ID for the new node - let new_index = lhs.builder.id_counter.as_ref().borrow().to_owned(); - let lhs = Rc::new(RefCell::new(lhs)); - let rhs = Rc::new(RefCell::new(rhs)); - + let id = lhs.builder.id_counter.as_ref().borrow().to_owned(); + let expression = BinaryOperation(binop, lhs.circuit.clone(), rhs.circuit.clone()); + let circuit = ConstraintCircuit { + id, + visited_counter: 0, + expression, + }; + let circuit = Rc::new(RefCell::new(circuit)); let new_node = ConstraintCircuitMonad { - circuit: Rc::new(RefCell::new(ConstraintCircuit { - visited_counter: 0, - expression: BinaryOperation( - binop, - Rc::clone(&lhs.as_ref().borrow().circuit), - Rc::clone(&rhs.as_ref().borrow().circuit), - ), - id: new_index, - })), - builder: lhs.as_ref().borrow().builder.clone(), + circuit, + builder: lhs.builder.clone(), }; - // check if node already exists - let contained = lhs - .as_ref() - .borrow() - .builder - .all_nodes - .as_ref() - .borrow() - .contains(&new_node); - if contained { - let ret0 = &lhs.as_ref().borrow(); - let ret1 = ret0.builder.all_nodes.as_ref().borrow(); - let ret2 = &(*ret1.get(&new_node).as_ref().unwrap()).clone(); - return ret2.to_owned(); - } - - // If the operator commutes, check if the inverse node has already been constructed. - // If it has, return this instead. Do not allow a new one to be built. + let mut all_nodes = lhs.builder.all_nodes.as_ref().borrow_mut(); + if let Some(same_node) = all_nodes.get(&new_node) { + return same_node.to_owned(); + } + + // If the operator commutes, check if the switched node has already been constructed. + // If it has, return it instead. Do not allow a new one to be built. if matches!(binop, BinOp::Add | BinOp::Mul) { - let new_node_inverted = ConstraintCircuitMonad { - circuit: Rc::new(RefCell::new(ConstraintCircuit { - visited_counter: 0, - expression: BinaryOperation( - binop, - // Switch rhs and lhs for symmetric operators to check membership in hash set - Rc::clone(&rhs.as_ref().borrow().circuit), - Rc::clone(&lhs.as_ref().borrow().circuit), - ), - id: new_index, - })), - builder: lhs.as_ref().borrow().builder.clone(), + let expression_switched = BinaryOperation(binop, rhs.circuit, lhs.circuit); + let circuit_switched = ConstraintCircuit { + id, + visited_counter: 0, + expression: expression_switched, }; - - // check if node already exists - let inverted_contained = lhs - .as_ref() - .borrow() - .builder - .all_nodes - .as_ref() - .borrow() - .contains(&new_node_inverted); - if inverted_contained { - let ret0 = &lhs.as_ref().borrow(); - let ret1 = ret0.builder.all_nodes.as_ref().borrow(); - let ret2 = &(*ret1.get(&new_node_inverted).as_ref().unwrap()).clone(); - return ret2.to_owned(); + let circuit_switched = Rc::new(RefCell::new(circuit_switched)); + let new_node_switched = ConstraintCircuitMonad { + circuit: circuit_switched, + builder: lhs.builder.clone(), + }; + if let Some(same_node) = all_nodes.get(&new_node_switched) { + return same_node.to_owned(); } } - // Increment counter index - *lhs.as_ref() - .borrow() - .builder - .id_counter - .as_ref() - .borrow_mut() = new_index + 1; - - // Store new node in HashSet - let inserted_value_was_new = new_node - .builder - .all_nodes - .as_ref() - .borrow_mut() - .insert(new_node.clone()); - assert!(inserted_value_was_new, "Binop-created value must be new"); - + *lhs.builder.id_counter.as_ref().borrow_mut() += 1; + let was_inserted = all_nodes.insert(new_node.clone()); + assert!(was_inserted, "Binop-created value must be new"); new_node } From 568e114e36df4446f4eb6a9b41e82be45f4b5a67 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 23 May 2023 10:28:25 +0200 Subject: [PATCH 37/72] use new `BinOp::operation` instead of `match`ing manually --- triton-vm/src/table/constraint_circuit.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 58003d6b..7fb90308 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -545,11 +545,7 @@ impl ConstraintCircuit { .as_ref() .borrow() .evaluate(base_table, ext_table, challenges); - match binop { - BinOp::Add => lhs_value + rhs_value, - BinOp::Sub => lhs_value - rhs_value, - BinOp::Mul => lhs_value * rhs_value, - } + binop.operation(lhs_value, rhs_value) } } } @@ -1586,11 +1582,7 @@ mod constraint_circuit_tests { let rhs = rhs.as_ref().borrow(); let lhs = evaluate_assert_unique(&lhs, challenges, base_rows, ext_rows, values); let rhs = evaluate_assert_unique(&rhs, challenges, base_rows, ext_rows, values); - match binop { - BinOp::Add => lhs + rhs, - BinOp::Sub => lhs - rhs, - BinOp::Mul => lhs * rhs, - } + binop.operation(lhs, rhs) } _ => constraint.evaluate(base_rows, ext_rows, challenges), }; From c1f8f4493f09c98c228f3c9a59214aab316b6a82 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 23 May 2023 11:12:34 +0200 Subject: [PATCH 38/72] use Schwartz-Zippel lemma to check uniqueness of substitution rules --- triton-vm/src/table/constraint_circuit.rs | 42 +++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 7fb90308..d9465c2c 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -1846,6 +1846,10 @@ mod constraint_circuit_tests { Vec>, Vec>, ) { + let seed = random(); + let mut rng = StdRng::seed_from_u64(seed); + println!("seed: {seed}"); + let num_constraints = multicircuit.len(); println!("original multicircuit:"); for circuit in multicircuit.iter() { @@ -1864,13 +1868,15 @@ mod constraint_circuit_tests { assert!(ConstraintCircuitMonad::multicircuit_degree(&new_base_constraints) <= target_deg); assert!(ConstraintCircuitMonad::multicircuit_degree(&new_ext_constraints) <= target_deg); + // Check that the new constraints are simple substitutions. + let mut substitution_rules = vec![]; for (constraint_type, constraints) in [ ("base", &new_base_constraints), ("ext", &new_ext_constraints), ] { for (i, constraint) in constraints.iter().enumerate() { let expression = constraint.circuit.as_ref().borrow().expression.clone(); - let BinaryOperation(BinOp::Sub, lhs, _) = expression else { + let BinaryOperation(BinOp::Sub, lhs, rhs) = expression else { panic!("New {constraint_type} constraint {i} must be a subtraction."); }; let lhs_degree = lhs.as_ref().borrow().degree(); @@ -1878,9 +1884,41 @@ mod constraint_circuit_tests { 1, lhs_degree, "New {constraint_type} constraint {i} must be a simple substitution." ); + substitution_rules.push(rhs.as_ref().borrow().clone()); + } + } + + // Use the Schwartz-Zippel lemma to check no two substitution rules are equal. + let challenges: [XFieldElement; Challenges::num_challenges_to_sample()] = rng.gen(); + let challenges = challenges.to_vec(); + let challenges = Challenges::new(challenges, &[], &[]); + + let num_rows = 2; + let num_new_base_constraints = new_base_constraints.len(); + let num_new_ext_constraints = new_ext_constraints.len(); + let num_base_cols = master_table::NUM_BASE_COLUMNS + num_new_base_constraints; + let num_ext_cols = master_table::NUM_EXT_COLUMNS + num_new_ext_constraints; + let base_shape = [num_rows, num_base_cols]; + let ext_shape = [num_rows, num_ext_cols]; + let base_rows = Array2::from_shape_simple_fn(base_shape, || rng.gen::()); + let ext_rows = Array2::from_shape_simple_fn(ext_shape, || rng.gen::()); + let base_rows = base_rows.view(); + let ext_rows = ext_rows.view(); + + let evaluated_substitution_rules = substitution_rules + .iter() + .map(|c| c.evaluate(base_rows, ext_rows, &challenges)); + + let mut values_to_index = HashMap::new(); + for (idx, value) in evaluated_substitution_rules.enumerate() { + if let Some(index) = values_to_index.get(&value) { + panic!("Substitution {idx} must be distinct from substitution {index}."); + } else { + values_to_index.insert(value, idx); } } + // Print the multicircuit and new constraints after degree lowering. println!("new multicircuit:"); for circuit in multicircuit.iter() { println!(" {circuit}"); @@ -1894,8 +1932,6 @@ mod constraint_circuit_tests { println!(" {constraint}"); } - let num_new_base_constraints = new_base_constraints.len(); - let num_new_ext_constraints = new_ext_constraints.len(); println!( "Started with {num_constraints} constraints. \ Derived {num_new_base_constraints} new base, \ From 54c3282375aba3cf331ab66e9dfca5f87f57824f Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 23 May 2023 12:09:05 +0200 Subject: [PATCH 39/72] remove prototype code from degree lowering table --- triton-vm/src/table/degree_lowering_table.rs | 57 ++++---------------- 1 file changed, 10 insertions(+), 47 deletions(-) diff --git a/triton-vm/src/table/degree_lowering_table.rs b/triton-vm/src/table/degree_lowering_table.rs index 97d03b27..632baf5f 100644 --- a/triton-vm/src/table/degree_lowering_table.rs +++ b/triton-vm/src/table/degree_lowering_table.rs @@ -1,8 +1,5 @@ -use ndarray::s; use ndarray::ArrayView2; use ndarray::ArrayViewMut2; -use ndarray::Axis; -use ndarray::Zip; use strum::EnumCount; use strum_macros::Display; use strum_macros::EnumCount as EnumCountMacro; @@ -10,24 +7,24 @@ use strum_macros::EnumIter; use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::x_field_element::XFieldElement; -use crate::table::master_table::NUM_BASE_COLUMNS; -use crate::table::master_table::NUM_EXT_COLUMNS; - pub const BASE_WIDTH: usize = DegreeLoweringBaseTableColumn::COUNT; pub const EXT_WIDTH: usize = DegreeLoweringExtTableColumn::COUNT; pub const FULL_WIDTH: usize = BASE_WIDTH + EXT_WIDTH; +// This file is a placeholder for auto-generated code. +// Run `cargo run --bin constraint-evaluation-generator` to generate the actual code. + #[repr(usize)] #[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] pub enum DegreeLoweringBaseTableColumn { - /// To be replaced by generated code. Needed to keep the type-checker happy. + /// To be replaced by generated code. Needed to keep the type-checker and linter happy. STANDIN, } #[repr(usize)] #[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] pub enum DegreeLoweringExtTableColumn { - /// To be replaced by generated code. Needed to keep the type-checker happy. + /// To be replaced by generated code. Needed to keep the type-checker and linter happy. STANDIN, } @@ -35,48 +32,14 @@ pub enum DegreeLoweringExtTableColumn { pub struct DegreeLoweringTable {} impl DegreeLoweringTable { - pub fn fill_deterministic_base_columns(master_base_table: &mut ArrayViewMut2) { - assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); - - let main_trace_section_start = 0; - let main_trace_section_end = main_trace_section_start + NUM_BASE_COLUMNS - BASE_WIDTH; - let deterministic_section_start = main_trace_section_end; - let deterministic_section_end = deterministic_section_start + BASE_WIDTH; - - let (main_trace_section, mut deterministic_section) = master_base_table.multi_slice_mut(( - s![.., main_trace_section_start..main_trace_section_end], - s![.., deterministic_section_start..deterministic_section_end], - )); - - Zip::from(main_trace_section.axis_iter(Axis(0))) - .and(deterministic_section.axis_iter_mut(Axis(0))) - .par_for_each(|main_trace_row, mut deterministic_row| { - deterministic_row[0] = main_trace_row[0] * main_trace_row[1]; - }); + pub fn fill_deterministic_base_columns(_master_base_table: &mut ArrayViewMut2) { + // to be filled by generated code } pub fn fill_deterministic_ext_columns( - master_base_table: ArrayView2, - master_ext_table: &mut ArrayViewMut2, + _master_base_table: ArrayView2, + _master_ext_table: &mut ArrayViewMut2, ) { - assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); - assert_eq!(NUM_EXT_COLUMNS, master_ext_table.ncols()); - - let main_ext_section_start = 0; - let main_ext_section_end = main_ext_section_start + NUM_EXT_COLUMNS - EXT_WIDTH; - let det_ext_section_start = main_ext_section_end; - let det_ext_section_end = det_ext_section_start + EXT_WIDTH; - - let (main_ext_section, mut deterministic_section) = master_ext_table.multi_slice_mut(( - s![.., main_ext_section_start..main_ext_section_end], - s![.., det_ext_section_start..det_ext_section_end], - )); - - Zip::from(master_base_table.axis_iter(Axis(0))) - .and(main_ext_section.axis_iter(Axis(0))) - .and(deterministic_section.axis_iter_mut(Axis(0))) - .par_for_each(|base_row, main_ext_row, mut det_ext_row| { - det_ext_row[0] = base_row[0] * main_ext_row[0]; - }); + // to be filled by generated code } } From 18797fcc26319e5cc1e373798f03666dc2b2f8c6 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 23 May 2023 13:07:41 +0200 Subject: [PATCH 40/72] remove debug statements --- triton-vm/src/table/constraint_circuit.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index d9465c2c..e134961f 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -878,11 +878,9 @@ impl ConstraintCircuitMonad { while Self::multicircuit_degree(multicircuit) > target_degree { let chosen_node = Self::pick_node_to_substitute(target_degree, &builder); - dbg!(chosen_node.as_ref().borrow().degree()); // Create a new variable. let chosen_node_id = chosen_node.as_ref().borrow().id; - dbg!(chosen_node_id); let chosen_node_is_base_col = chosen_node.as_ref().borrow().evaluates_to_base_element(); let new_col_idx = match chosen_node_is_base_col { true => num_base_cols + base_constraints.len(), From d77a2a4e2d260a3454e983479e87934d0eb70f20 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 24 May 2023 11:10:16 +0200 Subject: [PATCH 41/72] declare target AIR degree in master table Also, remove unused `const NUM_TABLES` and add a few doc strings. --- triton-vm/src/table/master_table.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index 2896edb3..3f586340 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -13,7 +13,6 @@ use num_traits::One; use rand::distributions::Standard; use rand::prelude::Distribution; use rand::random; -use strum::EnumCount; use strum_macros::Display; use strum_macros::EnumCount as EnumCountMacro; use strum_macros::EnumIter; @@ -56,8 +55,11 @@ use crate::table::u32_table::U32Table; use crate::table::*; use crate::vm::AlgebraicExecutionTrace; -pub const NUM_TABLES: usize = TableId::COUNT; +/// The degree of the AIR after the degree lowering step. +/// See also [`DegreeLoweringTable`]. +pub const AIR_TARGET_DEGREE: Degree = 5; +/// The total number of base columns across all tables. pub const NUM_BASE_COLUMNS: usize = program_table::BASE_WIDTH + processor_table::BASE_WIDTH + op_stack_table::BASE_WIDTH @@ -68,6 +70,8 @@ pub const NUM_BASE_COLUMNS: usize = program_table::BASE_WIDTH + lookup_table::BASE_WIDTH + u32_table::BASE_WIDTH + degree_lowering_table::BASE_WIDTH; + +/// The total number of extension columns across all tables. pub const NUM_EXT_COLUMNS: usize = program_table::EXT_WIDTH + processor_table::EXT_WIDTH + op_stack_table::EXT_WIDTH @@ -78,6 +82,8 @@ pub const NUM_EXT_COLUMNS: usize = program_table::EXT_WIDTH + lookup_table::EXT_WIDTH + u32_table::EXT_WIDTH + degree_lowering_table::EXT_WIDTH; + +/// The total number of columns across all tables. pub const NUM_COLUMNS: usize = NUM_BASE_COLUMNS + NUM_EXT_COLUMNS; pub const PROGRAM_TABLE_START: usize = 0; From bb5ef171da67c033eb283b0e1361244d17ed3f1d Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 24 May 2023 11:35:08 +0200 Subject: [PATCH 42/72] remove stand-in columns and the associated tests clippy was unhappy with --- triton-vm/src/table/degree_lowering_table.rs | 12 ++---------- triton-vm/src/table/table_column.rs | 19 ------------------- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/triton-vm/src/table/degree_lowering_table.rs b/triton-vm/src/table/degree_lowering_table.rs index 632baf5f..b3b3d813 100644 --- a/triton-vm/src/table/degree_lowering_table.rs +++ b/triton-vm/src/table/degree_lowering_table.rs @@ -14,19 +14,11 @@ pub const FULL_WIDTH: usize = BASE_WIDTH + EXT_WIDTH; // This file is a placeholder for auto-generated code. // Run `cargo run --bin constraint-evaluation-generator` to generate the actual code. -#[repr(usize)] #[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] -pub enum DegreeLoweringBaseTableColumn { - /// To be replaced by generated code. Needed to keep the type-checker and linter happy. - STANDIN, -} +pub enum DegreeLoweringBaseTableColumn {} -#[repr(usize)] #[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] -pub enum DegreeLoweringExtTableColumn { - /// To be replaced by generated code. Needed to keep the type-checker and linter happy. - STANDIN, -} +pub enum DegreeLoweringExtTableColumn {} #[derive(Debug, Clone)] pub struct DegreeLoweringTable {} diff --git a/triton-vm/src/table/table_column.rs b/triton-vm/src/table/table_column.rs index 85f35e5c..32993460 100644 --- a/triton-vm/src/table/table_column.rs +++ b/triton-vm/src/table/table_column.rs @@ -693,7 +693,6 @@ mod table_column_tests { use strum::IntoEnumIterator; use crate::table::cascade_table; - use crate::table::degree_lowering_table; use crate::table::hash_table; use crate::table::jump_stack_table; use crate::table::lookup_table; @@ -788,15 +787,6 @@ mod table_column_tests { + 1, "U32Table's BASE_WIDTH is 1 + its max column index", ); - assert_eq!( - degree_lowering_table::BASE_WIDTH, - DegreeLoweringBaseTableColumn::iter() - .last() - .unwrap() - .base_table_index() - + 1, - "DegreeLoweringTable's BASE_WIDTH is 1 + its max column index", - ); assert_eq!( program_table::EXT_WIDTH, @@ -867,15 +857,6 @@ mod table_column_tests { U32ExtTableColumn::iter().last().unwrap().ext_table_index() + 1, "U32Table's EXT_WIDTH is 1 + its max column index", ); - assert_eq!( - degree_lowering_table::EXT_WIDTH, - DegreeLoweringExtTableColumn::iter() - .last() - .unwrap() - .ext_table_index() - + 1, - "DegreeLoweringTable's EXT_WIDTH is 1 + its max column index", - ); } #[test] From 07a94155c413ec6d158dc046e53f5f11e8e63a8f Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 24 May 2023 11:11:43 +0200 Subject: [PATCH 43/72] generate necessary new columns for AIR of lowered degree --- constraint-evaluation-generator/src/main.rs | 245 +++++++++++++++++++- 1 file changed, 243 insertions(+), 2 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 3a1ed5a4..57a40430 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -6,15 +6,21 @@ use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::x_field_element::XFieldElement; use triton_vm::table::cascade_table::ExtCascadeTable; +use triton_vm::table::constraint_circuit::BinOp; use triton_vm::table::constraint_circuit::CircuitExpression; +use triton_vm::table::constraint_circuit::CircuitExpression::*; use triton_vm::table::constraint_circuit::ConstraintCircuit; use triton_vm::table::constraint_circuit::ConstraintCircuitBuilder; use triton_vm::table::constraint_circuit::ConstraintCircuitMonad; +use triton_vm::table::constraint_circuit::DualRowIndicator; use triton_vm::table::constraint_circuit::InputIndicator; +use triton_vm::table::constraint_circuit::SingleRowIndicator; use triton_vm::table::cross_table_argument::GrandCrossTableArg; +use triton_vm::table::degree_lowering_table; use triton_vm::table::hash_table::ExtHashTable; use triton_vm::table::jump_stack_table::ExtJumpStackTable; use triton_vm::table::lookup_table::ExtLookupTable; +use triton_vm::table::master_table; use triton_vm::table::op_stack_table::ExtOpStackTable; use triton_vm::table::processor_table::ExtProcessorTable; use triton_vm::table::program_table::ExtProgramTable; @@ -87,19 +93,72 @@ fn main() { ConstraintCircuitMonad::constant_folding(&mut transition_constraints); ConstraintCircuitMonad::constant_folding(&mut terminal_constraints); + // Subtract the degree lowering table's width from the total number of columns to guarantee + // the same number of columns even for repeated runs of the constraint evaluation generator. + let mut num_base_cols = master_table::NUM_BASE_COLUMNS - degree_lowering_table::BASE_WIDTH; + let mut num_ext_cols = master_table::NUM_EXT_COLUMNS - degree_lowering_table::EXT_WIDTH; + let (init_base_substitutions, init_ext_substitutions) = ConstraintCircuitMonad::lower_to_degree( + &mut initial_constraints, + master_table::AIR_TARGET_DEGREE, + num_base_cols, + num_ext_cols, + ); + num_base_cols += init_base_substitutions.len(); + num_ext_cols += init_ext_substitutions.len(); + + let (cons_base_substitutions, cons_ext_substitutions) = ConstraintCircuitMonad::lower_to_degree( + &mut consistency_constraints, + master_table::AIR_TARGET_DEGREE, + num_base_cols, + num_ext_cols, + ); + num_base_cols += cons_base_substitutions.len(); + num_ext_cols += cons_ext_substitutions.len(); + + let (tran_base_substitutions, tran_ext_substitutions) = ConstraintCircuitMonad::lower_to_degree( + &mut transition_constraints, + master_table::AIR_TARGET_DEGREE, + num_base_cols, + num_ext_cols, + ); + num_base_cols += tran_base_substitutions.len(); + num_ext_cols += tran_ext_substitutions.len(); + + let (term_base_substitutions, term_ext_substitutions) = ConstraintCircuitMonad::lower_to_degree( + &mut terminal_constraints, + master_table::AIR_TARGET_DEGREE, + num_base_cols, + num_ext_cols, + ); + + let table_code = generate_degree_lowering_table_code( + &init_base_substitutions, + &cons_base_substitutions, + &tran_base_substitutions, + &term_base_substitutions, + &init_ext_substitutions, + &cons_ext_substitutions, + &tran_ext_substitutions, + &term_ext_substitutions, + ); + let mut initial_constraints = consume(initial_constraints); let mut consistency_constraints = consume(consistency_constraints); let mut transition_constraints = consume(transition_constraints); let mut terminal_constraints = consume(terminal_constraints); - let code = generate_constraint_code( + // todo: add substitution rules to the constraints + + let constraint_code = generate_constraint_code( &mut initial_constraints, &mut consistency_constraints, &mut transition_constraints, &mut terminal_constraints, ); - std::fs::write("triton-vm/src/table/constraints.rs", code) + std::fs::write("triton-vm/src/table/degree_lowering_table.rs", table_code) + .expect("Writing to disk has failed."); + std::fs::write("triton-vm/src/table/constraints.rs", constraint_code) .expect("Writing to disk has failed."); if let Err(fmt_failed) = Command::new("cargo").arg("fmt").output() { @@ -523,3 +582,185 @@ fn evaluate_single_node( let operation = binop.to_string(); format!("({evaluated_lhs}) {operation} ({evaluated_rhs})") } + +/// Given a substitution rule, i.e., a `ConstraintCircuit` of the form `x - expr`, generate code +/// that evaluates `expr` on the appropriate base rows and sets `x` to the result. +fn substitution_rule_to_code(circuit: ConstraintCircuit) -> String { + let circuit_evaluates_to_base_element = circuit.evaluates_to_base_element(); + let BinaryOperation(BinOp::Sub, new_var, expr) = circuit.expression else { + panic!("Substitution rule must be a subtraction."); + }; + let Input(new_var) = new_var.as_ref().borrow().expression else { + panic!("Substitution rule must be a simple substitution."); + }; + let new_var_idx = match circuit_evaluates_to_base_element { + true => new_var.base_col_index(), + false => new_var.ext_col_index(), + }; + + let expr = expr.as_ref().borrow().to_owned(); + let expr = evaluate_single_node(usize::MAX, &expr, &HashSet::new()); + + format!("deterministic_row[{new_var_idx} - deterministic_section_start] = {expr};") +} + +/// Given all substitution rules, generate the code that evaluates them in order. +/// This includes generating the columns that are to be filled using the substitution rules. +#[allow(clippy::too_many_arguments)] +fn generate_degree_lowering_table_code( + init_base_substitutions: &[ConstraintCircuitMonad], + cons_base_substitutions: &[ConstraintCircuitMonad], + tran_base_substitutions: &[ConstraintCircuitMonad], + term_base_substitutions: &[ConstraintCircuitMonad], + init_ext_substitutions: &[ConstraintCircuitMonad], + cons_ext_substitutions: &[ConstraintCircuitMonad], + tran_ext_substitutions: &[ConstraintCircuitMonad], + term_ext_substitutions: &[ConstraintCircuitMonad], +) -> String { + let num_new_base_cols = init_base_substitutions.len() + + cons_base_substitutions.len() + + tran_base_substitutions.len() + + term_base_substitutions.len(); + let num_new_ext_cols = init_ext_substitutions.len() + + cons_ext_substitutions.len() + + tran_ext_substitutions.len() + + term_ext_substitutions.len(); + + // A zero-variant enum cannot be annotated with `repr(usize)`. + let base_repr_usize = match num_new_base_cols == 0 { + true => "", + false => "#[repr(usize)]", + }; + let ext_repr_usize = match num_new_ext_cols == 0 { + true => "", + false => "#[repr(usize)]", + }; + + let base_columns = (0..num_new_base_cols) + .map(|i| format!("DegreeLoweringBaseCol{i}")) + .collect_vec() + .join(",\n"); + let ext_columns = (0..num_new_ext_cols) + .map(|i| format!("DegreeLoweringExtCol{i}")) + .collect_vec() + .join(",\n"); + + // todo: generate code to compute the columns' content corresponding to the substitutions + for circuit in init_base_substitutions.iter() { + let _code = substitution_rule_to_code(circuit.circuit.as_ref().borrow().to_owned()); + } + + format!( + " +use ndarray::s; +use ndarray::ArrayView2; +use ndarray::ArrayViewMut2; +use ndarray::Axis; +use ndarray::Zip; +use strum::EnumCount; +use strum_macros::Display; +use strum_macros::EnumCount as EnumCountMacro; +use strum_macros::EnumIter; +use twenty_first::shared_math::b_field_element::BFieldElement; +use twenty_first::shared_math::x_field_element::XFieldElement; + +use crate::table::master_table::NUM_BASE_COLUMNS; +use crate::table::master_table::NUM_EXT_COLUMNS; + +pub const BASE_WIDTH: usize = DegreeLoweringBaseTableColumn::COUNT; +pub const EXT_WIDTH: usize = DegreeLoweringExtTableColumn::COUNT; +pub const FULL_WIDTH: usize = BASE_WIDTH + EXT_WIDTH; + +// This file has been auto-generated. Any modifications _will_ be lost. +// To re-generate, execute: +// `cargo run --bin constraint-evaluation-generator` + +{base_repr_usize} +#[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] +pub enum DegreeLoweringBaseTableColumn {{ + {base_columns} +}} + +{ext_repr_usize} +#[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] +pub enum DegreeLoweringExtTableColumn {{ + {ext_columns} +}} + +#[derive(Debug, Clone)] +pub struct DegreeLoweringTable {{}} + +impl DegreeLoweringTable {{ + pub fn fill_deterministic_base_columns(master_base_table: &mut ArrayViewMut2) {{ + assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); + + let main_trace_section_start = 0; + let main_trace_section_end = main_trace_section_start + NUM_BASE_COLUMNS - BASE_WIDTH; + let deterministic_section_start = main_trace_section_end; + let deterministic_section_end = deterministic_section_start + BASE_WIDTH; + + let (main_trace_section, mut deterministic_section) = master_base_table.multi_slice_mut(( + s![.., main_trace_section_start..main_trace_section_end], + s![.., deterministic_section_start..deterministic_section_end], + )); + + // For single-row constraints. + Zip::from(main_trace_section.axis_iter(Axis(0))) + .and(deterministic_section.axis_iter_mut(Axis(0))) + .par_for_each(|_base_row, mut _deterministic_row| {{ + }}); + + // For dual-row constraints. + // The last row of the deterministic section for transition constraints is not used. + let mut deterministic_section = deterministic_section.slice_mut(s![..-1, ..]); + Zip::from(main_trace_section.axis_windows(Axis(0), 2)) + .and(deterministic_section.exact_chunks_mut((1, deterministic_section.ncols()))) + .par_for_each(|main_trace_chunk, mut deterministic_chunk| {{ + let _current_base_row = main_trace_chunk.row(0); + let _next_base_row = main_trace_chunk.row(1); + let mut _current_deterministic_row = deterministic_chunk.row_mut(0); + }}); + }} + + pub fn fill_deterministic_ext_columns( + master_base_table: ArrayView2, + master_ext_table: &mut ArrayViewMut2, + ) {{ + assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); + assert_eq!(NUM_EXT_COLUMNS, master_ext_table.ncols()); + + let main_ext_section_start = 0; + let main_ext_section_end = main_ext_section_start + NUM_EXT_COLUMNS - EXT_WIDTH; + let det_ext_section_start = main_ext_section_end; + let det_ext_section_end = det_ext_section_start + EXT_WIDTH; + + let (main_ext_section, mut deterministic_section) = master_ext_table.multi_slice_mut(( + s![.., main_ext_section_start..main_ext_section_end], + s![.., det_ext_section_start..det_ext_section_end], + )); + + // For single-row constraints. + Zip::from(master_base_table.axis_iter(Axis(0))) + .and(main_ext_section.axis_iter(Axis(0))) + .and(deterministic_section.axis_iter_mut(Axis(0))) + .par_for_each(|_base_row, _ext_row, mut _det_ext_row| {{ + }}); + + // For dual-row constraints. + // The last row of the deterministic section for transition constraints is not used. + let mut deterministic_section = deterministic_section.slice_mut(s![..-1, ..]); + Zip::from(master_base_table.axis_windows(Axis(0), 2)) + .and(main_ext_section.axis_windows(Axis(0), 2)) + .and(deterministic_section.exact_chunks_mut((1, deterministic_section.ncols()))) + .par_for_each(|base_chunk, main_ext_chunk, mut det_ext_chunk| {{ + let _current_base_row = base_chunk.row(0); + let _next_base_row = base_chunk.row(1); + let _current_ext_row = main_ext_chunk.row(0); + let _next_ext_row = main_ext_chunk.row(1); + let mut _current_det_ext_row = det_ext_chunk.row_mut(0); + }}); + }} +}} +" + ) +} From c8fd1ee0039ca122ca019d7c6bd9afb79dbeb5ba Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 24 May 2023 12:28:26 +0200 Subject: [PATCH 44/72] treat empty substitution rules special to avoid division by 0 --- constraint-evaluation-generator/src/main.rs | 85 +++++++++++++++++++-- 1 file changed, 80 insertions(+), 5 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 57a40430..c5e24f42 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -645,10 +645,18 @@ fn generate_degree_lowering_table_code( .collect_vec() .join(",\n"); - // todo: generate code to compute the columns' content corresponding to the substitutions - for circuit in init_base_substitutions.iter() { - let _code = substitution_rule_to_code(circuit.circuit.as_ref().borrow().to_owned()); - } + let fill_base_columns_code = generate_fill_base_columns_code( + init_base_substitutions, + cons_base_substitutions, + tran_base_substitutions, + term_base_substitutions, + ); + let fill_ext_columns_code = generate_fill_ext_columns_code( + init_ext_substitutions, + cons_ext_substitutions, + tran_ext_substitutions, + term_ext_substitutions, + ); format!( " @@ -691,6 +699,41 @@ pub enum DegreeLoweringExtTableColumn {{ pub struct DegreeLoweringTable {{}} impl DegreeLoweringTable {{ + {fill_base_columns_code} + {fill_ext_columns_code} +}} +" + ) +} + +fn generate_fill_base_columns_code( + init_base_substitutions: &[ConstraintCircuitMonad], + cons_base_substitutions: &[ConstraintCircuitMonad], + tran_base_substitutions: &[ConstraintCircuitMonad], + term_base_substitutions: &[ConstraintCircuitMonad], +) -> String { + if init_base_substitutions.is_empty() + && cons_base_substitutions.is_empty() + && tran_base_substitutions.is_empty() + && term_base_substitutions.is_empty() + { + return "pub fn fill_deterministic_base_columns(_: &mut ArrayViewMut2) { + // prevent unused variable warning + let _ = NUM_BASE_COLUMNS; + // no substitutions + }" + .to_owned(); + } + + // todo: generate code to compute the columns' content corresponding to the substitutions + for circuit in init_base_substitutions.iter() { + let _code = substitution_rule_to_code(circuit.circuit.as_ref().borrow().to_owned()); + } + + let placeholder = ""; + + format!( + " pub fn fill_deterministic_base_columns(master_base_table: &mut ArrayViewMut2) {{ assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); @@ -708,6 +751,7 @@ impl DegreeLoweringTable {{ Zip::from(main_trace_section.axis_iter(Axis(0))) .and(deterministic_section.axis_iter_mut(Axis(0))) .par_for_each(|_base_row, mut _deterministic_row| {{ + {placeholder} }}); // For dual-row constraints. @@ -719,9 +763,39 @@ impl DegreeLoweringTable {{ let _current_base_row = main_trace_chunk.row(0); let _next_base_row = main_trace_chunk.row(1); let mut _current_deterministic_row = deterministic_chunk.row_mut(0); + {placeholder} }}); }} +" + ) +} + +fn generate_fill_ext_columns_code( + init_ext_substitutions: &[ConstraintCircuitMonad], + cons_ext_substitutions: &[ConstraintCircuitMonad], + tran_ext_substitutions: &[ConstraintCircuitMonad], + term_ext_substitutions: &[ConstraintCircuitMonad], +) -> String { + if init_ext_substitutions.is_empty() + && cons_ext_substitutions.is_empty() + && tran_ext_substitutions.is_empty() + && term_ext_substitutions.is_empty() + { + return "pub fn fill_deterministic_ext_columns( + _: ArrayView2, + _: &mut ArrayViewMut2, + ) { + // prevent unused variable warning + let _ = NUM_EXT_COLUMNS; + // no substitutions + }" + .to_owned(); + } + + let placeholder = ""; + format!( + " pub fn fill_deterministic_ext_columns( master_base_table: ArrayView2, master_ext_table: &mut ArrayViewMut2, @@ -744,6 +818,7 @@ impl DegreeLoweringTable {{ .and(main_ext_section.axis_iter(Axis(0))) .and(deterministic_section.axis_iter_mut(Axis(0))) .par_for_each(|_base_row, _ext_row, mut _det_ext_row| {{ + {placeholder} }}); // For dual-row constraints. @@ -758,9 +833,9 @@ impl DegreeLoweringTable {{ let _current_ext_row = main_ext_chunk.row(0); let _next_ext_row = main_ext_chunk.row(1); let mut _current_det_ext_row = det_ext_chunk.row_mut(0); + {placeholder} }}); }} -}} " ) } From 5e55c99c1b7315aef2498d9ddc672d3fb4a580ba Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 24 May 2023 13:05:46 +0200 Subject: [PATCH 45/72] improve debugging of too-high-degree quotients --- triton-vm/src/stark.rs | 67 +++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/triton-vm/src/stark.rs b/triton-vm/src/stark.rs index c1c1dadf..428fc4f7 100644 --- a/triton-vm/src/stark.rs +++ b/triton-vm/src/stark.rs @@ -1,5 +1,6 @@ use std::ops::Add; use std::ops::Mul; +use std::ops::MulAssign; use anyhow::bail; use anyhow::Result; @@ -15,26 +16,25 @@ use num_traits::One; use rayon::prelude::*; use serde::Deserialize; use serde::Serialize; +use triton_profiler::prof_itr0; +use triton_profiler::prof_start; +use triton_profiler::prof_stop; +use triton_profiler::triton_profiler::TritonProfiler; use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::mpolynomial::Degree; use twenty_first::shared_math::other::roundup_npo2; +use twenty_first::shared_math::polynomial::Polynomial; use twenty_first::shared_math::tip5::Tip5; use twenty_first::shared_math::traits::FiniteField; use twenty_first::shared_math::traits::Inverse; use twenty_first::shared_math::traits::ModPowU32; +use twenty_first::shared_math::x_field_element; use twenty_first::shared_math::x_field_element::XFieldElement; use twenty_first::util_types::algebraic_hasher::AlgebraicHasher; use twenty_first::util_types::merkle_tree::CpuParallel; use twenty_first::util_types::merkle_tree::MerkleTree; use twenty_first::util_types::merkle_tree_maker::MerkleTreeMaker; -use triton_profiler::prof_itr0; -use triton_profiler::prof_start; -use triton_profiler::prof_stop; -use triton_profiler::triton_profiler::TritonProfiler; -use twenty_first::shared_math::polynomial::Polynomial; -use twenty_first::shared_math::x_field_element; - use crate::arithmetic_domain::ArithmeticDomain; use crate::fri::Fri; use crate::proof::Claim; @@ -232,6 +232,26 @@ impl Stark { ); prof_stop!(maybe_profiler, "quotient codewords"); + #[cfg(debug_assertions)] + { + prof_start!(maybe_profiler, "debug degree check", "debug"); + println!(" -- checking degree of base columns --"); + Self::debug_check_degree( + base_quotient_domain_codewords.view(), + quotient_domain, + max_degree, + ); + println!(" -- checking degree of extension columns --"); + Self::debug_check_degree( + extension_quotient_domain_codewords.view(), + quotient_domain, + max_degree, + ); + println!(" -- checking degree of quotient columns --"); + Self::debug_check_degree(master_quotient_table.view(), quotient_domain, max_degree); + prof_stop!(maybe_profiler, "debug degree check"); + } + prof_start!(maybe_profiler, "linearly combine quotient codewords", "CC"); // Create quotient codeword. This is a part of the combination codeword. To reduce the // amount of hashing necessary, the quotient codeword is linearly summed instead of @@ -250,9 +270,6 @@ impl Stark { assert_eq!(quotient_domain.length, quotient_codeword.len()); prof_stop!(maybe_profiler, "linearly combine quotient codewords"); - #[cfg(debug_assertions)] - Self::debug_check_degree("ient_codeword.to_vec(), quotient_domain, max_degree); - prof_start!(maybe_profiler, "commit to quotient codeword"); prof_start!(maybe_profiler, "LDE", "LDE"); let quotient_interpolation_poly = quotient_domain.interpolate("ient_codeword.to_vec()); @@ -335,8 +352,6 @@ impl Stark { weighted_base_codewords.sum_axis(Axis(1)) + weighted_ext_codewords.sum_axis(Axis(1)); assert_eq!(quotient_domain.length, base_and_ext_codeword.len()); - #[cfg(debug_assertions)] - Self::debug_check_degree(&base_and_ext_codeword.to_vec(), quotient_domain, max_degree); prof_stop!(maybe_profiler, "base&ext: linear combination"); prof_start!(maybe_profiler, "DEEP"); @@ -554,21 +569,25 @@ impl Stark { } #[cfg(debug_assertions)] - fn debug_check_degree( - combination_codeword: &[XFieldElement], + fn debug_check_degree( + table: ArrayView2, quotient_domain: ArithmeticDomain, max_degree: Degree, - ) { + ) where + FF: FiniteField + MulAssign, + { let max_degree = max_degree as isize; - let degree = quotient_domain.interpolate(combination_codeword).degree(); - let maybe_excl_mark = match degree > max_degree { - true => "!", - false => " ", - }; - println!( - "{maybe_excl_mark} Combination codeword has degree {degree}. \ - Must be of maximal degree {max_degree}." - ); + for (col_idx, codeword) in table.columns().into_iter().enumerate() { + let degree = quotient_domain.interpolate(&codeword.to_vec()).degree(); + let maybe_excl_mark = match degree > max_degree { + true => "!", + false => " ", + }; + println!( + "{maybe_excl_mark} Codeword {col_idx:>3} has degree {degree:>5}. \ + Must be of maximal degree {max_degree:>5}." + ); + } } pub fn verify( From a131e73f50e25d779c64855083937a09b1fad020 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 24 May 2023 13:29:34 +0200 Subject: [PATCH 46/72] generate code to fill deterministic degree-lowering columns --- constraint-evaluation-generator/src/main.rs | 146 +++++++++++++------- 1 file changed, 98 insertions(+), 48 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index c5e24f42..9633144d 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -700,9 +700,9 @@ pub struct DegreeLoweringTable {{}} impl DegreeLoweringTable {{ {fill_base_columns_code} + {fill_ext_columns_code} -}} -" +}}" ) } @@ -725,12 +725,50 @@ fn generate_fill_base_columns_code( .to_owned(); } - // todo: generate code to compute the columns' content corresponding to the substitutions - for circuit in init_base_substitutions.iter() { - let _code = substitution_rule_to_code(circuit.circuit.as_ref().borrow().to_owned()); - } + let single_row_substitutions = init_base_substitutions + .iter() + .chain(cons_base_substitutions.iter()) + .chain(term_base_substitutions.iter()) + .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) + .collect_vec() + .join("\n"); + let single_row_substitutions = if single_row_substitutions.is_empty() { + "".to_owned() + } else { + format!( + " + // For single-row constraints. + Zip::from(main_trace_section.axis_iter(Axis(0))) + .and(deterministic_section.axis_iter_mut(Axis(0))) + .par_for_each(|base_row, mut deterministic_row| {{ + {single_row_substitutions} + }});" + ) + }; - let placeholder = ""; + let dual_row_substitutions = tran_base_substitutions + .iter() + .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) + .collect_vec() + .join("\n"); + let dual_row_substitutions = if dual_row_substitutions.is_empty() { + "".to_owned() + } else { + format!( + " + // For dual-row constraints. + // The last row of the deterministic section for transition constraints is not used. + let mut deterministic_section = deterministic_section.slice_mut(s![..-1, ..]); + Zip::from(main_trace_section.axis_windows(Axis(0), 2)) + .and(deterministic_section.exact_chunks_mut((1, deterministic_section.ncols()))) + .par_for_each(|main_trace_chunk, mut deterministic_chunk| {{ + let current_base_row = main_trace_chunk.row(0); + let next_base_row = main_trace_chunk.row(1); + let mut deterministic_row = deterministic_chunk.row_mut(0); + {dual_row_substitutions} + }});" + ) + }; format!( " @@ -747,26 +785,10 @@ fn generate_fill_base_columns_code( s![.., deterministic_section_start..deterministic_section_end], )); - // For single-row constraints. - Zip::from(main_trace_section.axis_iter(Axis(0))) - .and(deterministic_section.axis_iter_mut(Axis(0))) - .par_for_each(|_base_row, mut _deterministic_row| {{ - {placeholder} - }}); + {single_row_substitutions} - // For dual-row constraints. - // The last row of the deterministic section for transition constraints is not used. - let mut deterministic_section = deterministic_section.slice_mut(s![..-1, ..]); - Zip::from(main_trace_section.axis_windows(Axis(0), 2)) - .and(deterministic_section.exact_chunks_mut((1, deterministic_section.ncols()))) - .par_for_each(|main_trace_chunk, mut deterministic_chunk| {{ - let _current_base_row = main_trace_chunk.row(0); - let _next_base_row = main_trace_chunk.row(1); - let mut _current_deterministic_row = deterministic_chunk.row_mut(0); - {placeholder} - }}); - }} -" + {dual_row_substitutions} + }}" ) } @@ -792,7 +814,54 @@ fn generate_fill_ext_columns_code( .to_owned(); } - let placeholder = ""; + let single_row_substitutions = init_ext_substitutions + .iter() + .chain(cons_ext_substitutions.iter()) + .chain(term_ext_substitutions.iter()) + .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) + .collect_vec() + .join("\n"); + let single_row_substitutions = if single_row_substitutions.is_empty() { + "".to_owned() + } else { + format!( + " + // For single-row constraints. + Zip::from(master_base_table.axis_iter(Axis(0))) + .and(main_ext_section.axis_iter(Axis(0))) + .and(deterministic_section.axis_iter_mut(Axis(0))) + .par_for_each(|base_row, ext_row, mut deterministic_row| {{ + {single_row_substitutions} + }});" + ) + }; + + let dual_row_substitutions = tran_ext_substitutions + .iter() + .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) + .collect_vec() + .join("\n"); + let dual_row_substitutions = if dual_row_substitutions.is_empty() { + "".to_owned() + } else { + format!( + " + // For dual-row constraints. + // The last row of the deterministic section for transition constraints is not used. + let mut deterministic_section = deterministic_section.slice_mut(s![..-1, ..]); + Zip::from(master_base_table.axis_windows(Axis(0), 2)) + .and(main_ext_section.axis_windows(Axis(0), 2)) + .and(deterministic_section.exact_chunks_mut((1, deterministic_section.ncols()))) + .par_for_each(|base_chunk, main_ext_chunk, mut det_ext_chunk| {{ + let current_base_row = base_chunk.row(0); + let next_base_row = base_chunk.row(1); + let current_ext_row = main_ext_chunk.row(0); + let next_ext_row = main_ext_chunk.row(1); + let mut deterministic_row = det_ext_chunk.row_mut(0); + {dual_row_substitutions} + }});" + ) + }; format!( " @@ -813,28 +882,9 @@ fn generate_fill_ext_columns_code( s![.., det_ext_section_start..det_ext_section_end], )); - // For single-row constraints. - Zip::from(master_base_table.axis_iter(Axis(0))) - .and(main_ext_section.axis_iter(Axis(0))) - .and(deterministic_section.axis_iter_mut(Axis(0))) - .par_for_each(|_base_row, _ext_row, mut _det_ext_row| {{ - {placeholder} - }}); + {single_row_substitutions} - // For dual-row constraints. - // The last row of the deterministic section for transition constraints is not used. - let mut deterministic_section = deterministic_section.slice_mut(s![..-1, ..]); - Zip::from(master_base_table.axis_windows(Axis(0), 2)) - .and(main_ext_section.axis_windows(Axis(0), 2)) - .and(deterministic_section.exact_chunks_mut((1, deterministic_section.ncols()))) - .par_for_each(|base_chunk, main_ext_chunk, mut det_ext_chunk| {{ - let _current_base_row = base_chunk.row(0); - let _next_base_row = base_chunk.row(1); - let _current_ext_row = main_ext_chunk.row(0); - let _next_ext_row = main_ext_chunk.row(1); - let mut _current_det_ext_row = det_ext_chunk.row_mut(0); - {placeholder} - }}); + {dual_row_substitutions} }} " ) From 4c4b3dc5f2565144308883de1112f422902cfe4f Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 24 May 2023 14:30:09 +0200 Subject: [PATCH 47/72] add clippy check to code generator --- constraint-evaluation-generator/src/main.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 9633144d..15941cb5 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -161,8 +161,18 @@ fn main() { std::fs::write("triton-vm/src/table/constraints.rs", constraint_code) .expect("Writing to disk has failed."); - if let Err(fmt_failed) = Command::new("cargo").arg("fmt").output() { - println!("cargo fmt failed: {fmt_failed}"); + match Command::new("cargo") + .arg("clippy") + .arg("--workspace") + .arg("--all-targets") + .output() + { + Ok(_) => (), + Err(err) => panic!("cargo clippy failed: {err}"), + } + match Command::new("cargo").arg("fmt").output() { + Ok(_) => (), + Err(err) => panic!("cargo fmt failed: {err}"), } } From b0703ab84aa4a0e13774cba60f477626781f28de Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 24 May 2023 14:45:20 +0200 Subject: [PATCH 48/72] add degree-lowering constraints to generated code --- constraint-evaluation-generator/src/main.rs | 27 +++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 15941cb5..5efee75f 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -142,13 +142,36 @@ fn main() { &term_ext_substitutions, ); + let initial_constraints = vec![ + initial_constraints, + init_base_substitutions, + init_ext_substitutions, + ] + .concat(); + let consistency_constraints = vec![ + consistency_constraints, + cons_base_substitutions, + cons_ext_substitutions, + ] + .concat(); + let transition_constraints = vec![ + transition_constraints, + tran_base_substitutions, + tran_ext_substitutions, + ] + .concat(); + let terminal_constraints = vec![ + terminal_constraints, + term_base_substitutions, + term_ext_substitutions, + ] + .concat(); + let mut initial_constraints = consume(initial_constraints); let mut consistency_constraints = consume(consistency_constraints); let mut transition_constraints = consume(transition_constraints); let mut terminal_constraints = consume(terminal_constraints); - // todo: add substitution rules to the constraints - let constraint_code = generate_constraint_code( &mut initial_constraints, &mut consistency_constraints, From 6d96ad5b7b90a0cca29024852c76e09876c30000 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 25 May 2023 09:41:55 +0200 Subject: [PATCH 49/72] use `TokenStream`s to generate constraint code --- constraint-evaluation-generator/Cargo.toml | 2 + constraint-evaluation-generator/src/main.rs | 481 ++++++++++---------- triton-vm/Cargo.toml | 2 + triton-vm/src/table/constraint_circuit.rs | 38 +- 4 files changed, 282 insertions(+), 241 deletions(-) diff --git a/constraint-evaluation-generator/Cargo.toml b/constraint-evaluation-generator/Cargo.toml index 69791e37..cb4a77cf 100644 --- a/constraint-evaluation-generator/Cargo.toml +++ b/constraint-evaluation-generator/Cargo.toml @@ -26,3 +26,5 @@ default-features = false twenty-first = "0.21.1" triton-vm = { path = "../triton-vm" } itertools = "0.10" +quote = "1.0" +proc-macro2 = "1.0" diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 5efee75f..89c23cc3 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -2,9 +2,12 @@ use std::collections::HashSet; use std::process::Command; use itertools::Itertools; +use quote::format_ident; +use quote::quote; use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::x_field_element::XFieldElement; +use proc_macro2::TokenStream; use triton_vm::table::cascade_table::ExtCascadeTable; use triton_vm::table::constraint_circuit::BinOp; use triton_vm::table::constraint_circuit::CircuitExpression; @@ -181,8 +184,11 @@ fn main() { std::fs::write("triton-vm/src/table/degree_lowering_table.rs", table_code) .expect("Writing to disk has failed."); - std::fs::write("triton-vm/src/table/constraints.rs", constraint_code) - .expect("Writing to disk has failed."); + std::fs::write( + "triton-vm/src/table/constraints.rs", + constraint_code.to_string(), + ) + .expect("Writing to disk has failed."); match Command::new("cargo") .arg("clippy") @@ -207,212 +213,198 @@ fn consume( } fn generate_constraint_code( - initial_constraint_circuits: &mut [ConstraintCircuit], - consistency_constraint_circuits: &mut [ConstraintCircuit], - transition_constraint_circuits: &mut [ConstraintCircuit], - terminal_constraint_circuits: &mut [ConstraintCircuit], -) -> String { - let num_initial_constraints = initial_constraint_circuits.len(); - let num_consistency_constraints = consistency_constraint_circuits.len(); - let num_transition_constraints = transition_constraint_circuits.len(); - let num_terminal_constraints = terminal_constraint_circuits.len(); - - let ( - initial_constraint_degrees, - initial_constraint_strings_bfe, - initial_constraint_strings_xfe, - ) = turn_circuits_into_string(initial_constraint_circuits); - let ( - consistency_constraint_degrees, - consistency_constraint_strings_bfe, - consistency_constraint_strings_xfe, - ) = turn_circuits_into_string(consistency_constraint_circuits); - let ( - transition_constraint_degrees, - transition_constraint_strings_bfe, - transition_constraint_strings_xfe, - ) = turn_circuits_into_string(transition_constraint_circuits); - let ( - terminal_constraint_degrees, - terminal_constraint_strings_bfe, - terminal_constraint_strings_xfe, - ) = turn_circuits_into_string(terminal_constraint_circuits); - - format!( - " -use ndarray::ArrayView1; -use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::mpolynomial::Degree; -use twenty_first::shared_math::x_field_element::XFieldElement; - -use crate::table::challenges::Challenges; -use crate::table::challenges::ChallengeId::*; -use crate::table::extension_table::Evaluable; -use crate::table::extension_table::Quotientable; -use crate::table::master_table::MasterExtTable; - -// This file has been auto-generated. Any modifications _will_ be lost. -// To re-generate, execute: -// `cargo run --bin constraint-evaluation-generator` -impl Evaluable for MasterExtTable {{ - #[inline] - #[allow(unused_variables)] - fn evaluate_initial_constraints( - base_row: ArrayView1, - ext_row: ArrayView1, - challenges: &Challenges, - ) -> Vec {{ - {initial_constraint_strings_bfe} - }} + init_constraint_circuits: &mut [ConstraintCircuit], + cons_constraint_circuits: &mut [ConstraintCircuit], + tran_constraint_circuits: &mut [ConstraintCircuit], + term_constraint_circuits: &mut [ConstraintCircuit], +) -> TokenStream { + let num_init_constraints = init_constraint_circuits.len(); + let num_cons_constraints = cons_constraint_circuits.len(); + let num_tran_constraints = tran_constraint_circuits.len(); + let num_term_constraints = term_constraint_circuits.len(); + + let (init_constraint_degrees, init_constraints_bfe, init_constraints_xfe) = + tokenize_circuits(init_constraint_circuits); + let (cons_constraint_degrees, cons_constraints_bfe, cons_constraints_xfe) = + tokenize_circuits(cons_constraint_circuits); + let (tran_constraint_degrees, tran_constraints_bfe, tran_constraints_xfe) = + tokenize_circuits(tran_constraint_circuits); + let (term_constraint_degrees, term_constraints_bfe, term_constraints_xfe) = + tokenize_circuits(term_constraint_circuits); + + quote!( + use ndarray::ArrayView1; + use twenty_first::shared_math::b_field_element::BFieldElement; + use twenty_first::shared_math::mpolynomial::Degree; + use twenty_first::shared_math::x_field_element::XFieldElement; + + use crate::table::challenges::Challenges; + use crate::table::challenges::ChallengeId::*; + use crate::table::extension_table::Evaluable; + use crate::table::extension_table::Quotientable; + use crate::table::master_table::MasterExtTable; + + // This file has been auto-generated. Any modifications _will_ be lost. + // To re-generate, execute: + // `cargo run --bin constraint-evaluation-generator` + impl Evaluable for MasterExtTable { + #[inline] + #[allow(unused_variables)] + fn evaluate_initial_constraints( + base_row: ArrayView1, + ext_row: ArrayView1, + challenges: &Challenges, + ) -> Vec { + #init_constraints_bfe + } - #[inline] - #[allow(unused_variables)] - fn evaluate_consistency_constraints( - base_row: ArrayView1, - ext_row: ArrayView1, - challenges: &Challenges, - ) -> Vec {{ - {consistency_constraint_strings_bfe} - }} + #[inline] + #[allow(unused_variables)] + fn evaluate_consistency_constraints( + base_row: ArrayView1, + ext_row: ArrayView1, + challenges: &Challenges, + ) -> Vec { + #cons_constraints_bfe + } - #[inline] - #[allow(unused_variables)] - fn evaluate_transition_constraints( - current_base_row: ArrayView1, - current_ext_row: ArrayView1, - next_base_row: ArrayView1, - next_ext_row: ArrayView1, - challenges: &Challenges, - ) -> Vec {{ - {transition_constraint_strings_bfe} - }} + #[inline] + #[allow(unused_variables)] + fn evaluate_transition_constraints( + current_base_row: ArrayView1, + current_ext_row: ArrayView1, + next_base_row: ArrayView1, + next_ext_row: ArrayView1, + challenges: &Challenges, + ) -> Vec { + #tran_constraints_bfe + } - #[inline] - #[allow(unused_variables)] - fn evaluate_terminal_constraints( - base_row: ArrayView1, - ext_row: ArrayView1, - challenges: &Challenges, - ) -> Vec {{ - {terminal_constraint_strings_bfe} - }} -}} + #[inline] + #[allow(unused_variables)] + fn evaluate_terminal_constraints( + base_row: ArrayView1, + ext_row: ArrayView1, + challenges: &Challenges, + ) -> Vec { + #term_constraints_bfe + } + } -impl Evaluable for MasterExtTable {{ - #[inline] - #[allow(unused_variables)] - fn evaluate_initial_constraints( - base_row: ArrayView1, - ext_row: ArrayView1, - challenges: &Challenges, - ) -> Vec {{ - {initial_constraint_strings_xfe} - }} + impl Evaluable for MasterExtTable { + #[inline] + #[allow(unused_variables)] + fn evaluate_initial_constraints( + base_row: ArrayView1, + ext_row: ArrayView1, + challenges: &Challenges, + ) -> Vec { + #init_constraints_xfe + } - #[inline] - #[allow(unused_variables)] - fn evaluate_consistency_constraints( - base_row: ArrayView1, - ext_row: ArrayView1, - challenges: &Challenges, - ) -> Vec {{ - {consistency_constraint_strings_xfe} - }} + #[inline] + #[allow(unused_variables)] + fn evaluate_consistency_constraints( + base_row: ArrayView1, + ext_row: ArrayView1, + challenges: &Challenges, + ) -> Vec { + #cons_constraints_xfe + } - #[inline] - #[allow(unused_variables)] - fn evaluate_transition_constraints( - current_base_row: ArrayView1, - current_ext_row: ArrayView1, - next_base_row: ArrayView1, - next_ext_row: ArrayView1, - challenges: &Challenges, - ) -> Vec {{ - {transition_constraint_strings_xfe} - }} + #[inline] + #[allow(unused_variables)] + fn evaluate_transition_constraints( + current_base_row: ArrayView1, + current_ext_row: ArrayView1, + next_base_row: ArrayView1, + next_ext_row: ArrayView1, + challenges: &Challenges, + ) -> Vec { + #tran_constraints_xfe + } - #[inline] - #[allow(unused_variables)] - fn evaluate_terminal_constraints( - base_row: ArrayView1, - ext_row: ArrayView1, - challenges: &Challenges, - ) -> Vec {{ - {terminal_constraint_strings_xfe} - }} -}} + #[inline] + #[allow(unused_variables)] + fn evaluate_terminal_constraints( + base_row: ArrayView1, + ext_row: ArrayView1, + challenges: &Challenges, + ) -> Vec { + #term_constraints_xfe + } + } -impl Quotientable for MasterExtTable {{ - fn num_initial_quotients() -> usize {{ - {num_initial_constraints} - }} + impl Quotientable for MasterExtTable { + fn num_initial_quotients() -> usize { + #num_init_constraints + } - fn num_consistency_quotients() -> usize {{ - {num_consistency_constraints} - }} + fn num_consistency_quotients() -> usize { + #num_cons_constraints + } - fn num_transition_quotients() -> usize {{ - {num_transition_constraints} - }} + fn num_transition_quotients() -> usize { + #num_tran_constraints + } - fn num_terminal_quotients() -> usize {{ - {num_terminal_constraints} - }} + fn num_terminal_quotients() -> usize { + #num_term_constraints + } - #[allow(unused_variables)] - fn initial_quotient_degree_bounds( - interpolant_degree: Degree, - ) -> Vec {{ - let zerofier_degree = 1; - [{initial_constraint_degrees}].to_vec() - }} + #[allow(unused_variables)] + fn initial_quotient_degree_bounds( + interpolant_degree: Degree, + ) -> Vec { + let zerofier_degree = 1; + [#init_constraint_degrees].to_vec() + } - #[allow(unused_variables)] - fn consistency_quotient_degree_bounds( - interpolant_degree: Degree, - padded_height: usize, - ) -> Vec {{ - let zerofier_degree = padded_height as Degree; - [{consistency_constraint_degrees}].to_vec() - }} + #[allow(unused_variables)] + fn consistency_quotient_degree_bounds( + interpolant_degree: Degree, + padded_height: usize, + ) -> Vec { + let zerofier_degree = padded_height as Degree; + [#cons_constraint_degrees].to_vec() + } - #[allow(unused_variables)] - fn transition_quotient_degree_bounds( - interpolant_degree: Degree, - padded_height: usize, - ) -> Vec {{ - let zerofier_degree = padded_height as Degree - 1; - [{transition_constraint_degrees}].to_vec() - }} + #[allow(unused_variables)] + fn transition_quotient_degree_bounds( + interpolant_degree: Degree, + padded_height: usize, + ) -> Vec { + let zerofier_degree = padded_height as Degree - 1; + [#tran_constraint_degrees].to_vec() + } - #[allow(unused_variables)] - fn terminal_quotient_degree_bounds( - interpolant_degree: Degree, - ) -> Vec {{ - let zerofier_degree = 1; - [{terminal_constraint_degrees}].to_vec() - }} -}} -" + #[allow(unused_variables)] + fn terminal_quotient_degree_bounds( + interpolant_degree: Degree, + ) -> Vec { + let zerofier_degree = 1; + [#term_constraint_degrees].to_vec() + } + } ) } -/// Given a slice of constraint circuits, return a tuple of strings corresponding to code +/// Given a slice of constraint circuits, return a tuple of [`TokenStream`]s corresponding to code /// evaluating these constraints as well as their degrees. In particular: -/// 1. The first string contains code that, when evaluated, produces the constraints' degrees, -/// 1. the second string contains code that, when evaluated, produces the constraints' values, with +/// 1. The first stream contains code that, when evaluated, produces the constraints' degrees, +/// 1. the second stream contains code that, when evaluated, produces the constraints' values, with /// the input type for the base row being `BFieldElement`, and -/// 1. the third string is like the second string, except that the input type for the base row is +/// 1. the third stream is like the second, except that the input type for the base row is /// `XFieldElement`. -fn turn_circuits_into_string( +fn tokenize_circuits( constraint_circuits: &mut [ConstraintCircuit], -) -> (String, String, String) { +) -> (TokenStream, TokenStream, TokenStream) { if constraint_circuits.is_empty() { - return ("".to_string(), "vec![]".to_string(), "vec![]".to_string()); + return (quote!(), quote!(vec![]), quote!(vec![])); } // Sanity check: all node IDs must be unique. - // This also ounts the number of times each node is referenced. + // This also counts the number of times each node is referenced. ConstraintCircuit::assert_has_unique_ids(constraint_circuits); // Get all unique reference counts. @@ -431,8 +423,7 @@ fn turn_circuits_into_string( .filter(|&x| x > 1) .rev() .map(|visit_count| declare_nodes_with_visit_count(visit_count, constraint_circuits)) - .collect_vec() - .join(""); + .collect_vec(); let (base_constraints, ext_constraints): (Vec<_>, Vec<_>) = constraint_circuits .iter() @@ -441,57 +432,56 @@ fn turn_circuits_into_string( // The order of the constraints' degrees must match the order of the constraints. // Hence, listing the degrees is only possible after the partition into base and extension // constraints is known. - let degree_bounds_string = base_constraints + let tokenized_degree_bounds = base_constraints .iter() .chain(ext_constraints.iter()) .map(|circuit| match circuit.degree() { - d if d > 1 => format!("interpolant_degree * {d} - zerofier_degree"), - d if d == 1 => "interpolant_degree - zerofier_degree".to_string(), + d if d > 1 => quote!(interpolant_degree * #d - zerofier_degree), + d if d == 1 => quote!(interpolant_degree - zerofier_degree), _ => unreachable!("Constraint degree must be positive"), }) - .join(",\n"); + .collect_vec(); + let tokenized_degree_bounds = quote!(#(#tokenized_degree_bounds),*); - let build_constraint_evaluation_code = |constraints: &[&ConstraintCircuit]| { + let tokenize_constraint_evaluation = |constraints: &[&ConstraintCircuit]| { constraints .iter() .map(|constraint| evaluate_single_node(1, constraint, &HashSet::default())) .collect_vec() - .join(",\n") }; - let base_constraint_strings = build_constraint_evaluation_code(&base_constraints); - let ext_constraint_strings = build_constraint_evaluation_code(&ext_constraints); + let tokenized_base_constraints = tokenize_constraint_evaluation(&base_constraints); + let tokenized_ext_constraints = tokenize_constraint_evaluation(&ext_constraints); // If there are no base constraints, the type needs to be explicitly declared. - let base_constraint_bfe_type = match base_constraints.is_empty() { - true => ": [BFieldElement; 0]", - false => "", + let tokenized_bfe_base_constraints = match base_constraints.is_empty() { + true => quote!(let base_constraints: [BFieldElement; 0] = []), + false => quote!(let base_constraints = [#(#tokenized_base_constraints),*]), }; - - let constraint_string_bfe = format!( - "{shared_declarations} - let base_constraints{base_constraint_bfe_type} = [{base_constraint_strings}]; - let ext_constraints = [{ext_constraint_strings}]; + let tokenized_bfe_constraints = quote!( + #(#shared_declarations)* + #tokenized_bfe_base_constraints; + let ext_constraints = [#(#tokenized_ext_constraints),*]; base_constraints .into_iter() .map(|bfe| bfe.lift()) .chain(ext_constraints.into_iter()) - .collect()" + .collect() ); - let constraint_string_xfe = format!( - "{shared_declarations} - let base_constraints = [{base_constraint_strings}]; - let ext_constraints = [{ext_constraint_strings}]; + let tokenized_xfe_constraints = quote!( + #(#shared_declarations)* + let base_constraints = [#(#tokenized_base_constraints),*]; + let ext_constraints = [#(#tokenized_ext_constraints),*]; base_constraints .into_iter() .chain(ext_constraints.into_iter()) - .collect()" + .collect() ); ( - degree_bounds_string, - constraint_string_bfe, - constraint_string_xfe, + tokenized_degree_bounds, + tokenized_bfe_constraints, + tokenized_xfe_constraints, ) } @@ -501,37 +491,37 @@ fn turn_circuits_into_string( fn declare_nodes_with_visit_count( requested_visited_count: usize, circuits: &[ConstraintCircuit], -) -> String { +) -> TokenStream { let mut scope: HashSet = HashSet::new(); - circuits + let tokenized_circuits = circuits .iter() - .map(|circuit| { + .filter_map(|circuit| { declare_single_node_with_visit_count(circuit, requested_visited_count, &mut scope) }) - .collect_vec() - .join("") + .collect_vec(); + quote!(#(#tokenized_circuits)*) } fn declare_single_node_with_visit_count( circuit: &ConstraintCircuit, requested_visited_count: usize, scope: &mut HashSet, -) -> String { +) -> Option { // Don't declare a node twice. if scope.contains(&circuit.id) { - return String::default(); + return None; } // A higher-than-requested visit counter means the node is already in global scope, albeit not // necessarily in the passed-in scope. if circuit.visited_counter > requested_visited_count { - return String::default(); + return None; } let CircuitExpression::BinaryOperation(_, lhs, rhs) = &circuit.expression else { // Constants are already (or can be) trivially declared. - return String::default(); + return None; }; // If the visited counter is not yet exact, start recursing on the BinaryOperation's children. @@ -546,7 +536,12 @@ fn declare_single_node_with_visit_count( requested_visited_count, scope, ); - return [out_left, out_right].join(""); + return match (out_left, out_right) { + (None, None) => None, + (Some(l), None) => Some(l), + (None, Some(r)) => Some(r), + (Some(l), Some(r)) => Some(quote!(#l #r)), + }; } // Declare a new binding. @@ -557,32 +552,37 @@ fn declare_single_node_with_visit_count( let is_new_insertion = scope.insert(circuit.id); assert!(is_new_insertion); - format!("let {binding_name} = {evaluation};\n") + Some(quote!(let #binding_name = #evaluation;)) } /// Return a variable name for the node. Returns `point[n]` if node is just /// a value from the codewords. Otherwise returns the ID of the circuit. -fn get_binding_name(circuit: &ConstraintCircuit) -> String { +fn get_binding_name(circuit: &ConstraintCircuit) -> TokenStream { match &circuit.expression { - CircuitExpression::BConstant(bfe) => print_bfe(bfe), - CircuitExpression::XConstant(xfe) => print_xfe(xfe), - CircuitExpression::Input(idx) => idx.to_string(), - CircuitExpression::Challenge(challenge_id) => { - format!("challenges.get_challenge({challenge_id})") + CircuitExpression::BConstant(bfe) => tokenize_bfe(bfe), + CircuitExpression::XConstant(xfe) => tokenize_xfe(xfe), + CircuitExpression::Input(idx) => quote!(#idx), + CircuitExpression::Challenge(challenge) => { + let challenge_ident = format_ident!("{challenge}"); + quote!(challenges.get_challenge(#challenge_ident)) + } + CircuitExpression::BinaryOperation(_, _, _) => { + let node_ident = format_ident!("node_{}", circuit.id); + quote!(#node_ident) } - CircuitExpression::BinaryOperation(_, _, _) => format!("node_{}", circuit.id), } } -fn print_bfe(bfe: &BFieldElement) -> String { - format!("BFieldElement::from_raw_u64({})", bfe.raw_u64()) +fn tokenize_bfe(bfe: &BFieldElement) -> TokenStream { + let raw_u64 = bfe.raw_u64(); + quote!(BFieldElement::from_raw_u64(#raw_u64)) } -fn print_xfe(xfe: &XFieldElement) -> String { - let coeff_0 = print_bfe(&xfe.coefficients[0]); - let coeff_1 = print_bfe(&xfe.coefficients[1]); - let coeff_2 = print_bfe(&xfe.coefficients[2]); - format!("XFieldElement::new([{coeff_0}, {coeff_1}, {coeff_2}])") +fn tokenize_xfe(xfe: &XFieldElement) -> TokenStream { + let coeff_0 = tokenize_bfe(&xfe.coefficients[0]); + let coeff_1 = tokenize_bfe(&xfe.coefficients[1]); + let coeff_2 = tokenize_bfe(&xfe.coefficients[2]); + quote!(XFieldElement::new([#coeff_0, #coeff_1, #coeff_2])) } /// Recursively construct the code for evaluating a single node. @@ -590,7 +590,7 @@ fn evaluate_single_node( requested_visited_count: usize, circuit: &ConstraintCircuit, scope: &HashSet, -) -> String { +) -> TokenStream { let binding_name = get_binding_name(circuit); // Don't declare a node twice. @@ -612,8 +612,7 @@ fn evaluate_single_node( let rhs = rhs.as_ref().borrow(); let evaluated_lhs = evaluate_single_node(requested_visited_count, &lhs, scope); let evaluated_rhs = evaluate_single_node(requested_visited_count, &rhs, scope); - let operation = binop.to_string(); - format!("({evaluated_lhs}) {operation} ({evaluated_rhs})") + quote!((#evaluated_lhs) #binop (#evaluated_rhs)) } /// Given a substitution rule, i.e., a `ConstraintCircuit` of the form `x - expr`, generate code @@ -836,15 +835,17 @@ fn generate_fill_ext_columns_code( && tran_ext_substitutions.is_empty() && term_ext_substitutions.is_empty() { - return "pub fn fill_deterministic_ext_columns( + return quote!( + pub fn fill_deterministic_ext_columns( _: ArrayView2, _: &mut ArrayViewMut2, ) { // prevent unused variable warning let _ = NUM_EXT_COLUMNS; // no substitutions - }" - .to_owned(); + } + ) + .to_string(); } let single_row_substitutions = init_ext_substitutions diff --git a/triton-vm/Cargo.toml b/triton-vm/Cargo.toml index a4401442..79068efd 100644 --- a/triton-vm/Cargo.toml +++ b/triton-vm/Cargo.toml @@ -44,6 +44,8 @@ serde = { version = "1.0", features = ["derive"] } strum = "0.24" strum_macros = "0.24" ndarray = { version = "0.15", features = ["rayon"] } +quote = "1.0" +proc-macro2 = "1.0" [[bench]] name = "prove_halt" diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index e134961f..e5eacac5 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -27,6 +27,8 @@ use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::mpolynomial::Degree; use twenty_first::shared_math::x_field_element::XFieldElement; +use quote::quote; +use quote::ToTokens; use CircuitExpression::*; use crate::table::challenges::ChallengeId; @@ -49,6 +51,16 @@ impl Display for BinOp { } } +impl ToTokens for BinOp { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + match self { + BinOp::Add => tokens.extend(quote!(+)), + BinOp::Sub => tokens.extend(quote!(-)), + BinOp::Mul => tokens.extend(quote!(*)), + } + } +} + impl BinOp { pub fn operation(&self, lhs: L, rhs: R) -> O where @@ -73,7 +85,9 @@ impl BinOp { /// a uniform interface for accessing the index. /// /// Having `Clone + Copy + Hash + PartialEq + Eq` helps putting `InputIndicator`s into containers. -pub trait InputIndicator: Debug + Clone + Copy + Hash + PartialEq + Eq + Display { +pub trait InputIndicator: + Debug + Clone + Copy + Hash + PartialEq + Eq + Display + ToTokens +{ /// `true` iff `self` refers to a column in the base table. fn is_base_table_column(&self) -> bool; @@ -110,6 +124,16 @@ impl Display for SingleRowIndicator { } } +impl ToTokens for SingleRowIndicator { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + use SingleRowIndicator::*; + match self { + BaseRow(i) => tokens.extend(quote!(base_row[#i])), + ExtRow(i) => tokens.extend(quote!(ext_row[#i])), + } + } +} + impl InputIndicator for SingleRowIndicator { fn is_base_table_column(&self) -> bool { use SingleRowIndicator::*; @@ -177,6 +201,18 @@ impl Display for DualRowIndicator { } } +impl ToTokens for DualRowIndicator { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + use DualRowIndicator::*; + match self { + CurrentBaseRow(i) => tokens.extend(quote!(current_base_row[#i])), + CurrentExtRow(i) => tokens.extend(quote!(current_ext_row[#i])), + NextBaseRow(i) => tokens.extend(quote!(next_base_row[#i])), + NextExtRow(i) => tokens.extend(quote!(next_ext_row[#i])), + } + } +} + impl InputIndicator for DualRowIndicator { fn is_base_table_column(&self) -> bool { use DualRowIndicator::*; From 7da19dadbfc0d82fcecf7568d8fc415f55b838a9 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 25 May 2023 11:33:27 +0200 Subject: [PATCH 50/72] use `prettyplease` instead of `rustfmt` to format generated code --- constraint-evaluation-generator/Cargo.toml | 2 ++ constraint-evaluation-generator/src/main.rs | 26 +++++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/constraint-evaluation-generator/Cargo.toml b/constraint-evaluation-generator/Cargo.toml index cb4a77cf..c1fe5779 100644 --- a/constraint-evaluation-generator/Cargo.toml +++ b/constraint-evaluation-generator/Cargo.toml @@ -26,5 +26,7 @@ default-features = false twenty-first = "0.21.1" triton-vm = { path = "../triton-vm" } itertools = "0.10" +syn = "2.0" quote = "1.0" proc-macro2 = "1.0" +prettyplease = "0.2" diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 89c23cc3..41c9123f 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -2,12 +2,12 @@ use std::collections::HashSet; use std::process::Command; use itertools::Itertools; +use proc_macro2::TokenStream; use quote::format_ident; use quote::quote; use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::x_field_element::XFieldElement; -use proc_macro2::TokenStream; use triton_vm::table::cascade_table::ExtCascadeTable; use triton_vm::table::constraint_circuit::BinOp; use triton_vm::table::constraint_circuit::CircuitExpression; @@ -182,13 +182,19 @@ fn main() { &mut terminal_constraints, ); - std::fs::write("triton-vm/src/table/degree_lowering_table.rs", table_code) - .expect("Writing to disk has failed."); - std::fs::write( - "triton-vm/src/table/constraints.rs", - constraint_code.to_string(), - ) - .expect("Writing to disk has failed."); + let table_syntax_tree = syn::parse_str(&table_code).unwrap(); + let table_code = prettyplease::unparse(&table_syntax_tree); + match std::fs::write("triton-vm/src/table/degree_lowering_table.rs", table_code) { + Ok(_) => (), + Err(err) => panic!("Writing to disk has failed: {err}"), + } + + let constraint_syntax_tree = syn::parse2(constraint_code).unwrap(); + let constraint_code = prettyplease::unparse(&constraint_syntax_tree); + match std::fs::write("triton-vm/src/table/constraints.rs", constraint_code) { + Ok(_) => (), + Err(err) => panic!("Writing to disk has failed: {err}"), + } match Command::new("cargo") .arg("clippy") @@ -199,10 +205,6 @@ fn main() { Ok(_) => (), Err(err) => panic!("cargo clippy failed: {err}"), } - match Command::new("cargo").arg("fmt").output() { - Ok(_) => (), - Err(err) => panic!("cargo fmt failed: {err}"), - } } /// Consumes every `ConstraintCircuitMonad`, returning their corresponding `ConstraintCircuit`s. From 289ccc122a59415a213d6aa97f3868412d634c89 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 25 May 2023 12:29:13 +0200 Subject: [PATCH 51/72] use `TokenStream`s to generate degree-lowering code --- constraint-evaluation-generator/src/main.rs | 271 +++++++++----------- 1 file changed, 128 insertions(+), 143 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 41c9123f..947a640e 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -182,7 +182,7 @@ fn main() { &mut terminal_constraints, ); - let table_syntax_tree = syn::parse_str(&table_code).unwrap(); + let table_syntax_tree = syn::parse2(table_code).unwrap(); let table_code = prettyplease::unparse(&table_syntax_tree); match std::fs::write("triton-vm/src/table/degree_lowering_table.rs", table_code) { Ok(_) => (), @@ -619,7 +619,7 @@ fn evaluate_single_node( /// Given a substitution rule, i.e., a `ConstraintCircuit` of the form `x - expr`, generate code /// that evaluates `expr` on the appropriate base rows and sets `x` to the result. -fn substitution_rule_to_code(circuit: ConstraintCircuit) -> String { +fn substitution_rule_to_code(circuit: ConstraintCircuit) -> TokenStream { let circuit_evaluates_to_base_element = circuit.evaluates_to_base_element(); let BinaryOperation(BinOp::Sub, new_var, expr) = circuit.expression else { panic!("Substitution rule must be a subtraction."); @@ -635,7 +635,7 @@ fn substitution_rule_to_code(circuit: ConstraintCircuit) let expr = expr.as_ref().borrow().to_owned(); let expr = evaluate_single_node(usize::MAX, &expr, &HashSet::new()); - format!("deterministic_row[{new_var_idx} - deterministic_section_start] = {expr};") + quote!(deterministic_row[#new_var_idx - deterministic_section_start] = #expr;) } /// Given all substitution rules, generate the code that evaluates them in order. @@ -650,7 +650,7 @@ fn generate_degree_lowering_table_code( cons_ext_substitutions: &[ConstraintCircuitMonad], tran_ext_substitutions: &[ConstraintCircuitMonad], term_ext_substitutions: &[ConstraintCircuitMonad], -) -> String { +) -> TokenStream { let num_new_base_cols = init_base_substitutions.len() + cons_base_substitutions.len() + tran_base_substitutions.len() @@ -662,22 +662,22 @@ fn generate_degree_lowering_table_code( // A zero-variant enum cannot be annotated with `repr(usize)`. let base_repr_usize = match num_new_base_cols == 0 { - true => "", - false => "#[repr(usize)]", + true => quote!(), + false => quote!(#[repr(usize)]), }; let ext_repr_usize = match num_new_ext_cols == 0 { - true => "", - false => "#[repr(usize)]", + true => quote!(), + false => quote!(#[repr(usize)]), }; let base_columns = (0..num_new_base_cols) - .map(|i| format!("DegreeLoweringBaseCol{i}")) - .collect_vec() - .join(",\n"); + .map(|i| format_ident!("DegreeLoweringBaseCol{i}")) + .map(|ident| quote!(#ident)) + .collect_vec(); let ext_columns = (0..num_new_ext_cols) - .map(|i| format!("DegreeLoweringExtCol{i}")) - .collect_vec() - .join(",\n"); + .map(|i| format_ident!("DegreeLoweringExtCol{i}")) + .map(|ident| quote!(#ident)) + .collect_vec(); let fill_base_columns_code = generate_fill_base_columns_code( init_base_substitutions, @@ -692,51 +692,49 @@ fn generate_degree_lowering_table_code( term_ext_substitutions, ); - format!( - " -use ndarray::s; -use ndarray::ArrayView2; -use ndarray::ArrayViewMut2; -use ndarray::Axis; -use ndarray::Zip; -use strum::EnumCount; -use strum_macros::Display; -use strum_macros::EnumCount as EnumCountMacro; -use strum_macros::EnumIter; -use twenty_first::shared_math::b_field_element::BFieldElement; -use twenty_first::shared_math::x_field_element::XFieldElement; - -use crate::table::master_table::NUM_BASE_COLUMNS; -use crate::table::master_table::NUM_EXT_COLUMNS; - -pub const BASE_WIDTH: usize = DegreeLoweringBaseTableColumn::COUNT; -pub const EXT_WIDTH: usize = DegreeLoweringExtTableColumn::COUNT; -pub const FULL_WIDTH: usize = BASE_WIDTH + EXT_WIDTH; - -// This file has been auto-generated. Any modifications _will_ be lost. -// To re-generate, execute: -// `cargo run --bin constraint-evaluation-generator` - -{base_repr_usize} -#[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] -pub enum DegreeLoweringBaseTableColumn {{ - {base_columns} -}} - -{ext_repr_usize} -#[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] -pub enum DegreeLoweringExtTableColumn {{ - {ext_columns} -}} + quote!( + use ndarray::s; + use ndarray::ArrayView2; + use ndarray::ArrayViewMut2; + use ndarray::Axis; + use ndarray::Zip; + use strum::EnumCount; + use strum_macros::Display; + use strum_macros::EnumCount as EnumCountMacro; + use strum_macros::EnumIter; + use twenty_first::shared_math::b_field_element::BFieldElement; + use twenty_first::shared_math::x_field_element::XFieldElement; + + use crate::table::master_table::NUM_BASE_COLUMNS; + use crate::table::master_table::NUM_EXT_COLUMNS; + + pub const BASE_WIDTH: usize = DegreeLoweringBaseTableColumn::COUNT; + pub const EXT_WIDTH: usize = DegreeLoweringExtTableColumn::COUNT; + pub const FULL_WIDTH: usize = BASE_WIDTH + EXT_WIDTH; + + // This file has been auto-generated. Any modifications _will_ be lost. + // To re-generate, execute: + // `cargo run --bin constraint-evaluation-generator` + + #base_repr_usize + #[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] + pub enum DegreeLoweringBaseTableColumn { + #(#base_columns),* + } -#[derive(Debug, Clone)] -pub struct DegreeLoweringTable {{}} + #ext_repr_usize + #[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] + pub enum DegreeLoweringExtTableColumn { + #(#ext_columns),* + } -impl DegreeLoweringTable {{ - {fill_base_columns_code} + #[derive(Debug, Clone)] + pub struct DegreeLoweringTable {} - {fill_ext_columns_code} -}}" + impl DegreeLoweringTable { + #fill_base_columns_code + #fill_ext_columns_code + } ) } @@ -745,18 +743,19 @@ fn generate_fill_base_columns_code( cons_base_substitutions: &[ConstraintCircuitMonad], tran_base_substitutions: &[ConstraintCircuitMonad], term_base_substitutions: &[ConstraintCircuitMonad], -) -> String { +) -> TokenStream { if init_base_substitutions.is_empty() && cons_base_substitutions.is_empty() && tran_base_substitutions.is_empty() && term_base_substitutions.is_empty() { - return "pub fn fill_deterministic_base_columns(_: &mut ArrayViewMut2) { - // prevent unused variable warning - let _ = NUM_BASE_COLUMNS; - // no substitutions - }" - .to_owned(); + return quote!( + pub fn fill_deterministic_base_columns(_: &mut ArrayViewMut2) { + // prevent unused variable warning + let _ = NUM_BASE_COLUMNS; + // no substitutions + } + ); } let single_row_substitutions = init_base_substitutions @@ -764,49 +763,44 @@ fn generate_fill_base_columns_code( .chain(cons_base_substitutions.iter()) .chain(term_base_substitutions.iter()) .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) - .collect_vec() - .join("\n"); + .collect_vec(); let single_row_substitutions = if single_row_substitutions.is_empty() { - "".to_owned() + quote!() } else { - format!( - " + quote!( // For single-row constraints. Zip::from(main_trace_section.axis_iter(Axis(0))) .and(deterministic_section.axis_iter_mut(Axis(0))) - .par_for_each(|base_row, mut deterministic_row| {{ - {single_row_substitutions} - }});" + .par_for_each(|base_row, mut deterministic_row| { + #(#single_row_substitutions)* + }); ) }; let dual_row_substitutions = tran_base_substitutions .iter() .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) - .collect_vec() - .join("\n"); + .collect_vec(); let dual_row_substitutions = if dual_row_substitutions.is_empty() { - "".to_owned() + quote!() } else { - format!( - " + quote!( // For dual-row constraints. // The last row of the deterministic section for transition constraints is not used. let mut deterministic_section = deterministic_section.slice_mut(s![..-1, ..]); Zip::from(main_trace_section.axis_windows(Axis(0), 2)) .and(deterministic_section.exact_chunks_mut((1, deterministic_section.ncols()))) - .par_for_each(|main_trace_chunk, mut deterministic_chunk| {{ + .par_for_each(|main_trace_chunk, mut deterministic_chunk| { let current_base_row = main_trace_chunk.row(0); let next_base_row = main_trace_chunk.row(1); let mut deterministic_row = deterministic_chunk.row_mut(0); - {dual_row_substitutions} - }});" + #(#dual_row_substitutions)* + }); ) }; - format!( - " - pub fn fill_deterministic_base_columns(master_base_table: &mut ArrayViewMut2) {{ + quote!( + pub fn fill_deterministic_base_columns(master_base_table: &mut ArrayViewMut2) { assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); let main_trace_section_start = 0; @@ -819,10 +813,10 @@ fn generate_fill_base_columns_code( s![.., deterministic_section_start..deterministic_section_end], )); - {single_row_substitutions} + #single_row_substitutions - {dual_row_substitutions} - }}" + #dual_row_substitutions + } ) } @@ -831,7 +825,7 @@ fn generate_fill_ext_columns_code( cons_ext_substitutions: &[ConstraintCircuitMonad], tran_ext_substitutions: &[ConstraintCircuitMonad], term_ext_substitutions: &[ConstraintCircuitMonad], -) -> String { +) -> TokenStream { if init_ext_substitutions.is_empty() && cons_ext_substitutions.is_empty() && tran_ext_substitutions.is_empty() @@ -846,8 +840,7 @@ fn generate_fill_ext_columns_code( let _ = NUM_EXT_COLUMNS; // no substitutions } - ) - .to_string(); + ); } let single_row_substitutions = init_ext_substitutions @@ -855,73 +848,65 @@ fn generate_fill_ext_columns_code( .chain(cons_ext_substitutions.iter()) .chain(term_ext_substitutions.iter()) .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) - .collect_vec() - .join("\n"); + .collect_vec(); let single_row_substitutions = if single_row_substitutions.is_empty() { - "".to_owned() + quote!() } else { - format!( - " - // For single-row constraints. - Zip::from(master_base_table.axis_iter(Axis(0))) - .and(main_ext_section.axis_iter(Axis(0))) - .and(deterministic_section.axis_iter_mut(Axis(0))) - .par_for_each(|base_row, ext_row, mut deterministic_row| {{ - {single_row_substitutions} - }});" + quote!( + // For single-row constraints. + Zip::from(master_base_table.axis_iter(Axis(0))) + .and(main_ext_section.axis_iter(Axis(0))) + .and(deterministic_section.axis_iter_mut(Axis(0))) + .par_for_each(|base_row, ext_row, mut deterministic_row| { + #(#single_row_substitutions)* + }); ) }; let dual_row_substitutions = tran_ext_substitutions .iter() .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) - .collect_vec() - .join("\n"); + .collect_vec(); let dual_row_substitutions = if dual_row_substitutions.is_empty() { - "".to_owned() + quote!() } else { - format!( - " - // For dual-row constraints. - // The last row of the deterministic section for transition constraints is not used. - let mut deterministic_section = deterministic_section.slice_mut(s![..-1, ..]); - Zip::from(master_base_table.axis_windows(Axis(0), 2)) - .and(main_ext_section.axis_windows(Axis(0), 2)) - .and(deterministic_section.exact_chunks_mut((1, deterministic_section.ncols()))) - .par_for_each(|base_chunk, main_ext_chunk, mut det_ext_chunk| {{ - let current_base_row = base_chunk.row(0); - let next_base_row = base_chunk.row(1); - let current_ext_row = main_ext_chunk.row(0); - let next_ext_row = main_ext_chunk.row(1); - let mut deterministic_row = det_ext_chunk.row_mut(0); - {dual_row_substitutions} - }});" + quote!( + // For dual-row constraints. + // The last row of the deterministic section for transition constraints is not used. + let mut deterministic_section = deterministic_section.slice_mut(s![..-1, ..]); + Zip::from(master_base_table.axis_windows(Axis(0), 2)) + .and(main_ext_section.axis_windows(Axis(0), 2)) + .and(deterministic_section.exact_chunks_mut((1, deterministic_section.ncols()))) + .par_for_each(|base_chunk, main_ext_chunk, mut det_ext_chunk| { + let current_base_row = base_chunk.row(0); + let next_base_row = base_chunk.row(1); + let current_ext_row = main_ext_chunk.row(0); + let next_ext_row = main_ext_chunk.row(1); + let mut deterministic_row = det_ext_chunk.row_mut(0); + #(#dual_row_substitutions)* + }); ) }; - format!( - " - pub fn fill_deterministic_ext_columns( - master_base_table: ArrayView2, - master_ext_table: &mut ArrayViewMut2, - ) {{ - assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); - assert_eq!(NUM_EXT_COLUMNS, master_ext_table.ncols()); - - let main_ext_section_start = 0; - let main_ext_section_end = main_ext_section_start + NUM_EXT_COLUMNS - EXT_WIDTH; - let det_ext_section_start = main_ext_section_end; - let det_ext_section_end = det_ext_section_start + EXT_WIDTH; - - let (main_ext_section, mut deterministic_section) = master_ext_table.multi_slice_mut(( - s![.., main_ext_section_start..main_ext_section_end], - s![.., det_ext_section_start..det_ext_section_end], - )); - - {single_row_substitutions} - - {dual_row_substitutions} - }} -" + quote!( + pub fn fill_deterministic_ext_columns( + master_base_table: ArrayView2, + master_ext_table: &mut ArrayViewMut2, + ) { + assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); + assert_eq!(NUM_EXT_COLUMNS, master_ext_table.ncols()); + + let main_ext_section_start = 0; + let main_ext_section_end = main_ext_section_start + NUM_EXT_COLUMNS - EXT_WIDTH; + let det_ext_section_start = main_ext_section_end; + let det_ext_section_end = det_ext_section_start + EXT_WIDTH; + + let (main_ext_section, mut deterministic_section) = master_ext_table.multi_slice_mut(( + s![.., main_ext_section_start..main_ext_section_end], + s![.., det_ext_section_start..det_ext_section_end], + )); + #single_row_substitutions + #dual_row_substitutions + } ) } From aca251bd535123f6348adf3c9feeceaa69b41490 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 25 May 2023 13:28:01 +0200 Subject: [PATCH 52/72] evaluate base table substitution rules sequentially --- constraint-evaluation-generator/src/main.rs | 158 +++++++++++--------- 1 file changed, 85 insertions(+), 73 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 947a640e..e3bc2396 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -618,24 +618,17 @@ fn evaluate_single_node( } /// Given a substitution rule, i.e., a `ConstraintCircuit` of the form `x - expr`, generate code -/// that evaluates `expr` on the appropriate base rows and sets `x` to the result. +/// that evaluates `expr`. fn substitution_rule_to_code(circuit: ConstraintCircuit) -> TokenStream { - let circuit_evaluates_to_base_element = circuit.evaluates_to_base_element(); let BinaryOperation(BinOp::Sub, new_var, expr) = circuit.expression else { panic!("Substitution rule must be a subtraction."); }; - let Input(new_var) = new_var.as_ref().borrow().expression else { + let Input(_) = new_var.as_ref().borrow().expression else { panic!("Substitution rule must be a simple substitution."); }; - let new_var_idx = match circuit_evaluates_to_base_element { - true => new_var.base_col_index(), - false => new_var.ext_col_index(), - }; let expr = expr.as_ref().borrow().to_owned(); - let expr = evaluate_single_node(usize::MAX, &expr, &HashSet::new()); - - quote!(deterministic_row[#new_var_idx - deterministic_section_start] = #expr;) + evaluate_single_node(usize::MAX, &expr, &HashSet::new()) } /// Given all substitution rules, generate the code that evaluates them in order. @@ -696,8 +689,6 @@ fn generate_degree_lowering_table_code( use ndarray::s; use ndarray::ArrayView2; use ndarray::ArrayViewMut2; - use ndarray::Axis; - use ndarray::Zip; use strum::EnumCount; use strum_macros::Display; use strum_macros::EnumCount as EnumCountMacro; @@ -739,83 +730,104 @@ fn generate_degree_lowering_table_code( } fn generate_fill_base_columns_code( - init_base_substitutions: &[ConstraintCircuitMonad], - cons_base_substitutions: &[ConstraintCircuitMonad], - tran_base_substitutions: &[ConstraintCircuitMonad], - term_base_substitutions: &[ConstraintCircuitMonad], + init_substitutions: &[ConstraintCircuitMonad], + cons_substitutions: &[ConstraintCircuitMonad], + tran_substitutions: &[ConstraintCircuitMonad], + term_substitutions: &[ConstraintCircuitMonad], ) -> TokenStream { - if init_base_substitutions.is_empty() - && cons_base_substitutions.is_empty() - && tran_base_substitutions.is_empty() - && term_base_substitutions.is_empty() - { - return quote!( - pub fn fill_deterministic_base_columns(_: &mut ArrayViewMut2) { - // prevent unused variable warning - let _ = NUM_BASE_COLUMNS; - // no substitutions - } - ); - } + let deterministic_section_start = + master_table::NUM_BASE_COLUMNS - degree_lowering_table::BASE_WIDTH; - let single_row_substitutions = init_base_substitutions + let num_init_substitutions = init_substitutions.len(); + let num_cons_substitutions = cons_substitutions.len(); + let num_tran_substitutions = tran_substitutions.len(); + let num_term_substitutions = term_substitutions.len(); + + let init_col_indices = (0..num_init_substitutions) + .map(|i| i + deterministic_section_start) + .collect_vec(); + let cons_col_indices = (0..num_cons_substitutions) + .map(|i| i + deterministic_section_start + num_init_substitutions) + .collect_vec(); + let tran_col_indices = (0..num_tran_substitutions) + .map(|i| i + deterministic_section_start + num_init_substitutions + num_cons_substitutions) + .collect_vec(); + let term_col_indices = (0..num_term_substitutions) + .map(|i| { + i + deterministic_section_start + + num_init_substitutions + + num_cons_substitutions + + num_tran_substitutions + }) + .collect_vec(); + + let init_substitutions = init_substitutions .iter() - .chain(cons_base_substitutions.iter()) - .chain(term_base_substitutions.iter()) .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) .collect_vec(); - let single_row_substitutions = if single_row_substitutions.is_empty() { - quote!() - } else { - quote!( - // For single-row constraints. - Zip::from(main_trace_section.axis_iter(Axis(0))) - .and(deterministic_section.axis_iter_mut(Axis(0))) - .par_for_each(|base_row, mut deterministic_row| { - #(#single_row_substitutions)* - }); - ) - }; - - let dual_row_substitutions = tran_base_substitutions + let cons_substitutions = cons_substitutions .iter() .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) .collect_vec(); - let dual_row_substitutions = if dual_row_substitutions.is_empty() { - quote!() - } else { + let tran_substitutions = tran_substitutions + .iter() + .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) + .collect_vec(); + let term_substitutions = term_substitutions + .iter() + .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) + .collect_vec(); + + let single_row_substitutions = |indices: Vec, substitutions: Vec| { + assert_eq!(indices.len(), substitutions.len()); + if indices.is_empty() { + return quote!(); + } quote!( - // For dual-row constraints. - // The last row of the deterministic section for transition constraints is not used. - let mut deterministic_section = deterministic_section.slice_mut(s![..-1, ..]); - Zip::from(main_trace_section.axis_windows(Axis(0), 2)) - .and(deterministic_section.exact_chunks_mut((1, deterministic_section.ncols()))) - .par_for_each(|main_trace_chunk, mut deterministic_chunk| { - let current_base_row = main_trace_chunk.row(0); - let next_base_row = main_trace_chunk.row(1); - let mut deterministic_row = deterministic_chunk.row_mut(0); - #(#dual_row_substitutions)* + master_base_table.rows_mut().into_iter().for_each(|mut row| { + #( + let (base_row, mut det_col) = + row.multi_slice_mut((s![..#indices],s![#indices..#indices + 1])); + det_col[0] = #substitutions; + )* }); ) }; + let dual_row_substitutions = |indices: Vec, substitutions: Vec| { + assert_eq!(indices.len(), substitutions.len()); + if indices.is_empty() { + return quote!(); + } + quote!( + for row_idx in 0..master_base_table.nrows() - 1 { + let (mut curr_base_row, next_base_row) = master_base_table.multi_slice_mut(( + s![row_idx..row_idx + 1, ..], + s![row_idx + 1..row_idx + 2, ..], + )); + let mut curr_base_row = curr_base_row.row_mut(0); + let next_base_row = next_base_row.row(0); + #( + let (current_base_row, mut det_col) = + curr_base_row.multi_slice_mut((s![..#indices], s![#indices..#indices + 1])); + det_col[0] = #substitutions; + )* + } + ) + }; + + let init_substitutions = single_row_substitutions(init_col_indices, init_substitutions); + let cons_substitutions = single_row_substitutions(cons_col_indices, cons_substitutions); + let tran_substitutions = dual_row_substitutions(tran_col_indices, tran_substitutions); + let term_substitutions = single_row_substitutions(term_col_indices, term_substitutions); quote!( + #[allow(unused_variables)] pub fn fill_deterministic_base_columns(master_base_table: &mut ArrayViewMut2) { assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); - - let main_trace_section_start = 0; - let main_trace_section_end = main_trace_section_start + NUM_BASE_COLUMNS - BASE_WIDTH; - let deterministic_section_start = main_trace_section_end; - let deterministic_section_end = deterministic_section_start + BASE_WIDTH; - - let (main_trace_section, mut deterministic_section) = master_base_table.multi_slice_mut(( - s![.., main_trace_section_start..main_trace_section_end], - s![.., deterministic_section_start..deterministic_section_end], - )); - - #single_row_substitutions - - #dual_row_substitutions + #init_substitutions + #cons_substitutions + #tran_substitutions + #term_substitutions } ) } From e8590f65f73bc540dbe2a76e6c837308c64bc510 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 25 May 2023 14:50:08 +0200 Subject: [PATCH 53/72] evaluate extension table substitution rules sequentially --- constraint-evaluation-generator/src/main.rs | 155 +++++++++++--------- 1 file changed, 88 insertions(+), 67 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index e3bc2396..44cd99a9 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -833,92 +833,113 @@ fn generate_fill_base_columns_code( } fn generate_fill_ext_columns_code( - init_ext_substitutions: &[ConstraintCircuitMonad], - cons_ext_substitutions: &[ConstraintCircuitMonad], - tran_ext_substitutions: &[ConstraintCircuitMonad], - term_ext_substitutions: &[ConstraintCircuitMonad], + init_substitutions: &[ConstraintCircuitMonad], + cons_substitutions: &[ConstraintCircuitMonad], + tran_substitutions: &[ConstraintCircuitMonad], + term_substitutions: &[ConstraintCircuitMonad], ) -> TokenStream { - if init_ext_substitutions.is_empty() - && cons_ext_substitutions.is_empty() - && tran_ext_substitutions.is_empty() - && term_ext_substitutions.is_empty() - { - return quote!( - pub fn fill_deterministic_ext_columns( - _: ArrayView2, - _: &mut ArrayViewMut2, - ) { - // prevent unused variable warning - let _ = NUM_EXT_COLUMNS; - // no substitutions - } - ); - } + let deterministic_section_start = + master_table::NUM_EXT_COLUMNS - degree_lowering_table::EXT_WIDTH; + + let num_init_substitutions = init_substitutions.len(); + let num_cons_substitutions = cons_substitutions.len(); + let num_tran_substitutions = tran_substitutions.len(); + let num_term_substitutions = term_substitutions.len(); + + let init_col_indices = (0..num_init_substitutions) + .map(|i| i + deterministic_section_start) + .collect_vec(); + let cons_col_indices = (0..num_cons_substitutions) + .map(|i| i + deterministic_section_start + num_init_substitutions) + .collect_vec(); + let tran_col_indices = (0..num_tran_substitutions) + .map(|i| i + deterministic_section_start + num_init_substitutions + num_cons_substitutions) + .collect_vec(); + let term_col_indices = (0..num_term_substitutions) + .map(|i| { + i + deterministic_section_start + + num_init_substitutions + + num_cons_substitutions + + num_tran_substitutions + }) + .collect_vec(); - let single_row_substitutions = init_ext_substitutions + let init_substitutions = init_substitutions .iter() - .chain(cons_ext_substitutions.iter()) - .chain(term_ext_substitutions.iter()) .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) .collect_vec(); - let single_row_substitutions = if single_row_substitutions.is_empty() { - quote!() - } else { - quote!( - // For single-row constraints. - Zip::from(master_base_table.axis_iter(Axis(0))) - .and(main_ext_section.axis_iter(Axis(0))) - .and(deterministic_section.axis_iter_mut(Axis(0))) - .par_for_each(|base_row, ext_row, mut deterministic_row| { - #(#single_row_substitutions)* - }); - ) - }; - - let dual_row_substitutions = tran_ext_substitutions + let cons_substitutions = cons_substitutions .iter() .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) .collect_vec(); - let dual_row_substitutions = if dual_row_substitutions.is_empty() { - quote!() - } else { + let tran_substitutions = tran_substitutions + .iter() + .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) + .collect_vec(); + let term_substitutions = term_substitutions + .iter() + .map(|c| substitution_rule_to_code(c.circuit.as_ref().borrow().to_owned())) + .collect_vec(); + + let single_row_substitutions = |indices: Vec, substitutions: Vec| { + assert_eq!(indices.len(), substitutions.len()); + if indices.is_empty() { + return quote!(); + } quote!( - // For dual-row constraints. - // The last row of the deterministic section for transition constraints is not used. - let mut deterministic_section = deterministic_section.slice_mut(s![..-1, ..]); - Zip::from(master_base_table.axis_windows(Axis(0), 2)) - .and(main_ext_section.axis_windows(Axis(0), 2)) - .and(deterministic_section.exact_chunks_mut((1, deterministic_section.ncols()))) - .par_for_each(|base_chunk, main_ext_chunk, mut det_ext_chunk| { - let current_base_row = base_chunk.row(0); - let next_base_row = base_chunk.row(1); - let current_ext_row = main_ext_chunk.row(0); - let next_ext_row = main_ext_chunk.row(1); - let mut deterministic_row = det_ext_chunk.row_mut(0); - #(#dual_row_substitutions)* - }); + for row_idx in 0..master_base_table.nrows() - 1 { + let base_row = master_base_table.row(row_idx); + let mut extension_row = master_ext_table.row_mut(row_idx); + #( + let (ext_row, mut det_col) = + extension_row.multi_slice_mut((s![..#indices],s![#indices..#indices + 1])); + det_col[0] = #substitutions; + )* + } ) }; + let dual_row_substitutions = |indices: Vec, substitutions: Vec| { + assert_eq!(indices.len(), substitutions.len()); + if indices.is_empty() { + return quote!(); + } + quote!( + for row_idx in 0..master_base_table.nrows() - 1 { + let current_base_row = master_base_table.row(row_idx); + let next_base_row = master_base_table.row(row_idx + 1); + let (mut curr_ext_row, next_ext_row) = master_ext_table.multi_slice_mut(( + s![row_idx..row_idx + 1, ..], + s![row_idx + 1..row_idx + 2, ..], + )); + let mut curr_ext_row = curr_ext_row.row_mut(0); + let next_ext_row = next_ext_row.row(0); + #( + let (current_ext_row, mut det_col) = + curr_ext_row.multi_slice_mut((s![..#indices], s![#indices..#indices + 1])); + det_col[0] = #substitutions; + )* + } + ) + }; + + let init_substitutions = single_row_substitutions(init_col_indices, init_substitutions); + let cons_substitutions = single_row_substitutions(cons_col_indices, cons_substitutions); + let tran_substitutions = dual_row_substitutions(tran_col_indices, tran_substitutions); + let term_substitutions = single_row_substitutions(term_col_indices, term_substitutions); quote!( + #[allow(unused_variables)] pub fn fill_deterministic_ext_columns( master_base_table: ArrayView2, master_ext_table: &mut ArrayViewMut2, ) { assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); assert_eq!(NUM_EXT_COLUMNS, master_ext_table.ncols()); - - let main_ext_section_start = 0; - let main_ext_section_end = main_ext_section_start + NUM_EXT_COLUMNS - EXT_WIDTH; - let det_ext_section_start = main_ext_section_end; - let det_ext_section_end = det_ext_section_start + EXT_WIDTH; - - let (main_ext_section, mut deterministic_section) = master_ext_table.multi_slice_mut(( - s![.., main_ext_section_start..main_ext_section_end], - s![.., det_ext_section_start..det_ext_section_end], - )); - #single_row_substitutions - #dual_row_substitutions + assert_eq!(master_base_table.nrows(), master_ext_table.nrows()); + #init_substitutions + #cons_substitutions + #tran_substitutions + #term_substitutions } ) } From 3b8522160475802ca9065311089180284adcfbfd Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 30 May 2023 10:51:55 +0200 Subject: [PATCH 54/72] assert in degree lowering tests: only substitute existing variables --- triton-vm/src/table/constraint_circuit.rs | 41 +++++++++++++++++++---- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index e5eacac5..ad270663 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -1913,12 +1913,12 @@ mod constraint_circuit_tests { let BinaryOperation(BinOp::Sub, lhs, rhs) = expression else { panic!("New {constraint_type} constraint {i} must be a subtraction."); }; - let lhs_degree = lhs.as_ref().borrow().degree(); - assert_eq!( - 1, lhs_degree, - "New {constraint_type} constraint {i} must be a simple substitution." - ); - substitution_rules.push(rhs.as_ref().borrow().clone()); + let Input(input_indicator) = lhs.as_ref().borrow().expression.clone() else { + panic!("New {constraint_type} constraint {i} must be a simple substitution."); + }; + let substitution_rule = rhs.as_ref().borrow().clone(); + assert_substitution_rule_uses_legal_variables(input_indicator, &substitution_rule); + substitution_rules.push(substitution_rule); } } @@ -1974,4 +1974,33 @@ mod constraint_circuit_tests { (new_base_constraints, new_ext_constraints) } + + /// Panics if the given substitution rule uses variables with an index greater than (or equal) + /// to the given index. In practice, this given index corresponds to a newly introduced + /// variable. + fn assert_substitution_rule_uses_legal_variables( + new_var: II, + substitution_rule: &ConstraintCircuit, + ) { + match substitution_rule.expression.clone() { + BinaryOperation(_, lhs, rhs) => { + let lhs = lhs.as_ref().borrow(); + let rhs = rhs.as_ref().borrow(); + assert_substitution_rule_uses_legal_variables(new_var, &lhs); + assert_substitution_rule_uses_legal_variables(new_var, &rhs); + } + Input(old_var) => { + let new_var_is_base = new_var.is_base_table_column(); + let old_var_is_base = old_var.is_base_table_column(); + let legal_substitute = match (new_var_is_base, old_var_is_base) { + (true, true) => old_var.base_col_index() < new_var.base_col_index(), + (true, false) => false, + (false, true) => true, + (false, false) => old_var.ext_col_index() < new_var.ext_col_index(), + }; + assert!(legal_substitute, "Cannot replace {old_var} with {new_var}."); + } + _ => (), + }; + } } From 97a2963f9fe1d4c67e1fa1f8e1777ab451c0a8f8 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 30 May 2023 11:20:48 +0200 Subject: [PATCH 55/72] add degree lowering tests --- triton-vm/src/table/constraint_circuit.rs | 372 +++++++++++++++++++++- 1 file changed, 360 insertions(+), 12 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index ad270663..05be3d82 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -891,8 +891,10 @@ impl ConstraintCircuitMonad { /// These can then be used to construct new columns, /// as well as derivation rules for filling those new columns. /// - /// The number of base and extension columns used by the multicircuit have to be provided. - /// The uniqueness of the new columns' indices depends on these provided values. + /// The highest index of base and extension columns used by the multicircuit have to be + /// provided. The uniqueness of the new columns' indices depends on these provided values. + /// Note that these indices are generally not equal to the number of used columns, especially + /// when a tables' constraints are built using the master table's column indices. pub fn lower_to_degree( multicircuit: &mut [Self], target_degree: Degree, @@ -1151,8 +1153,8 @@ mod constraint_circuit_tests { use crate::table::jump_stack_table::ExtJumpStackTable; use crate::table::lookup_table::ExtLookupTable; use crate::table::master_table; + use crate::table::master_table::*; use crate::table::op_stack_table::ExtOpStackTable; - use crate::table::processor_table; use crate::table::processor_table::ExtProcessorTable; use crate::table::program_table::ExtProgramTable; use crate::table::ram_table::ExtRamTable; @@ -1851,16 +1853,362 @@ mod constraint_circuit_tests { } #[test] - fn serious_degree_lowering_test() { - let mut multicircuit = build_multicircuit(&ExtProcessorTable::transition_constraints); - let target_degree = 3; - let num_base_cols = processor_table::BASE_WIDTH; - let num_ext_cols = processor_table::EXT_WIDTH; + fn program_table_initial_constraints_degree_lowering_test() { lower_degree_and_assert_properties( - &mut multicircuit, - target_degree, - num_base_cols, - num_ext_cols, + &mut build_multicircuit(&ExtProgramTable::initial_constraints), + AIR_TARGET_DEGREE, + PROGRAM_TABLE_END, + EXT_PROGRAM_TABLE_END, + ); + } + + #[test] + fn program_table_consistency_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtProgramTable::consistency_constraints), + AIR_TARGET_DEGREE, + PROGRAM_TABLE_END, + EXT_PROGRAM_TABLE_END, + ); + } + + #[test] + fn program_table_transition_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtProgramTable::transition_constraints), + AIR_TARGET_DEGREE, + PROGRAM_TABLE_END, + EXT_PROGRAM_TABLE_END, + ); + } + + #[test] + fn program_table_terminal_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtProgramTable::terminal_constraints), + AIR_TARGET_DEGREE, + PROGRAM_TABLE_END, + EXT_PROGRAM_TABLE_END, + ); + } + + #[test] + fn processor_table_initial_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtProcessorTable::initial_constraints), + AIR_TARGET_DEGREE, + PROCESSOR_TABLE_END, + EXT_PROCESSOR_TABLE_END, + ); + } + + #[test] + fn processor_table_consistency_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtProcessorTable::consistency_constraints), + AIR_TARGET_DEGREE, + PROCESSOR_TABLE_END, + EXT_PROCESSOR_TABLE_END, + ); + } + + #[test] + fn processor_table_transition_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtProcessorTable::transition_constraints), + AIR_TARGET_DEGREE, + PROCESSOR_TABLE_END, + EXT_PROCESSOR_TABLE_END, + ); + } + + #[test] + fn processor_table_terminal_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtProcessorTable::terminal_constraints), + AIR_TARGET_DEGREE, + PROCESSOR_TABLE_END, + EXT_PROCESSOR_TABLE_END, + ); + } + + #[test] + fn op_stack_table_initial_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtOpStackTable::initial_constraints), + AIR_TARGET_DEGREE, + OP_STACK_TABLE_END, + EXT_OP_STACK_TABLE_END, + ); + } + + #[test] + fn op_stack_table_consistency_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtOpStackTable::consistency_constraints), + AIR_TARGET_DEGREE, + OP_STACK_TABLE_END, + EXT_OP_STACK_TABLE_END, + ); + } + + #[test] + fn op_stack_table_transition_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtOpStackTable::transition_constraints), + AIR_TARGET_DEGREE, + OP_STACK_TABLE_END, + EXT_OP_STACK_TABLE_END, + ); + } + + #[test] + fn op_stack_table_terminal_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtOpStackTable::terminal_constraints), + AIR_TARGET_DEGREE, + OP_STACK_TABLE_END, + EXT_OP_STACK_TABLE_END, + ); + } + + #[test] + fn ram_table_initial_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtRamTable::initial_constraints), + AIR_TARGET_DEGREE, + RAM_TABLE_END, + EXT_RAM_TABLE_END, + ); + } + + #[test] + fn ram_table_consistency_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtRamTable::consistency_constraints), + AIR_TARGET_DEGREE, + RAM_TABLE_END, + EXT_RAM_TABLE_END, + ); + } + + #[test] + fn ram_table_transition_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtRamTable::transition_constraints), + AIR_TARGET_DEGREE, + RAM_TABLE_END, + EXT_RAM_TABLE_END, + ); + } + + #[test] + fn ram_table_terminal_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtRamTable::terminal_constraints), + AIR_TARGET_DEGREE, + RAM_TABLE_END, + EXT_RAM_TABLE_END, + ); + } + + #[test] + fn jump_stack_table_initial_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtJumpStackTable::initial_constraints), + AIR_TARGET_DEGREE, + JUMP_STACK_TABLE_END, + EXT_JUMP_STACK_TABLE_END, + ); + } + + #[test] + fn jump_stack_table_consistency_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtJumpStackTable::consistency_constraints), + AIR_TARGET_DEGREE, + JUMP_STACK_TABLE_END, + EXT_JUMP_STACK_TABLE_END, + ); + } + + #[test] + fn jump_stack_table_transition_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtJumpStackTable::transition_constraints), + AIR_TARGET_DEGREE, + JUMP_STACK_TABLE_END, + EXT_JUMP_STACK_TABLE_END, + ); + } + + #[test] + fn jump_stack_table_terminal_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtJumpStackTable::terminal_constraints), + AIR_TARGET_DEGREE, + JUMP_STACK_TABLE_END, + EXT_JUMP_STACK_TABLE_END, + ); + } + + #[test] + fn hash_table_initial_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtHashTable::initial_constraints), + AIR_TARGET_DEGREE, + HASH_TABLE_END, + EXT_HASH_TABLE_END, + ); + } + + #[test] + fn hash_table_consistency_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtHashTable::consistency_constraints), + AIR_TARGET_DEGREE, + HASH_TABLE_END, + EXT_HASH_TABLE_END, + ); + } + + #[test] + fn hash_table_transition_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtHashTable::transition_constraints), + AIR_TARGET_DEGREE, + HASH_TABLE_END, + EXT_HASH_TABLE_END, + ); + } + + #[test] + fn hash_table_terminal_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtHashTable::terminal_constraints), + AIR_TARGET_DEGREE, + HASH_TABLE_END, + EXT_HASH_TABLE_END, + ); + } + + #[test] + fn cascade_table_initial_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtCascadeTable::initial_constraints), + AIR_TARGET_DEGREE, + CASCADE_TABLE_END, + EXT_CASCADE_TABLE_END, + ); + } + + #[test] + fn cascade_table_consistency_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtCascadeTable::consistency_constraints), + AIR_TARGET_DEGREE, + CASCADE_TABLE_END, + EXT_CASCADE_TABLE_END, + ); + } + + #[test] + fn cascade_table_transition_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtCascadeTable::transition_constraints), + AIR_TARGET_DEGREE, + CASCADE_TABLE_END, + EXT_CASCADE_TABLE_END, + ); + } + + #[test] + fn cascade_table_terminal_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtCascadeTable::terminal_constraints), + AIR_TARGET_DEGREE, + CASCADE_TABLE_END, + EXT_CASCADE_TABLE_END, + ); + } + + #[test] + fn lookup_table_initial_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtLookupTable::initial_constraints), + AIR_TARGET_DEGREE, + LOOKUP_TABLE_END, + EXT_LOOKUP_TABLE_END, + ); + } + + #[test] + fn lookup_table_consistency_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtLookupTable::consistency_constraints), + AIR_TARGET_DEGREE, + LOOKUP_TABLE_END, + EXT_LOOKUP_TABLE_END, + ); + } + + #[test] + fn lookup_table_transition_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtLookupTable::transition_constraints), + AIR_TARGET_DEGREE, + LOOKUP_TABLE_END, + EXT_LOOKUP_TABLE_END, + ); + } + + #[test] + fn lookup_table_terminal_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtLookupTable::terminal_constraints), + AIR_TARGET_DEGREE, + LOOKUP_TABLE_END, + EXT_LOOKUP_TABLE_END, + ); + } + + #[test] + fn u32_table_initial_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtU32Table::initial_constraints), + AIR_TARGET_DEGREE, + U32_TABLE_END, + EXT_U32_TABLE_END, + ); + } + + #[test] + fn u32_table_consistency_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtU32Table::consistency_constraints), + AIR_TARGET_DEGREE, + U32_TABLE_END, + EXT_U32_TABLE_END, + ); + } + + #[test] + fn u32_table_transition_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtU32Table::transition_constraints), + AIR_TARGET_DEGREE, + U32_TABLE_END, + EXT_U32_TABLE_END, + ); + } + + #[test] + fn u32_table_terminal_constraints_degree_lowering_test() { + lower_degree_and_assert_properties( + &mut build_multicircuit(&ExtU32Table::terminal_constraints), + AIR_TARGET_DEGREE, + U32_TABLE_END, + EXT_U32_TABLE_END, ); } From 7282d9ec6995562abd9d69488744000d0c9ec3ba Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 30 May 2023 15:13:13 +0200 Subject: [PATCH 56/72] disregard unused variables when deciding on the substitution --- triton-vm/src/table/constraint_circuit.rs | 49 +++++++++++++++++++---- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 05be3d82..89ad7b7a 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -915,7 +915,9 @@ impl ConstraintCircuitMonad { let builder = multicircuit[0].builder.clone(); while Self::multicircuit_degree(multicircuit) > target_degree { - let chosen_node = Self::pick_node_to_substitute(target_degree, &builder); + let current_multicircuit = + [multicircuit, &base_constraints[..], &ext_constraints[..]].concat(); + let chosen_node = Self::pick_node_to_substitute(¤t_multicircuit, target_degree); // Create a new variable. let chosen_node_id = chosen_node.as_ref().borrow().id; @@ -958,24 +960,29 @@ impl ConstraintCircuitMonad { (base_constraints, ext_constraints) } + /// Pick a node from the given multicircuit that is to be substituted with a new variable. + /// The node is chosen such that the degree of the multicircuit is lowered. + /// A heuristic is used to pick the node. fn pick_node_to_substitute( + multicircuit: &[ConstraintCircuitMonad], target_degree: Degree, - builder: &ConstraintCircuitBuilder, ) -> Rc>> { + if multicircuit.is_empty() { + panic!("Multicircuit must be non-empty in order to pick a node from it."); + } + // Only nodes with degree > target_degree need changing. - let all_nodes = builder.all_nodes.as_ref().borrow(); + let all_nodes = Self::all_nodes_in_multicircuit(multicircuit); let high_degree_nodes = all_nodes .iter() - .filter(|node| node.circuit.as_ref().borrow().degree() > target_degree); + .filter(|node| node.degree() > target_degree); // Of those nodes, get all the children with degree <= target_degree. let mut barely_low_degree_nodes = vec![]; for node in high_degree_nodes { // Constants, inputs, and challenges are of degree <= 1, which is not high. // Addition and subtraction don't affect the degree. Hence, they are uninteresting. - if let BinaryOperation(BinOp::Mul, lhs, rhs) = - &node.circuit.as_ref().borrow().expression - { + if let BinaryOperation(BinOp::Mul, lhs, rhs) = &node.expression { if lhs.as_ref().borrow().degree() <= target_degree { barely_low_degree_nodes.push(lhs.clone()); } @@ -1014,6 +1021,34 @@ impl ConstraintCircuitMonad { .unwrap() } + /// Returns all nodes used in the multicircuit. + /// This is distinct from [`ConstraintCircuitBuilder::all_nodes`] because it only considers + /// nodes actually used in the given multicircuit, not all nodes in the builder. + pub fn all_nodes_in_multicircuit( + multicircuit: &[ConstraintCircuitMonad], + ) -> HashSet> { + let mut all_nodes = HashSet::new(); + for circuit in multicircuit.iter() { + let constraint_circuit = circuit.circuit.as_ref().borrow().clone(); + let nodes_in_circuit = Self::all_nodes_in_circuit(constraint_circuit); + all_nodes.extend(nodes_in_circuit); + } + all_nodes + } + + /// Internal helper function to recursively find all nodes in a circuit. + fn all_nodes_in_circuit(circuit: ConstraintCircuit) -> HashSet> { + let mut all_nodes = HashSet::new(); + if let BinaryOperation(_, lhs, rhs) = circuit.expression.clone() { + let lhs_nodes = Self::all_nodes_in_circuit(lhs.as_ref().borrow().clone()); + let rhs_nodes = Self::all_nodes_in_circuit(rhs.as_ref().borrow().clone()); + all_nodes.extend(lhs_nodes); + all_nodes.extend(rhs_nodes); + }; + all_nodes.insert(circuit); + all_nodes + } + /// Returns the maximum degree of all circuits in the multicircuit. fn multicircuit_degree(multicircuit: &[ConstraintCircuitMonad]) -> Degree { multicircuit From b35238776251929cf8b3555c17fce23c84b622e8 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 31 May 2023 07:31:30 +0200 Subject: [PATCH 57/72] add minimal (non-)working example demonstrating failure in heuristic --- triton-vm/src/table/constraint_circuit.rs | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 89ad7b7a..ae46f957 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -1887,6 +1887,30 @@ mod constraint_circuit_tests { assert!(new_ext_constraints.len() <= 1); } + #[test] + fn less_simple_degree_lowering_test() { + let builder = ConstraintCircuitBuilder::new(); + let x = |i| builder.input(BaseRow(i)); + + let constraint_0 = (x(0) * x(1) * x(2)) * (x(3) * x(4)) * x(5); + let constraint_1 = (x(6) * x(7)) * (x(3) * x(4)) * x(8); + + let mut multicircuit = [constraint_0, constraint_1]; + + let target_degree = 3; + let num_base_cols = 9; + let num_ext_cols = 0; + let (new_base_constraints, new_ext_constraints) = lower_degree_and_assert_properties( + &mut multicircuit, + target_degree, + num_base_cols, + num_ext_cols, + ); + + assert!(new_base_constraints.len() <= 4); + assert!(new_ext_constraints.is_empty()); + } + #[test] fn program_table_initial_constraints_degree_lowering_test() { lower_degree_and_assert_properties( From 8dabfb3051a32e62c92d01e16564059bbb58d79f Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 31 May 2023 08:40:50 +0200 Subject: [PATCH 58/72] rework heuristic for picking nodes to be substituted --- triton-vm/src/table/constraint_circuit.rs | 118 ++++++++++++---------- 1 file changed, 62 insertions(+), 56 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index ae46f957..92039668 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -6,6 +6,7 @@ //! constraint polynomials, with each root corresponding to a different constraint polynomial. //! Because the graph has multiple roots, it is called a “multitree.” +use itertools::Itertools; use std::borrow::BorrowMut; use std::cell::RefCell; use std::cmp; @@ -915,13 +916,11 @@ impl ConstraintCircuitMonad { let builder = multicircuit[0].builder.clone(); while Self::multicircuit_degree(multicircuit) > target_degree { - let current_multicircuit = - [multicircuit, &base_constraints[..], &ext_constraints[..]].concat(); - let chosen_node = Self::pick_node_to_substitute(¤t_multicircuit, target_degree); + let chosen_node_id = Self::pick_node_to_substitute(multicircuit, target_degree); // Create a new variable. - let chosen_node_id = chosen_node.as_ref().borrow().id; - let chosen_node_is_base_col = chosen_node.as_ref().borrow().evaluates_to_base_element(); + let chosen_node = builder.get_node_by_id(chosen_node_id).unwrap(); + let chosen_node_is_base_col = chosen_node.circuit.borrow().evaluates_to_base_element(); let new_col_idx = match chosen_node_is_base_col { true => num_base_cols + base_constraints.len(), false => num_ext_cols + ext_constraints.len(), @@ -936,14 +935,8 @@ impl ConstraintCircuitMonad { // Substitute the chosen circuit with the new variable. builder.substitute(chosen_node_id, new_circuit.clone()); - // Create a new constraint: new_var - chosen_node - let chosen_node_monad = ConstraintCircuitMonad { - circuit: chosen_node, - builder: builder.clone(), - }; - let new_constraint = new_variable - chosen_node_monad; - - // Put the new constraint into the appropriate return vector. + // Create new constraint and put it into the appropriate return vector. + let new_constraint = new_variable - chosen_node; match chosen_node_is_base_col { true => base_constraints.push(new_constraint), false => ext_constraints.push(new_constraint), @@ -951,7 +944,7 @@ impl ConstraintCircuitMonad { // Treat roots of the multicircuit explicitly. for circuit in multicircuit.iter_mut() { - if circuit.circuit.as_ref().borrow().id == chosen_node_id { + if circuit.circuit.borrow().id == chosen_node_id { circuit.circuit = new_circuit.clone(); } } @@ -960,19 +953,23 @@ impl ConstraintCircuitMonad { (base_constraints, ext_constraints) } - /// Pick a node from the given multicircuit that is to be substituted with a new variable. - /// The node is chosen such that the degree of the multicircuit is lowered. - /// A heuristic is used to pick the node. + /// Heuristically pick a node from the given multicircuit that is to be substituted with a new + /// variable. The ID of the chosen node is returned. fn pick_node_to_substitute( multicircuit: &[ConstraintCircuitMonad], target_degree: Degree, - ) -> Rc>> { + ) -> usize { if multicircuit.is_empty() { panic!("Multicircuit must be non-empty in order to pick a node from it."); } // Only nodes with degree > target_degree need changing. - let all_nodes = Self::all_nodes_in_multicircuit(multicircuit); + let multicircuit = multicircuit + .iter() + .map(|c| c.clone().consume()) + .collect_vec(); + let all_nodes = Self::all_nodes_in_multicircuit(&multicircuit); + let all_nodes: HashSet<_> = HashSet::from_iter(all_nodes.iter()); let high_degree_nodes = all_nodes .iter() .filter(|node| node.degree() > target_degree); @@ -983,69 +980,78 @@ impl ConstraintCircuitMonad { // Constants, inputs, and challenges are of degree <= 1, which is not high. // Addition and subtraction don't affect the degree. Hence, they are uninteresting. if let BinaryOperation(BinOp::Mul, lhs, rhs) = &node.expression { - if lhs.as_ref().borrow().degree() <= target_degree { - barely_low_degree_nodes.push(lhs.clone()); + if lhs.borrow().degree() <= target_degree { + barely_low_degree_nodes.push(lhs.borrow().clone()); } - if rhs.as_ref().borrow().degree() <= target_degree { - barely_low_degree_nodes.push(rhs.clone()); + if rhs.borrow().degree() <= target_degree { + barely_low_degree_nodes.push(rhs.borrow().clone()); } } } + // Collect all the nodes where some substitution is necessary. // Substituting a node of degree 1 is both pointless and can lead to infinite iteration. - barely_low_degree_nodes.retain(|node| node.as_ref().borrow().degree() > 1); - let max_degree = barely_low_degree_nodes - .iter() - .map(|node| node.as_ref().borrow().degree()) - .max() - .unwrap_or(-1); - barely_low_degree_nodes.retain(|node| node.as_ref().borrow().degree() == max_degree); - let barely_low_degree_nodes = barely_low_degree_nodes; + let low_degree_nodes = Self::all_nodes_in_multicircuit(&barely_low_degree_nodes) + .into_iter() + .filter(|node| node.degree() > 1) + .collect_vec(); // If the resulting list is empty, there is no way forward. Stop – panic time! assert!( - !barely_low_degree_nodes.is_empty(), + !low_degree_nodes.is_empty(), "Could not lower degree of circuit to target degree. This is a bug." ); - // Of the remaining nodes, pick the one occurring the most often. - let mut occurrences = HashMap::new(); - for node in barely_low_degree_nodes.iter() { - let node_id = node.as_ref().borrow().id; - *occurrences.entry(node_id).or_insert(0) += 1; + // Of the remaining nodes, keep the ones occurring the most often. + let mut nodes_and_occurrences = HashMap::new(); + for node in low_degree_nodes.iter() { + *nodes_and_occurrences.entry(node).or_insert(0) += 1; } - let (&chosen_node_id, _) = occurrences.iter().max_by_key(|(_, &count)| count).unwrap(); - barely_low_degree_nodes - .into_iter() - .find(|node| node.as_ref().borrow().id == chosen_node_id) - .unwrap() + let max_occurrences = nodes_and_occurrences + .iter() + .map(|(_, &count)| count) + .max() + .unwrap(); + nodes_and_occurrences.retain(|_, &mut count| count == max_occurrences); + let mut candidate_nodes = nodes_and_occurrences.keys().cloned().collect_vec(); + + let max_degree = candidate_nodes + .iter() + .map(|node| node.degree()) + .max() + .unwrap(); + candidate_nodes.retain(|node| node.degree() == max_degree); + + // If there are still multiple nodes, pick any. + candidate_nodes[0].id } /// Returns all nodes used in the multicircuit. - /// This is distinct from [`ConstraintCircuitBuilder::all_nodes`] because it only considers - /// nodes actually used in the given multicircuit, not all nodes in the builder. + /// This is distinct from [`ConstraintCircuitBuilder::all_nodes`] because it + /// 1. only considers nodes used in the given multicircuit, not all nodes in the builder, + /// 2. returns the nodes as [`ConstraintCircuit`]s, not as [`ConstraintCircuitMonad`]s, and + /// 3. keeps duplicates, allowing to count how often a node occurs. pub fn all_nodes_in_multicircuit( - multicircuit: &[ConstraintCircuitMonad], - ) -> HashSet> { - let mut all_nodes = HashSet::new(); + multicircuit: &[ConstraintCircuit], + ) -> Vec> { + let mut all_nodes = vec![]; for circuit in multicircuit.iter() { - let constraint_circuit = circuit.circuit.as_ref().borrow().clone(); - let nodes_in_circuit = Self::all_nodes_in_circuit(constraint_circuit); + let nodes_in_circuit = Self::all_nodes_in_circuit(circuit); all_nodes.extend(nodes_in_circuit); } all_nodes } /// Internal helper function to recursively find all nodes in a circuit. - fn all_nodes_in_circuit(circuit: ConstraintCircuit) -> HashSet> { - let mut all_nodes = HashSet::new(); + fn all_nodes_in_circuit(circuit: &ConstraintCircuit) -> Vec> { + let mut all_nodes = vec![]; if let BinaryOperation(_, lhs, rhs) = circuit.expression.clone() { - let lhs_nodes = Self::all_nodes_in_circuit(lhs.as_ref().borrow().clone()); - let rhs_nodes = Self::all_nodes_in_circuit(rhs.as_ref().borrow().clone()); + let lhs_nodes = Self::all_nodes_in_circuit(&lhs.borrow()); + let rhs_nodes = Self::all_nodes_in_circuit(&rhs.borrow()); all_nodes.extend(lhs_nodes); all_nodes.extend(rhs_nodes); }; - all_nodes.insert(circuit); + all_nodes.push(circuit.to_owned()); all_nodes } @@ -1053,7 +1059,7 @@ impl ConstraintCircuitMonad { fn multicircuit_degree(multicircuit: &[ConstraintCircuitMonad]) -> Degree { multicircuit .iter() - .map(|circuit| circuit.circuit.as_ref().borrow().degree()) + .map(|circuit| circuit.circuit.borrow().degree()) .max() .unwrap_or(-1) } @@ -1907,7 +1913,7 @@ mod constraint_circuit_tests { num_ext_cols, ); - assert!(new_base_constraints.len() <= 4); + assert!(new_base_constraints.len() <= 3); assert!(new_ext_constraints.is_empty()); } From 9de20c4b102a2be529d37080e40408814df79d6d Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Wed, 31 May 2023 09:52:05 +0200 Subject: [PATCH 59/72] add challenges for filling deterministic extension columns --- constraint-evaluation-generator/src/main.rs | 9 +++++++++ triton-vm/src/table/degree_lowering_table.rs | 3 +++ triton-vm/src/table/master_table.rs | 1 + 3 files changed, 13 insertions(+) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 44cd99a9..e637fa1e 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -662,6 +662,12 @@ fn generate_degree_lowering_table_code( true => quote!(), false => quote!(#[repr(usize)]), }; + let use_challenge_ids = match num_new_ext_cols == 0 { + true => quote!(), + false => quote!( + use crate::table::challenges::ChallengeId::*; + ), + }; let base_columns = (0..num_new_base_cols) .map(|i| format_ident!("DegreeLoweringBaseCol{i}")) @@ -696,6 +702,8 @@ fn generate_degree_lowering_table_code( use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::x_field_element::XFieldElement; + #use_challenge_ids + use crate::table::challenges::Challenges; use crate::table::master_table::NUM_BASE_COLUMNS; use crate::table::master_table::NUM_EXT_COLUMNS; @@ -932,6 +940,7 @@ fn generate_fill_ext_columns_code( pub fn fill_deterministic_ext_columns( master_base_table: ArrayView2, master_ext_table: &mut ArrayViewMut2, + challenges: &Challenges, ) { assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); assert_eq!(NUM_EXT_COLUMNS, master_ext_table.ncols()); diff --git a/triton-vm/src/table/degree_lowering_table.rs b/triton-vm/src/table/degree_lowering_table.rs index b3b3d813..054e9b99 100644 --- a/triton-vm/src/table/degree_lowering_table.rs +++ b/triton-vm/src/table/degree_lowering_table.rs @@ -7,6 +7,8 @@ use strum_macros::EnumIter; use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::x_field_element::XFieldElement; +use crate::table::challenges::Challenges; + pub const BASE_WIDTH: usize = DegreeLoweringBaseTableColumn::COUNT; pub const EXT_WIDTH: usize = DegreeLoweringExtTableColumn::COUNT; pub const FULL_WIDTH: usize = BASE_WIDTH + EXT_WIDTH; @@ -31,6 +33,7 @@ impl DegreeLoweringTable { pub fn fill_deterministic_ext_columns( _master_base_table: ArrayView2, _master_ext_table: &mut ArrayViewMut2, + _challenges: &Challenges, ) { // to be filled by generated code } diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index 3f586340..78d71596 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -606,6 +606,7 @@ impl MasterBaseTable { &mut master_ext_table .master_ext_matrix .slice_mut(s![.., 0..NUM_EXT_COLUMNS]), + challenges, ); master_ext_table From 8e459b78b168850826382a86b3a3a86c14247e9e Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 1 Jun 2023 09:35:02 +0200 Subject: [PATCH 60/72] clear up identifiers for current and next row indices --- constraint-evaluation-generator/src/main.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index e637fa1e..e9dad2a3 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -807,10 +807,11 @@ fn generate_fill_base_columns_code( return quote!(); } quote!( - for row_idx in 0..master_base_table.nrows() - 1 { + for curr_row_idx in 0..master_base_table.nrows() - 1 { + let next_row_idx = curr_row_idx + 1; let (mut curr_base_row, next_base_row) = master_base_table.multi_slice_mut(( - s![row_idx..row_idx + 1, ..], - s![row_idx + 1..row_idx + 2, ..], + s![curr_row_idx..curr_row_idx + 1, ..], + s![next_row_idx..next_row_idx + 1, ..], )); let mut curr_base_row = curr_base_row.row_mut(0); let next_base_row = next_base_row.row(0); @@ -912,12 +913,13 @@ fn generate_fill_ext_columns_code( return quote!(); } quote!( - for row_idx in 0..master_base_table.nrows() - 1 { - let current_base_row = master_base_table.row(row_idx); - let next_base_row = master_base_table.row(row_idx + 1); + for curr_row_idx in 0..master_base_table.nrows() - 1 { + let next_row_idx = curr_row_idx + 1; + let current_base_row = master_base_table.row(curr_row_idx); + let next_base_row = master_base_table.row(next_row_idx); let (mut curr_ext_row, next_ext_row) = master_ext_table.multi_slice_mut(( - s![row_idx..row_idx + 1, ..], - s![row_idx + 1..row_idx + 2, ..], + s![curr_row_idx..curr_row_idx + 1, ..], + s![next_row_idx..next_row_idx + 1, ..], )); let mut curr_ext_row = curr_ext_row.row_mut(0); let next_ext_row = next_ext_row.row(0); From eecc63326d90b14216dc2a8bbfc5a23594f67680 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Thu, 1 Jun 2023 10:22:31 +0200 Subject: [PATCH 61/72] add test to check evaluation to zero also for derived constraints --- triton-vm/src/stark.rs | 81 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/triton-vm/src/stark.rs b/triton-vm/src/stark.rs index 428fc4f7..79735e04 100644 --- a/triton-vm/src/stark.rs +++ b/triton-vm/src/stark.rs @@ -1512,6 +1512,87 @@ pub(crate) mod triton_stark_tests { )); } + #[test] + fn derived_constraints_evaluate_to_zero_on_halt_test() { + derived_constraints_evaluate_to_zero(test_halt()); + } + + pub fn derived_constraints_evaluate_to_zero(source_code_and_input: SourceCodeAndInput) { + let (_, _, _, master_base_table, master_ext_table, challenges) = parse_simulate_pad_extend( + &source_code_and_input.source_code, + source_code_and_input.input, + source_code_and_input.secret_input, + ); + + let zero = XFieldElement::zero(); + let master_base_trace_table = master_base_table.trace_table(); + let master_ext_trace_table = master_ext_table.trace_table(); + + let evaluated_initial_constraints = MasterExtTable::evaluate_initial_constraints( + master_base_trace_table.row(0), + master_ext_trace_table.row(0), + &challenges, + ); + for (constraint_idx, evaluated_constraint) in + evaluated_initial_constraints.into_iter().enumerate() + { + assert_eq!( + zero, evaluated_constraint, + "Initial constraint {constraint_idx} failed.", + ); + } + + for row_idx in 0..master_base_trace_table.nrows() { + let evaluated_consistency_constraints = + MasterExtTable::evaluate_consistency_constraints( + master_base_trace_table.row(row_idx), + master_ext_trace_table.row(row_idx), + &challenges, + ); + for (constraint_idx, evaluated_constraint) in + evaluated_consistency_constraints.into_iter().enumerate() + { + assert_eq!( + zero, evaluated_constraint, + "Consistency constraint {constraint_idx} failed in row {row_idx}.", + ); + } + } + + for curr_row_idx in 0..master_base_trace_table.nrows() - 1 { + let next_row_idx = curr_row_idx + 1; + let evaluated_transition_constraints = MasterExtTable::evaluate_transition_constraints( + master_base_trace_table.row(curr_row_idx), + master_ext_trace_table.row(curr_row_idx), + master_base_trace_table.row(next_row_idx), + master_ext_trace_table.row(next_row_idx), + &challenges, + ); + for (constraint_idx, evaluated_constraint) in + evaluated_transition_constraints.into_iter().enumerate() + { + assert_eq!( + zero, evaluated_constraint, + "Transition constraint {constraint_idx} failed in row {curr_row_idx}.", + ); + } + } + + let evaluated_terminal_constraints = MasterExtTable::evaluate_terminal_constraints( + master_base_trace_table.row(master_base_trace_table.nrows() - 1), + master_ext_trace_table.row(master_ext_trace_table.nrows() - 1), + &challenges, + ); + for (constraint_idx, evaluated_constraint) in + evaluated_terminal_constraints.into_iter().enumerate() + { + assert_eq!( + zero, evaluated_constraint, + "Terminal constraint {constraint_idx} failed.", + ); + } + } + #[test] fn triton_prove_verify_simple_program_test() { let code_with_input = test_hash_nop_nop_lt(); From f7420ec0b42f225850b17dc563b682c7f2a3feac Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Fri, 2 Jun 2023 11:48:54 +0200 Subject: [PATCH 62/72] fill derived columns only on trace rows --- triton-vm/src/table/master_table.rs | 34 ++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index 78d71596..e8e982e2 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -196,10 +196,14 @@ where fn randomized_padded_trace_len(&self) -> usize; fn rand_trace_to_padded_trace_unit_distance(&self) -> usize; - /// Presents underlying trace data, excluding trace randomizers. Makes little sense over the - /// FRI domain. + /// Presents underlying trace data, excluding trace randomizers and randomizer polynomials. + /// Makes little sense over the FRI domain. fn trace_table(&self) -> ArrayView2; + /// Mutably presents underlying trace data, excluding trace randomizers and randomizer + /// polynomials. Makes little sense over the FRI domain. + fn trace_table_mut(&mut self) -> ArrayViewMut2; + /// Presents all underlying data. fn master_matrix(&self) -> ArrayView2; @@ -301,6 +305,11 @@ impl MasterTable for MasterBaseTable { .slice(s![..; self.rand_trace_to_padded_trace_unit_distance, ..]) } + fn trace_table_mut(&mut self) -> ArrayViewMut2 { + self.master_base_matrix + .slice_mut(s![..; self.rand_trace_to_padded_trace_unit_distance, ..]) + } + fn master_matrix(&self) -> ArrayView2 { self.master_base_matrix.view() } @@ -325,7 +334,12 @@ impl MasterTable for MasterExtTable { fn trace_table(&self) -> ArrayView2 { self.master_ext_matrix - .slice(s![..; self.rand_trace_to_padded_trace_unit_distance, ..]) + .slice(s![..; self.rand_trace_to_padded_trace_unit_distance, ..NUM_EXT_COLUMNS]) + } + + fn trace_table_mut(&mut self) -> ArrayViewMut2 { + self.master_ext_matrix + .slice_mut(s![..; self.rand_trace_to_padded_trace_unit_distance, ..NUM_EXT_COLUMNS]) } fn master_matrix(&self) -> ArrayView2 { @@ -489,9 +503,7 @@ impl MasterBaseTable { let u32_table = &mut self.table_mut(TableId::U32Table); U32Table::pad_trace(u32_table, u32_table_len); - DegreeLoweringTable::fill_deterministic_base_columns( - &mut self.master_base_matrix.view_mut(), - ); + DegreeLoweringTable::fill_deterministic_base_columns(&mut self.trace_table_mut()); } /// Returns the low-degree extended columns as well as the columns' interpolation polynomials. @@ -602,10 +614,8 @@ impl MasterBaseTable { ); DegreeLoweringTable::fill_deterministic_ext_columns( - self.master_base_matrix.view(), - &mut master_ext_table - .master_ext_matrix - .slice_mut(s![.., 0..NUM_EXT_COLUMNS]), + self.trace_table(), + &mut master_ext_table.trace_table_mut(), challenges, ); @@ -628,6 +638,7 @@ impl MasterBaseTable { } } + /// Returns a view of the specified table, excluding the trace randomizers. pub fn table(&self, id: TableId) -> ArrayView2 { let (table_start, table_end) = Self::table_slice_info(id); let unit_distance = self.rand_trace_to_padded_trace_unit_distance; @@ -635,6 +646,7 @@ impl MasterBaseTable { .slice(s![..; unit_distance, table_start..table_end]) } + /// Returns a mutable view of the specified table, excluding the trace randomizers. pub fn table_mut(&mut self, id: TableId) -> ArrayViewMut2 { let (table_start, table_end) = Self::table_slice_info(id); let unit_distance = self.rand_trace_to_padded_trace_unit_distance; @@ -710,6 +722,7 @@ impl MasterExtTable { } } + /// Returns a view of the entire master table, excluding the trace randomizers. pub fn table(&self, id: TableId) -> ArrayView2 { let unit_distance = self.rand_trace_to_padded_trace_unit_distance; let (table_start, table_end) = Self::table_slice_info(id); @@ -717,6 +730,7 @@ impl MasterExtTable { .slice(s![..; unit_distance, table_start..table_end]) } + /// Returns a mutable view of the specified table, excluding the trace randomizers. pub fn table_mut(&mut self, id: TableId) -> ArrayViewMut2 { let unit_distance = self.rand_trace_to_padded_trace_unit_distance; let (table_start, table_end) = Self::table_slice_info(id); From b553924219b28b19b18dd9352fc597d73ca72849 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Fri, 2 Jun 2023 12:40:12 +0200 Subject: [PATCH 63/72] rename `deterministic` columns to `derived` columns --- constraint-evaluation-generator/src/main.rs | 26 +++++++++----------- triton-vm/src/table/degree_lowering_table.rs | 4 +-- triton-vm/src/table/master_table.rs | 4 +-- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index e9dad2a3..2859fec4 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -743,8 +743,7 @@ fn generate_fill_base_columns_code( tran_substitutions: &[ConstraintCircuitMonad], term_substitutions: &[ConstraintCircuitMonad], ) -> TokenStream { - let deterministic_section_start = - master_table::NUM_BASE_COLUMNS - degree_lowering_table::BASE_WIDTH; + let derived_section_start = master_table::NUM_BASE_COLUMNS - degree_lowering_table::BASE_WIDTH; let num_init_substitutions = init_substitutions.len(); let num_cons_substitutions = cons_substitutions.len(); @@ -752,17 +751,17 @@ fn generate_fill_base_columns_code( let num_term_substitutions = term_substitutions.len(); let init_col_indices = (0..num_init_substitutions) - .map(|i| i + deterministic_section_start) + .map(|i| i + derived_section_start) .collect_vec(); let cons_col_indices = (0..num_cons_substitutions) - .map(|i| i + deterministic_section_start + num_init_substitutions) + .map(|i| i + derived_section_start + num_init_substitutions) .collect_vec(); let tran_col_indices = (0..num_tran_substitutions) - .map(|i| i + deterministic_section_start + num_init_substitutions + num_cons_substitutions) + .map(|i| i + derived_section_start + num_init_substitutions + num_cons_substitutions) .collect_vec(); let term_col_indices = (0..num_term_substitutions) .map(|i| { - i + deterministic_section_start + i + derived_section_start + num_init_substitutions + num_cons_substitutions + num_tran_substitutions @@ -831,7 +830,7 @@ fn generate_fill_base_columns_code( quote!( #[allow(unused_variables)] - pub fn fill_deterministic_base_columns(master_base_table: &mut ArrayViewMut2) { + pub fn fill_derived_base_columns(master_base_table: &mut ArrayViewMut2) { assert_eq!(NUM_BASE_COLUMNS, master_base_table.ncols()); #init_substitutions #cons_substitutions @@ -847,8 +846,7 @@ fn generate_fill_ext_columns_code( tran_substitutions: &[ConstraintCircuitMonad], term_substitutions: &[ConstraintCircuitMonad], ) -> TokenStream { - let deterministic_section_start = - master_table::NUM_EXT_COLUMNS - degree_lowering_table::EXT_WIDTH; + let derived_section_start = master_table::NUM_EXT_COLUMNS - degree_lowering_table::EXT_WIDTH; let num_init_substitutions = init_substitutions.len(); let num_cons_substitutions = cons_substitutions.len(); @@ -856,17 +854,17 @@ fn generate_fill_ext_columns_code( let num_term_substitutions = term_substitutions.len(); let init_col_indices = (0..num_init_substitutions) - .map(|i| i + deterministic_section_start) + .map(|i| i + derived_section_start) .collect_vec(); let cons_col_indices = (0..num_cons_substitutions) - .map(|i| i + deterministic_section_start + num_init_substitutions) + .map(|i| i + derived_section_start + num_init_substitutions) .collect_vec(); let tran_col_indices = (0..num_tran_substitutions) - .map(|i| i + deterministic_section_start + num_init_substitutions + num_cons_substitutions) + .map(|i| i + derived_section_start + num_init_substitutions + num_cons_substitutions) .collect_vec(); let term_col_indices = (0..num_term_substitutions) .map(|i| { - i + deterministic_section_start + i + derived_section_start + num_init_substitutions + num_cons_substitutions + num_tran_substitutions @@ -939,7 +937,7 @@ fn generate_fill_ext_columns_code( quote!( #[allow(unused_variables)] - pub fn fill_deterministic_ext_columns( + pub fn fill_derived_ext_columns( master_base_table: ArrayView2, master_ext_table: &mut ArrayViewMut2, challenges: &Challenges, diff --git a/triton-vm/src/table/degree_lowering_table.rs b/triton-vm/src/table/degree_lowering_table.rs index 054e9b99..33b5a50f 100644 --- a/triton-vm/src/table/degree_lowering_table.rs +++ b/triton-vm/src/table/degree_lowering_table.rs @@ -26,11 +26,11 @@ pub enum DegreeLoweringExtTableColumn {} pub struct DegreeLoweringTable {} impl DegreeLoweringTable { - pub fn fill_deterministic_base_columns(_master_base_table: &mut ArrayViewMut2) { + pub fn fill_derived_base_columns(_master_base_table: &mut ArrayViewMut2) { // to be filled by generated code } - pub fn fill_deterministic_ext_columns( + pub fn fill_derived_ext_columns( _master_base_table: ArrayView2, _master_ext_table: &mut ArrayViewMut2, _challenges: &Challenges, diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index e8e982e2..4d5a37a3 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -503,7 +503,7 @@ impl MasterBaseTable { let u32_table = &mut self.table_mut(TableId::U32Table); U32Table::pad_trace(u32_table, u32_table_len); - DegreeLoweringTable::fill_deterministic_base_columns(&mut self.trace_table_mut()); + DegreeLoweringTable::fill_derived_base_columns(&mut self.trace_table_mut()); } /// Returns the low-degree extended columns as well as the columns' interpolation polynomials. @@ -613,7 +613,7 @@ impl MasterBaseTable { challenges, ); - DegreeLoweringTable::fill_deterministic_ext_columns( + DegreeLoweringTable::fill_derived_ext_columns( self.trace_table(), &mut master_ext_table.trace_table_mut(), challenges, From fb28b3f913b0ea62607535f3c79f79f1f3b7348c Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Fri, 2 Jun 2023 13:26:28 +0200 Subject: [PATCH 64/72] have `rustfmt` ignore generated files --- triton-vm/src/table.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/triton-vm/src/table.rs b/triton-vm/src/table.rs index 17db6642..af3e7af2 100644 --- a/triton-vm/src/table.rs +++ b/triton-vm/src/table.rs @@ -1,8 +1,10 @@ pub mod cascade_table; pub mod challenges; pub mod constraint_circuit; +#[rustfmt::skip] pub mod constraints; pub mod cross_table_argument; +#[rustfmt::skip] pub mod degree_lowering_table; pub mod extension_table; pub mod hash_table; From 51ee9cea6c0de1369e63b136808a6abf4a2bfd43 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Fri, 2 Jun 2023 13:39:32 +0200 Subject: [PATCH 65/72] add documentation to `AIR_TARGET_DEGREE` and the degree lowering table --- constraint-evaluation-generator/src/main.rs | 13 +++++++++---- triton-vm/src/table/master_table.rs | 21 ++++++++++++++++++++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/constraint-evaluation-generator/src/main.rs b/constraint-evaluation-generator/src/main.rs index 2859fec4..9b44c74d 100644 --- a/constraint-evaluation-generator/src/main.rs +++ b/constraint-evaluation-generator/src/main.rs @@ -692,6 +692,15 @@ fn generate_degree_lowering_table_code( ); quote!( + //! The degree lowering table contains the introduced variables that allow + //! lowering the degree of the AIR. See + //! [`master_table::AIR_TARGET_DEGREE`] + //! for additional information. + //! + //! This file has been auto-generated. Any modifications _will_ be lost. + //! To re-generate, execute: + //! `cargo run --bin constraint-evaluation-generator` + use ndarray::s; use ndarray::ArrayView2; use ndarray::ArrayViewMut2; @@ -711,10 +720,6 @@ fn generate_degree_lowering_table_code( pub const EXT_WIDTH: usize = DegreeLoweringExtTableColumn::COUNT; pub const FULL_WIDTH: usize = BASE_WIDTH + EXT_WIDTH; - // This file has been auto-generated. Any modifications _will_ be lost. - // To re-generate, execute: - // `cargo run --bin constraint-evaluation-generator` - #base_repr_usize #[derive(Display, Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCountMacro, Hash)] pub enum DegreeLoweringBaseTableColumn { diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index 4d5a37a3..ef9f4cb0 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -56,7 +56,26 @@ use crate::table::*; use crate::vm::AlgebraicExecutionTrace; /// The degree of the AIR after the degree lowering step. -/// See also [`DegreeLoweringTable`]. +/// +/// Using substitution and the introduction of new variables, the degree of the AIR as specified +/// in the respective tables +/// (e.g., in [`processor_table::ExtProcessorTable::transition_constraints`]) +/// is lowered to this value. +/// For example, with a target degree of 2 and a (fictional) constraint of the form +/// `a = b²·c²·d`, +/// the degree lowering step could (as one among multiple possibilities) +/// - introduce new variables `e`, `f`, and `g`, +/// - introduce new constraints `e = b²`, `f = c²`, and `g = e·f`, +/// - replace the original constraint with `a = g·d`. +/// +/// The degree lowering happens in the constraint evaluation generator. +/// It can be executed by running `cargo run --bin constraint-evaluation-generator`. +/// Executing the constraint evaluator is a prerequisite for running both the Stark prover +/// and the Stark verifier. +/// +/// The new variables introduced by the degree lowering step are called “derived columns.” +/// They are added to the [`DegreeLoweringTable`], whose sole purpose is to store the values +/// of these derived columns. pub const AIR_TARGET_DEGREE: Degree = 5; /// The total number of base columns across all tables. From e6b0f3a41b8e339c0478921997313aa078f5992a Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Mon, 5 Jun 2023 14:21:38 +0200 Subject: [PATCH 66/72] add test for counting the number of equal nodes in a multicircuit --- triton-vm/src/table/constraint_circuit.rs | 65 +++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 92039668..654078c4 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -2416,4 +2416,69 @@ mod constraint_circuit_tests { _ => (), }; } + + #[test] + pub fn all_nodes_in_multicircuit_are_identified_correctly() { + let builder = ConstraintCircuitBuilder::new(); + + let x = |i| builder.input(BaseRow(i)); + let b_con = |i: u64| builder.b_constant(i.into()); + + let sub_tree_0 = x(0) * x(1) * (x(2) - b_con(1)) * x(3) * x(4); + let sub_tree_1 = x(0) * x(1) * (x(2) - b_con(1)) * x(3) * x(5); + let sub_tree_2 = x(10) * x(10) * x(2) * x(13); + let sub_tree_3 = x(10) * x(10) * x(2) * x(14); + + let circuit_0 = sub_tree_0.clone() + sub_tree_1.clone(); + let circuit_1 = sub_tree_2.clone() + sub_tree_3.clone(); + let circuit_2 = sub_tree_0 + sub_tree_2; + let circuit_3 = sub_tree_1 + sub_tree_3; + + let multicircuit = [circuit_0, circuit_1, circuit_2, circuit_3].map(|c| c.consume()); + + let all_nodes = ConstraintCircuitMonad::all_nodes_in_multicircuit(&multicircuit); + + let x0 = x(0).consume(); + let x0_count = all_nodes.iter().filter(|&node| node == &x0).count(); + assert_eq!(4, x0_count); + + let x2 = x(2).consume(); + let x2_count = all_nodes.iter().filter(|&node| node == &x2).count(); + assert_eq!(8, x2_count); + + let x10 = x(10).consume(); + let x10_count = all_nodes.iter().filter(|&node| node == &x10).count(); + assert_eq!(8, x10_count); + + let x4 = x(4).consume(); + let x4_count = all_nodes.iter().filter(|&node| node == &x4).count(); + assert_eq!(2, x4_count); + + let x6 = x(6).consume(); + let x6_count = all_nodes.iter().filter(|&node| node == &x6).count(); + assert_eq!(0, x6_count); + + let x0_x1 = (x(0) * x(1)).consume(); + let x0_x1_count = all_nodes.iter().filter(|&node| node == &x0_x1).count(); + assert_eq!(4, x0_x1_count); + + let tree = (x(0) * x(1) * (x(2) - b_con(1))).consume(); + let tree_count = all_nodes.iter().filter(|&node| node == &tree).count(); + assert_eq!(4, tree_count); + + let max_occurences = all_nodes + .iter() + .map(|node| all_nodes.iter().filter(|&n| n == node).count()) + .max() + .unwrap(); + assert_eq!(8, max_occurences); + + let most_frequent_nodes = all_nodes + .iter() + .filter(|&node| all_nodes.iter().filter(|&n| n == node).count() == max_occurences) + .collect::>(); + assert_eq!(2, most_frequent_nodes.len()); + assert!(most_frequent_nodes.contains(&&x2)); + assert!(most_frequent_nodes.contains(&&x10)); + } } From ab66c7498a3db43e1f8213b16f94b86962503b7d Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Mon, 5 Jun 2023 15:04:43 +0200 Subject: [PATCH 67/72] use better heuristic for node substitution --- triton-vm/src/table/constraint_circuit.rs | 28 +++++++---------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 654078c4..91eb4502 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -963,36 +963,24 @@ impl ConstraintCircuitMonad { panic!("Multicircuit must be non-empty in order to pick a node from it."); } - // Only nodes with degree > target_degree need changing. let multicircuit = multicircuit .iter() .map(|c| c.clone().consume()) .collect_vec(); let all_nodes = Self::all_nodes_in_multicircuit(&multicircuit); - let all_nodes: HashSet<_> = HashSet::from_iter(all_nodes.iter()); + let all_nodes: HashSet<_> = HashSet::from_iter(all_nodes.into_iter()); + + // Only nodes with degree > target_degree need changing. let high_degree_nodes = all_nodes - .iter() - .filter(|node| node.degree() > target_degree); - - // Of those nodes, get all the children with degree <= target_degree. - let mut barely_low_degree_nodes = vec![]; - for node in high_degree_nodes { - // Constants, inputs, and challenges are of degree <= 1, which is not high. - // Addition and subtraction don't affect the degree. Hence, they are uninteresting. - if let BinaryOperation(BinOp::Mul, lhs, rhs) = &node.expression { - if lhs.borrow().degree() <= target_degree { - barely_low_degree_nodes.push(lhs.borrow().clone()); - } - if rhs.borrow().degree() <= target_degree { - barely_low_degree_nodes.push(rhs.borrow().clone()); - } - } - } + .into_iter() + .filter(|node| node.degree() > target_degree) + .collect_vec(); // Collect all the nodes where some substitution is necessary. // Substituting a node of degree 1 is both pointless and can lead to infinite iteration. - let low_degree_nodes = Self::all_nodes_in_multicircuit(&barely_low_degree_nodes) + let low_degree_nodes = Self::all_nodes_in_multicircuit(&high_degree_nodes) .into_iter() + .filter(|node| node.degree() <= target_degree) .filter(|node| node.degree() > 1) .collect_vec(); From e5faf354d7b7f64865a8c6b97a5595423d39f963 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Mon, 5 Jun 2023 16:55:33 +0200 Subject: [PATCH 68/72] de-duplicate code in multicircuit node counting test --- triton-vm/src/table/constraint_circuit.rs | 28 +++++++++-------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 91eb4502..a24a782a 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -2406,7 +2406,7 @@ mod constraint_circuit_tests { } #[test] - pub fn all_nodes_in_multicircuit_are_identified_correctly() { + fn all_nodes_in_multicircuit_are_identified_correctly() { let builder = ConstraintCircuitBuilder::new(); let x = |i| builder.input(BaseRow(i)); @@ -2425,34 +2425,28 @@ mod constraint_circuit_tests { let multicircuit = [circuit_0, circuit_1, circuit_2, circuit_3].map(|c| c.consume()); let all_nodes = ConstraintCircuitMonad::all_nodes_in_multicircuit(&multicircuit); + let count_node = |node| all_nodes.iter().filter(|&n| n == &node).count(); let x0 = x(0).consume(); - let x0_count = all_nodes.iter().filter(|&node| node == &x0).count(); - assert_eq!(4, x0_count); + assert_eq!(4, count_node(x0)); let x2 = x(2).consume(); - let x2_count = all_nodes.iter().filter(|&node| node == &x2).count(); - assert_eq!(8, x2_count); + assert_eq!(8, count_node(x2)); let x10 = x(10).consume(); - let x10_count = all_nodes.iter().filter(|&node| node == &x10).count(); - assert_eq!(8, x10_count); + assert_eq!(8, count_node(x10)); let x4 = x(4).consume(); - let x4_count = all_nodes.iter().filter(|&node| node == &x4).count(); - assert_eq!(2, x4_count); + assert_eq!(2, count_node(x4)); let x6 = x(6).consume(); - let x6_count = all_nodes.iter().filter(|&node| node == &x6).count(); - assert_eq!(0, x6_count); + assert_eq!(0, count_node(x6)); let x0_x1 = (x(0) * x(1)).consume(); - let x0_x1_count = all_nodes.iter().filter(|&node| node == &x0_x1).count(); - assert_eq!(4, x0_x1_count); + assert_eq!(4, count_node(x0_x1)); let tree = (x(0) * x(1) * (x(2) - b_con(1))).consume(); - let tree_count = all_nodes.iter().filter(|&node| node == &tree).count(); - assert_eq!(4, tree_count); + assert_eq!(4, count_node(tree)); let max_occurences = all_nodes .iter() @@ -2466,7 +2460,7 @@ mod constraint_circuit_tests { .filter(|&node| all_nodes.iter().filter(|&n| n == node).count() == max_occurences) .collect::>(); assert_eq!(2, most_frequent_nodes.len()); - assert!(most_frequent_nodes.contains(&&x2)); - assert!(most_frequent_nodes.contains(&&x10)); + assert!(most_frequent_nodes.contains(&x(2).consume())); + assert!(most_frequent_nodes.contains(&x(10).consume())); } } From a5d5ccd1618058f4f477354814bf9eb52c24895c Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Mon, 5 Jun 2023 16:56:12 +0200 Subject: [PATCH 69/72] target an AIR degree of 4 This gives the best raw performance. --- triton-vm/src/table/master_table.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index ef9f4cb0..e2ea1e69 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -76,7 +76,7 @@ use crate::vm::AlgebraicExecutionTrace; /// The new variables introduced by the degree lowering step are called “derived columns.” /// They are added to the [`DegreeLoweringTable`], whose sole purpose is to store the values /// of these derived columns. -pub const AIR_TARGET_DEGREE: Degree = 5; +pub const AIR_TARGET_DEGREE: Degree = 4; /// The total number of base columns across all tables. pub const NUM_BASE_COLUMNS: usize = program_table::BASE_WIDTH From 271110febc2e63f068eb97586c1672dcf5698547 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Mon, 5 Jun 2023 17:39:59 +0200 Subject: [PATCH 70/72] test uniqueness of substitution rules --- triton-vm/src/table/constraint_circuit.rs | 37 +++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index a24a782a..7e9f0ab6 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -1178,6 +1178,7 @@ mod constraint_circuit_tests { use crate::table::challenges::ChallengeId::U32Indeterminate; use crate::table::challenges::Challenges; use crate::table::constraint_circuit::SingleRowIndicator::*; + use crate::table::degree_lowering_table::DegreeLoweringTable; use crate::table::hash_table::ExtHashTable; use crate::table::jump_stack_table::ExtJumpStackTable; use crate::table::lookup_table::ExtLookupTable; @@ -2463,4 +2464,40 @@ mod constraint_circuit_tests { assert!(most_frequent_nodes.contains(&x(2).consume())); assert!(most_frequent_nodes.contains(&x(10).consume())); } + + /// Fills the derived columns of the degree-lowering table using randomly generated rows and + /// checks the resulting values for uniqueness. The described method corresponds to an + /// application of the Schwartz-Zippel lemma to check uniqueness of the substitution rules + /// generated during degree lowering. + #[test] + #[ignore = "(probably) requires normalization of circuit expressions"] + fn substitution_rules_are_unique() { + let challenges = Challenges::placeholder(&[], &[]); + let mut base_table_rows = Array2::from_shape_fn((2, NUM_BASE_COLUMNS), |_| random()); + let mut ext_table_rows = Array2::from_shape_fn((2, NUM_EXT_COLUMNS), |_| random()); + + DegreeLoweringTable::fill_derived_base_columns(&mut base_table_rows.view_mut()); + DegreeLoweringTable::fill_derived_ext_columns( + base_table_rows.view(), + &mut ext_table_rows.view_mut(), + &challenges, + ); + + let mut encountered_values = HashMap::new(); + for col_idx in 0..NUM_BASE_COLUMNS { + let val = base_table_rows[(0, col_idx)].lift(); + let other_entry = encountered_values.insert(val, col_idx); + if let Some(other_idx) = other_entry { + panic!("Duplicate value {val} in derived base column {other_idx} and {col_idx}."); + } + } + println!("Now comparing extension columns…"); + for col_idx in 0..NUM_EXT_COLUMNS { + let val = ext_table_rows[(0, col_idx)]; + let other_entry = encountered_values.insert(val, col_idx); + if let Some(other_idx) = other_entry { + panic!("Duplicate value {val} in derived ext column {other_idx} and {col_idx}."); + } + } + } } From 72b02441d9af9c5feba2811a89ac97a979a030ca Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 6 Jun 2023 14:51:42 +0200 Subject: [PATCH 71/72] construct new input indicator with less convolution --- triton-vm/src/table/constraint_circuit.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 7e9f0ab6..1b3d8acb 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -921,13 +921,12 @@ impl ConstraintCircuitMonad { // Create a new variable. let chosen_node = builder.get_node_by_id(chosen_node_id).unwrap(); let chosen_node_is_base_col = chosen_node.circuit.borrow().evaluates_to_base_element(); - let new_col_idx = match chosen_node_is_base_col { - true => num_base_cols + base_constraints.len(), - false => num_ext_cols + ext_constraints.len(), - }; - let new_input_indicator = match chosen_node_is_base_col { - true => II::base_table_input(new_col_idx), - false => II::ext_table_input(new_col_idx), + let new_input_indicator = if chosen_node_is_base_col { + let new_base_col_idx = num_base_cols + base_constraints.len(); + II::base_table_input(new_base_col_idx) + } else { + let new_ext_col_idx = num_ext_cols + ext_constraints.len(); + II::ext_table_input(new_ext_col_idx) }; let new_variable = builder.input(new_input_indicator); let new_circuit = new_variable.circuit.clone(); From 43b27569acd0a48280ea2de34916525de131f8c0 Mon Sep 17 00:00:00 2001 From: Jan Ferdinand Sauer Date: Tue, 6 Jun 2023 15:17:22 +0200 Subject: [PATCH 72/72] improve some comments --- triton-vm/src/table/constraint_circuit.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/triton-vm/src/table/constraint_circuit.rs b/triton-vm/src/table/constraint_circuit.rs index 1b3d8acb..e3179f91 100644 --- a/triton-vm/src/table/constraint_circuit.rs +++ b/triton-vm/src/table/constraint_circuit.rs @@ -975,7 +975,8 @@ impl ConstraintCircuitMonad { .filter(|node| node.degree() > target_degree) .collect_vec(); - // Collect all the nodes where some substitution is necessary. + // Collect all candidates for substitution, i.e., descendents of high_degree_nodes + // with degree <= target_degree. // Substituting a node of degree 1 is both pointless and can lead to infinite iteration. let low_degree_nodes = Self::all_nodes_in_multicircuit(&high_degree_nodes) .into_iter() @@ -1002,6 +1003,7 @@ impl ConstraintCircuitMonad { nodes_and_occurrences.retain(|_, &mut count| count == max_occurrences); let mut candidate_nodes = nodes_and_occurrences.keys().cloned().collect_vec(); + // If there are still multiple nodes, pick the one with the highest degree. let max_degree = candidate_nodes .iter() .map(|node| node.degree())