tasm_lib/verifier/master_table/
divide_out_zerofiers.rsuse triton_vm::prelude::LabelledInstruction;
use triton_vm::prelude::*;
use triton_vm::table::master_table::MasterAuxTable;
use triton_vm::twenty_first::math::x_field_element::EXTENSION_DEGREE;
use crate::data_type::DataType;
use crate::library::Library;
use crate::traits::basic_snippet::BasicSnippet;
use crate::verifier::master_table::air_constraint_evaluation::AirConstraintEvaluation;
use crate::verifier::master_table::zerofiers_inverse::ConstraintType;
use crate::verifier::master_table::zerofiers_inverse::ZerofiersInverse;
#[derive(Debug, Clone)]
pub struct DivideOutZerofiers;
impl BasicSnippet for DivideOutZerofiers {
fn inputs(&self) -> Vec<(DataType, String)> {
vec![
(DataType::VoidPointer, "*air_evaluation_result".to_string()),
(DataType::Xfe, "out_of_domain_point_curr_row".to_owned()),
(DataType::U32, "padded_height".to_owned()),
(DataType::Bfe, "trace_domain_generator".to_owned()),
]
}
fn outputs(&self) -> Vec<(DataType, String)> {
vec![(
AirConstraintEvaluation::output_type(),
"*quotient_summands".to_owned(),
)]
}
fn entrypoint(&self) -> String {
"tasmlib_verifier_master_table_divide_out_zerofiers".to_owned()
}
fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
let entrypoint = self.entrypoint();
let zerofiers_inverse_alloc =
library.kmalloc(ZerofiersInverse::array_size().try_into().unwrap());
let zerofiers_inverse_snippet = ZerofiersInverse {
zerofiers_inverse_write_address: zerofiers_inverse_alloc.write_address(),
};
let zerofiers_inverse = library.import(Box::new(zerofiers_inverse_snippet));
let read_all_air_elements = vec![
triton_asm!(
read_mem { EXTENSION_DEGREE } );
MasterAuxTable::NUM_CONSTRAINTS
]
.concat();
let mul_and_write = |constraint_type: ConstraintType, num_constraints: usize| {
vec![
triton_asm!(
swap 3
swap 2
swap 1
push {zerofiers_inverse_snippet.zerofier_inv_read_address(constraint_type)}
read_mem {EXTENSION_DEGREE}
pop 1
xx_mul
swap 1
swap 2
swap 3
write_mem {EXTENSION_DEGREE}
);
num_constraints
]
.concat()
};
let mul_and_write = [
mul_and_write(
ConstraintType::Initial,
MasterAuxTable::NUM_INITIAL_CONSTRAINTS,
),
mul_and_write(
ConstraintType::Consistency,
MasterAuxTable::NUM_CONSISTENCY_CONSTRAINTS,
),
mul_and_write(
ConstraintType::Transition,
MasterAuxTable::NUM_TRANSITION_CONSTRAINTS,
),
mul_and_write(
ConstraintType::Terminal,
MasterAuxTable::NUM_TERMINAL_CONSTRAINTS,
),
]
.concat();
let jump_to_last_air_element = triton_asm!(
push {MasterAuxTable::NUM_CONSTRAINTS * EXTENSION_DEGREE - 1}
add
);
let jump_to_first_air_element = triton_asm!(
push {-((MasterAuxTable::NUM_CONSTRAINTS * EXTENSION_DEGREE) as i32)}
add
);
triton_asm!(
{entrypoint}:
call {zerofiers_inverse}
{&jump_to_last_air_element}
{&read_all_air_elements}
push 1 add
{&mul_and_write}
{&jump_to_first_air_element}
return
)
}
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use itertools::Itertools;
use rand::prelude::*;
use triton_vm::twenty_first::math::traits::Inverse;
use triton_vm::twenty_first::math::traits::ModPowU32;
use triton_vm::twenty_first::math::traits::PrimitiveRootOfUnity;
use super::*;
use crate::empty_stack;
use crate::execute_test;
use crate::linker::link_for_isolated_run;
#[test]
fn divide_out_zerofiers_test() {
let snippet = DivideOutZerofiers;
let mut seed: [u8; 32] = [0u8; 32];
thread_rng().fill_bytes(&mut seed);
snippet.test_equivalence_with_host_machine(seed);
}
impl DivideOutZerofiers {
fn test_equivalence_with_host_machine(&self, seed: [u8; 32]) {
let mut rng: StdRng = SeedableRng::from_seed(seed);
let (air_evaluation_result, ood_point_curr_row, padded_height, trace_domain_generator) =
Self::random_input_values(&mut rng);
let rust_result = Self::rust_result(
air_evaluation_result,
ood_point_curr_row,
padded_height,
trace_domain_generator,
);
let tasm_result = self.tasm_result(
air_evaluation_result,
ood_point_curr_row,
padded_height,
trace_domain_generator,
);
assert_eq!(tasm_result.len(), rust_result.len());
assert_eq!(
tasm_result.iter().copied().sum::<XFieldElement>(),
rust_result.iter().copied().sum::<XFieldElement>(),
"\ntasm: [{},...]\nrust: [{},...]",
tasm_result.iter().take(3).join(","),
rust_result.iter().take(3).join(",")
);
assert_eq!(tasm_result, rust_result);
}
pub(super) fn random_input_values(
rng: &mut StdRng,
) -> (
[XFieldElement; MasterAuxTable::NUM_CONSTRAINTS],
XFieldElement,
u32,
BFieldElement,
) {
let air_evaluation_result =
rng.gen::<[XFieldElement; MasterAuxTable::NUM_CONSTRAINTS]>();
let ood_point_curr_row: XFieldElement = rng.gen();
let padded_height = 2u32.pow(rng.gen_range(8..32));
let trace_domain_generator =
BFieldElement::primitive_root_of_unity(padded_height as u64).unwrap();
(
air_evaluation_result,
ood_point_curr_row,
padded_height,
trace_domain_generator,
)
}
fn tasm_result(
&self,
air_evaluation_result: [XFieldElement; MasterAuxTable::NUM_CONSTRAINTS],
out_of_domain_point_curr_row: XFieldElement,
padded_height: u32,
trace_domain_generator: BFieldElement,
) -> Vec<XFieldElement> {
let free_page_pointer = BFieldElement::new(((1u64 << 32) - 3) * (1 << 32));
let mut memory = HashMap::<BFieldElement, BFieldElement>::new();
println!(
"air evaluation result encoded: [{}, ...]",
air_evaluation_result.encode().iter().take(4).join(",")
);
for (i, e) in air_evaluation_result.encode().into_iter().enumerate() {
memory.insert(free_page_pointer + BFieldElement::new(i as u64), e);
}
let stack = [
empty_stack(),
vec![free_page_pointer],
out_of_domain_point_curr_row
.coefficients
.into_iter()
.rev()
.collect_vec(),
vec![
BFieldElement::new(padded_height as u64),
trace_domain_generator,
],
]
.concat();
let code = link_for_isolated_run(Rc::new(RefCell::new(self.to_owned())));
let final_state = execute_test(
&code,
&mut stack.clone(),
self.stack_diff(),
vec![],
NonDeterminism::default().with_ram(memory),
None,
);
AirConstraintEvaluation::read_result_from_memory(final_state).0
}
pub fn rust_result(
air_evaluation_result: [XFieldElement; MasterAuxTable::NUM_CONSTRAINTS],
out_of_domain_point_curr_row: XFieldElement,
padded_height: u32,
trace_domain_generator: BFieldElement,
) -> Vec<XFieldElement> {
println!("trace domain generator: {trace_domain_generator}");
println!("padded height: {padded_height}");
println!("out-of-domain point current row: {out_of_domain_point_curr_row}");
let initial_zerofier_inv = (out_of_domain_point_curr_row - bfe!(1)).inverse();
let consistency_zerofier_inv =
(out_of_domain_point_curr_row.mod_pow_u32(padded_height) - bfe!(1)).inverse();
let except_last_row = out_of_domain_point_curr_row - trace_domain_generator.inverse();
let transition_zerofier_inv = except_last_row * consistency_zerofier_inv;
let terminal_zerofier_inv = except_last_row.inverse(); println!("initial zerofier inverse: {}", initial_zerofier_inv);
println!("consistency zerofier inverse: {}", consistency_zerofier_inv);
println!("transition zerofier inverse: {}", transition_zerofier_inv);
println!("terminal zerofier inverse: {}", terminal_zerofier_inv);
let mut running_total_constraints = 0;
let initial_quotients = air_evaluation_result[running_total_constraints
..(running_total_constraints + MasterAuxTable::NUM_INITIAL_CONSTRAINTS)]
.iter()
.map(|&x| x * initial_zerofier_inv)
.collect_vec();
running_total_constraints += MasterAuxTable::NUM_INITIAL_CONSTRAINTS;
let consistency_quotients = air_evaluation_result[running_total_constraints
..(running_total_constraints + MasterAuxTable::NUM_CONSISTENCY_CONSTRAINTS)]
.iter()
.map(|&x| x * consistency_zerofier_inv)
.collect_vec();
running_total_constraints += MasterAuxTable::NUM_CONSISTENCY_CONSTRAINTS;
let transition_quotients = air_evaluation_result[running_total_constraints
..(running_total_constraints + MasterAuxTable::NUM_TRANSITION_CONSTRAINTS)]
.iter()
.map(|&x| x * transition_zerofier_inv)
.collect_vec();
running_total_constraints += MasterAuxTable::NUM_TRANSITION_CONSTRAINTS;
let terminal_quotients = air_evaluation_result[running_total_constraints
..(running_total_constraints + MasterAuxTable::NUM_TERMINAL_CONSTRAINTS)]
.iter()
.map(|&x| x * terminal_zerofier_inv)
.collect_vec();
[
initial_quotients,
consistency_quotients,
transition_quotients,
terminal_quotients,
]
.concat()
}
}
}
#[cfg(test)]
mod bench {
use std::collections::HashMap;
use itertools::Itertools;
use rand::prelude::*;
use twenty_first::math::traits::PrimitiveRootOfUnity;
use super::*;
use crate::empty_stack;
use crate::traits::function::Function;
use crate::traits::function::FunctionInitialState;
use crate::traits::function::ShadowedFunction;
use crate::traits::rust_shadow::RustShadow;
#[test]
fn bench_divide_out_zerofiers() {
ShadowedFunction::new(DivideOutZerofiers).bench();
}
impl Function for DivideOutZerofiers {
fn rust_shadow(
&self,
_stack: &mut Vec<BFieldElement>,
_memory: &mut HashMap<BFieldElement, BFieldElement>,
) {
unimplemented!()
}
fn pseudorandom_initial_state(
&self,
seed: [u8; 32],
_bench_case: Option<crate::snippet_bencher::BenchmarkCase>,
) -> FunctionInitialState {
let mut rng: StdRng = SeedableRng::from_seed(seed);
let air_evaluation_result =
rng.gen::<[XFieldElement; MasterAuxTable::NUM_CONSTRAINTS]>();
let ood_point_current_row = rng.gen::<XFieldElement>();
let padded_height = 1 << 20;
let trace_domain_generator =
BFieldElement::primitive_root_of_unity(padded_height).unwrap();
let free_page_pointer = BFieldElement::new(((1u64 << 32) - 3) * (1 << 32));
let mut memory = HashMap::<BFieldElement, BFieldElement>::new();
for (i, e) in air_evaluation_result
.encode()
.into_iter()
.skip(1)
.enumerate()
{
memory.insert(free_page_pointer + BFieldElement::new(i as u64), e);
}
let stack = [
empty_stack(),
vec![free_page_pointer],
ood_point_current_row
.coefficients
.into_iter()
.rev()
.collect_vec(),
vec![BFieldElement::new(padded_height), trace_domain_generator],
]
.concat();
FunctionInitialState { stack, memory }
}
}
}