From 2143ccf95ab6bf14f8b235f9f8607527c1dee0af Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Wed, 17 Sep 2025 08:05:10 +0200 Subject: [PATCH 01/83] feat: Add Plonky3 basic backend --- Cargo.toml | 1 + codegen/plonky3/Cargo.toml | 17 +++ codegen/plonky3/README.md | 3 + .../plonky3/src/air/boundary_constraints.rs | 17 +++ codegen/plonky3/src/air/graph.rs | 134 ++++++++++++++++++ codegen/plonky3/src/air/mod.rs | 74 ++++++++++ .../plonky3/src/air/transition_constraints.rs | 18 +++ codegen/plonky3/src/imports.rs | 13 ++ codegen/plonky3/src/lib.rs | 28 ++++ 9 files changed, 305 insertions(+) create mode 100644 codegen/plonky3/Cargo.toml create mode 100644 codegen/plonky3/README.md create mode 100644 codegen/plonky3/src/air/boundary_constraints.rs create mode 100644 codegen/plonky3/src/air/graph.rs create mode 100644 codegen/plonky3/src/air/mod.rs create mode 100644 codegen/plonky3/src/air/transition_constraints.rs create mode 100644 codegen/plonky3/src/imports.rs create mode 100644 codegen/plonky3/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 1278e9126..a3285a70d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "air", "codegen/winterfell", "codegen/ace", + "codegen/plonky3", ] resolver = "2" diff --git a/codegen/plonky3/Cargo.toml b/codegen/plonky3/Cargo.toml new file mode 100644 index 000000000..eb7766ea2 --- /dev/null +++ b/codegen/plonky3/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "air-codegen-plonky3" +version = "0.5.0" +description = "Plonky3 code generator for the AirScript language" +authors.workspace = true +readme = "README.md" +license.workspace = true +repository.workspace = true +categories = ["compilers", "cryptography"] +keywords = ["air", "stark", "plonky3", "zero-knowledge", "zkp"] +edition.workspace = true +rust-version.workspace = true + +[dependencies] +air-ir = { package = "air-ir", path = "../../air", version = "0.5" } +anyhow = { workspace = true } +codegen = "0.2" diff --git a/codegen/plonky3/README.md b/codegen/plonky3/README.md new file mode 100644 index 000000000..8ee666dfd --- /dev/null +++ b/codegen/plonky3/README.md @@ -0,0 +1,3 @@ +# Plonky3 Code Generator + +This crate contains a code generator targeting the [Plonky3 prover](https://github.com/Plonky3/Plonky3) Rust library. diff --git a/codegen/plonky3/src/air/boundary_constraints.rs b/codegen/plonky3/src/air/boundary_constraints.rs new file mode 100644 index 000000000..19a3519d1 --- /dev/null +++ b/codegen/plonky3/src/air/boundary_constraints.rs @@ -0,0 +1,17 @@ +use air_ir::{Air, TraceSegmentId}; +use codegen::Function; + +use super::Codegen; + +/// Adds the main boundary constraints to the generated code. +pub(super) fn add_main_boundary_constraints(eval_func: &mut Function, ir: &Air) { + for constraint in ir.boundary_constraints(TraceSegmentId::Main) { + let expr_root = constraint.node_index(); + + let expr_root_string = expr_root.to_string(ir, TraceSegmentId::Main); + + let assertion = format!("builder.when_first_row().assert_zero::<_>({expr_root_string});"); + + eval_func.line(assertion); + } +} diff --git a/codegen/plonky3/src/air/graph.rs b/codegen/plonky3/src/air/graph.rs new file mode 100644 index 000000000..8aa05a572 --- /dev/null +++ b/codegen/plonky3/src/air/graph.rs @@ -0,0 +1,134 @@ +use air_ir::{ + Air, IntegrityConstraintDegree, NodeIndex, Operation, TraceAccess, TraceSegmentId, Value, +}; + +// RUST STRING GENERATION FOR THE CONSTRAINT GRAPH +// ================================================================================================ + +/// Code generation trait for generating Rust code strings from IR types related to constraints and +/// the [AlgebraicGraph]. +pub trait Codegen { + fn to_string(&self, ir: &Air, trace_segment: TraceSegmentId) -> String; +} + +impl Codegen for IntegrityConstraintDegree { + fn to_string(&self, _ir: &Air, _trace_segment: TraceSegmentId) -> String { + if self.cycles().is_empty() { + format!("TransitionConstraintDegree::new({})", self.base()) + } else { + let cycles = self + .cycles() + .iter() + .map(|cycle_len| cycle_len.to_string()) + .collect::>() + .join(", "); + format!("TransitionConstraintDegree::with_cycles({}, vec![{}])", self.base(), cycles) + } + } +} + +impl Codegen for TraceAccess { + fn to_string(&self, _ir: &Air, trace_segment: TraceSegmentId) -> String { + let frame = self.segment.to_string(); + let row_offset = match self.row_offset { + 0 => { + format!("current[{}]", self.column) + }, + 1 => { + format!("next[{}]", self.column) + }, + _ => panic!("Plonky3 doesn't support row offsets greater than 1."), + }; + if self.segment == TraceSegmentId::Main && self.segment != trace_segment { + format!("E::from({frame}_{row_offset})") + } else { + format!("{frame}_{row_offset}") + } + } +} + +impl Codegen for NodeIndex { + fn to_string(&self, ir: &Air, trace_segment: TraceSegmentId) -> String { + let op = ir.constraint_graph().node(self).op(); + op.to_string(ir, trace_segment) + } +} + +impl Codegen for Operation { + fn to_string(&self, ir: &Air, trace_segment: TraceSegmentId) -> String { + match self { + Operation::Value(value) => value.to_string(ir, trace_segment), + Operation::Add(..) => binary_op_to_string(ir, self, trace_segment), + Operation::Sub(..) => binary_op_to_string(ir, self, trace_segment), + Operation::Mul(..) => binary_op_to_string(ir, self, trace_segment), + } + } +} + +impl Codegen for Value { + fn to_string(&self, ir: &Air, trace_segment: TraceSegmentId) -> String { + match self { + Value::Constant(value) => format!("AB::Expr::from(AB::F::from_u64({value}))"), + Value::TraceAccess(trace_access) => trace_access.to_string(ir, trace_segment), + Value::PublicInput(air_ir::PublicInputAccess { name, index }) => { + let get_public_input_offset = |name: &str| { + ir.public_inputs() + .take_while(|pi| pi.name() != name) + .map(|pi| pi.size()) + .sum::() + }; + format!("public_values[{}].into()", get_public_input_offset(name.as_str()) + index) + }, + _ => todo!(), + /*Value::PeriodicColumn(pc) => { + let index = + ir.periodic_columns.iter().position(|(qid, _)| qid == &pc.name).unwrap(); + format!("periodic_values[{index}]") + }, + Value::PublicInputTable(air_ir::PublicInputTableAccess { + table_name, + bus_type, + num_cols: _, + }) => { + format!("reduced_{table_name}_{bus_type}") + }, + Value::RandomValue(idx) => { + format!("aux_rand_elements.rand_elements()[{idx}]") + },*/ + } + } +} + +/// Returns a string representation of a binary operation. +fn binary_op_to_string(ir: &Air, op: &Operation, trace_segment: TraceSegmentId) -> String { + match op { + Operation::Add(l_idx, r_idx) => { + let lhs = l_idx.to_string(ir, trace_segment); + let rhs = r_idx.to_string(ir, trace_segment); + format!("{lhs} + {rhs}") + }, + Operation::Sub(l_idx, r_idx) => { + let lhs = l_idx.to_string(ir, trace_segment); + let rhs = if ir.constraint_graph().node(r_idx).op().precedence() <= op.precedence() { + format!("({})", r_idx.to_string(ir, trace_segment)) + } else { + r_idx.to_string(ir, trace_segment) + }; + format!("{lhs} - {rhs}") + }, + Operation::Mul(l_idx, r_idx) => { + let lhs = if ir.constraint_graph().node(l_idx).op().precedence() < op.precedence() { + format!("({})", l_idx.to_string(ir, trace_segment)) + } else { + l_idx.to_string(ir, trace_segment) + }; + let rhs = if ir.constraint_graph().node(r_idx).op().precedence() < op.precedence() { + format!("({})", r_idx.to_string(ir, trace_segment)) + } else { + r_idx.to_string(ir, trace_segment) + }; + format!("{lhs} * {rhs}") + }, + _ => panic!("unsupported operation"), + } +} diff --git a/codegen/plonky3/src/air/mod.rs b/codegen/plonky3/src/air/mod.rs new file mode 100644 index 000000000..f19230eb8 --- /dev/null +++ b/codegen/plonky3/src/air/mod.rs @@ -0,0 +1,74 @@ +mod boundary_constraints; +mod graph; +use graph::Codegen; +mod transition_constraints; + +use air_ir::Air; + +use super::Scope; +use crate::air::{ + boundary_constraints::add_main_boundary_constraints, + transition_constraints::add_main_transition_constraints, +}; + +// HELPERS TO GENERATE AN IMPLEMENTATION OF THE PLONKY3 AIR TRAIT +// ================================================================================================ + +/// Updates the provided scope with a new Air struct and Plonky3 Air trait implementation +/// which are equivalent the provided AirIR. +pub(super) fn add_air(scope: &mut Scope, ir: &Air) { + let name = ir.name(); + + // add the Air struct and its base implementation. + add_air_struct(scope, ir, name); + + // add Plonky3 AirBuilder trait implementation for the provided AirIR. + add_air_trait(scope, ir, name); +} + +/// Updates the provided scope with a custom Air struct. +fn add_air_struct(scope: &mut Scope, ir: &Air, name: &str) { + scope.raw(format!("pub const NUM_COLUMNS: usize = {};", ir.trace_segment_widths[0])); + + let num_public_values = + ir.public_inputs().map(|public_input| public_input.size()).sum::(); + scope.raw(format!("pub const NUM_PUBLIC_VALUES: usize = {num_public_values};")); + + // define the custom Air struct. + scope.new_struct(name).vis("pub"); + + // add the custom BaseAir implementation block + let base_air_impl = scope.new_impl(name).generic("F").impl_trait("BaseAir"); + base_air_impl.new_fn("width").arg_ref_self().ret("usize").line("NUM_COLUMNS"); + + // add the custom BaseAirWithPublicValues implementation block + let base_air_with_public_values_impl = + scope.new_impl(name).generic("F").impl_trait("BaseAirWithPublicValues"); + base_air_with_public_values_impl + .new_fn("num_public_values") + .arg_ref_self() + .ret("usize") + .line("NUM_PUBLIC_VALUES"); +} + +/// Updates the provided scope with the custom Air struct and an Air trait implementation based on +/// the provided AirIR. +fn add_air_trait(scope: &mut Scope, ir: &Air, name: &str) { + // add the implementation block for the Air trait. + let air_impl = scope + .new_impl(name) + .generic("AB: AirBuilderWithPublicValues") + .impl_trait("Air"); + + let eval_func = air_impl.new_fn("eval").arg_ref_self().arg("builder", "&mut AB"); + eval_func.line("let main = builder.main();"); + eval_func.line("let public_values = builder.public_values().to_vec();"); + eval_func.line("let (main_current, main_next) = ("); + eval_func.line(" main.row_slice(0).unwrap(),"); + eval_func.line(" main.row_slice(1).unwrap(),"); + eval_func.line(");"); + + add_main_boundary_constraints(eval_func, ir); + + add_main_transition_constraints(eval_func, ir); +} diff --git a/codegen/plonky3/src/air/transition_constraints.rs b/codegen/plonky3/src/air/transition_constraints.rs new file mode 100644 index 000000000..0a0ad92fa --- /dev/null +++ b/codegen/plonky3/src/air/transition_constraints.rs @@ -0,0 +1,18 @@ +use air_ir::{Air, TraceSegmentId}; +use codegen::Function; + +use super::Codegen; + +/// Adds the main transition constraints to the generated code. +pub(super) fn add_main_transition_constraints(eval_func: &mut Function, ir: &Air) { + // add the main integrity constraints + for constraint in ir.integrity_constraints(TraceSegmentId::Main) { + let expr_root = constraint.node_index(); + + let expr_root_string = expr_root.to_string(ir, TraceSegmentId::Main); + + let assertion = format!("builder.when_transition().assert_zero::<_>({expr_root_string});"); + + eval_func.line(assertion); + } +} diff --git a/codegen/plonky3/src/imports.rs b/codegen/plonky3/src/imports.rs new file mode 100644 index 000000000..7663096bb --- /dev/null +++ b/codegen/plonky3/src/imports.rs @@ -0,0 +1,13 @@ +use super::Scope; + +/// Adds the required imports to the provided scope. +pub(super) fn add_imports(scope: &mut Scope) { + // add plonky3 imports + scope.import("p3_air", "Air"); + scope.import("p3_air", "AirBuilder"); + scope.import("p3_air", "AirBuilderWithPublicValues"); + scope.import("p3_air", "BaseAir"); + scope.import("p3_air", "BaseAirWithPublicValues"); + scope.import("p3_matrix", "Matrix"); + scope.import("p3_field", "PrimeCharacteristicRing"); +} diff --git a/codegen/plonky3/src/lib.rs b/codegen/plonky3/src/lib.rs new file mode 100644 index 000000000..703a8ee94 --- /dev/null +++ b/codegen/plonky3/src/lib.rs @@ -0,0 +1,28 @@ +use air_ir::Air; +use codegen::Scope; + +mod air; +mod imports; + +// GENERATE RUST CODE FOR WINTERFELL AIR +// ================================================================================================ + +/// CodeGenerator is used to generate a Rust implementation of the Plonky3 STARK prover library's +/// Air trait. The generated Air expresses the constraints specified by the AirIR used to build the +/// CodeGenerator. +pub struct CodeGenerator; +impl air_ir::CodeGenerator for CodeGenerator { + type Output = String; + + fn generate(&self, ir: &Air) -> anyhow::Result { + let mut scope = Scope::new(); + + // add plonky3 imports. + imports::add_imports(&mut scope); + + // add an Air struct and plonky3 Air trait implementation for the provided AirIR. + air::add_air(&mut scope, ir); + + Ok(scope.to_string()) + } +} From d83b791c8e9dec9b3312716a557c4b87b7677616 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Wed, 17 Sep 2025 08:06:04 +0200 Subject: [PATCH 02/83] feat: Handle plonky3 target --- air-script/Cargo.toml | 1 + air-script/src/cli/transpile.rs | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/air-script/Cargo.toml b/air-script/Cargo.toml index 8d5d5e5ed..3ceafb720 100644 --- a/air-script/Cargo.toml +++ b/air-script/Cargo.toml @@ -18,6 +18,7 @@ path = "src/main.rs" [dependencies] air-codegen-winter = { package = "air-codegen-winter", path = "../codegen/winterfell", version = "0.5" } +air-codegen-plonky3 = { package = "air-codegen-plonky3", path = "../codegen/plonky3", version = "0.5" } air-ir = { package = "air-ir", path = "../air", version = "0.5" } air-parser = { package = "air-parser", path = "../parser", version = "0.5" } air-pass = { package = "air-pass", path = "../pass", version = "0.5" } diff --git a/air-script/src/cli/transpile.rs b/air-script/src/cli/transpile.rs index 3671dd096..48b7b37cd 100644 --- a/air-script/src/cli/transpile.rs +++ b/air-script/src/cli/transpile.rs @@ -9,11 +9,13 @@ use miden_diagnostics::{ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] pub enum Target { Winterfell, + Plonky3, } impl Target { pub fn extension(&self) -> &'static str { match self { Self::Winterfell => "rs", + Self::Plonky3 => "rs", } } } @@ -55,6 +57,7 @@ impl Transpile { let target = self.target.unwrap_or(Target::Winterfell); let backend: Box> = match target { Target::Winterfell => Box::new(air_codegen_winter::CodeGenerator), + Target::Plonky3 => Box::new(air_codegen_plonky3::CodeGenerator), }; // write transpiled output to the output path @@ -62,6 +65,13 @@ impl Transpile { Some(path) => path.clone(), None => { let mut path = input_path.clone(); + if target == Target::Plonky3 { + path.set_file_name(format!( + "{}_plonky3", + path.file_stem().unwrap().display() + )); + path.set_extension("air"); + } path.set_extension(target.extension()); path }, From 98495ba2c9fcb0b8ac443ed3981caabdad8290f0 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Wed, 17 Sep 2025 08:38:38 +0200 Subject: [PATCH 03/83] tests: Add E2E test codegen for binary plonky3 --- air-script/Cargo.toml | 12 ++++++++ air-script/tests/binary/binary.air | 2 +- air-script/tests/binary/binary.rs | 2 +- air-script/tests/binary/binary_plonky3.rs | 35 +++++++++++++++++++++++ air-script/tests/binary/mod.rs | 4 +++ air-script/tests/codegen/helpers.rs | 2 ++ air-script/tests/codegen/mod.rs | 1 + air-script/tests/codegen/plonky3.rs | 15 ++++++++++ 8 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 air-script/tests/binary/binary_plonky3.rs create mode 100644 air-script/tests/codegen/plonky3.rs diff --git a/air-script/Cargo.toml b/air-script/Cargo.toml index 3ceafb720..131e1dbf8 100644 --- a/air-script/Cargo.toml +++ b/air-script/Cargo.toml @@ -30,6 +30,18 @@ mir = { package = "air-mir", path = "../mir", version = "0.5" } [dev-dependencies] expect-test = "1.4" +p3-air = { package = "p3-air", version = "0.3", default-features = false } +p3-challenger = { package = "p3-challenger", version = "0.3", default-features = false } +p3-circle = { package = "p3-circle", version = "0.3", default-features = false } +p3-commit = { package = "p3-commit", version = "0.3", default-features = false } +p3-field = { package = "p3-field", version = "0.3", default-features = false } +p3-fri = { package = "p3-fri", version = "0.3", default-features = false } +p3-matrix = { package = "p3-matrix", version = "0.3", default-features = false } +p3-merkle-tree = { package = "p3-merkle-tree", version = "0.3", default-features = false } +p3-mersenne-31 = { package = "p3-mersenne-31", version = "0.3", default-features = false } +p3-sha256 = { package = "p3-sha256", version = "0.3", default-features = false } +p3-symmetric = { package = "p3-symmetric", version = "0.3", default-features = false } +p3-uni-stark = { package = "p3-uni-stark", version = "0.3", default-features = false } winter-air = { package = "winter-air", version = "0.12", default-features = false } winter-math = { package = "winter-math", version = "0.12", default-features = false } winter-utils = { package = "winter-utils", version = "0.12", default-features = false } diff --git a/air-script/tests/binary/binary.air b/air-script/tests/binary/binary.air index 50e218052..e609a69b3 100644 --- a/air-script/tests/binary/binary.air +++ b/air-script/tests/binary/binary.air @@ -9,7 +9,7 @@ public_inputs { } boundary_constraints { - enf a.first = 0; + enf a.first = stack_inputs[0]; } integrity_constraints { diff --git a/air-script/tests/binary/binary.rs b/air-script/tests/binary/binary.rs index 8b7185bbd..b398843dc 100644 --- a/air-script/tests/binary/binary.rs +++ b/air-script/tests/binary/binary.rs @@ -70,7 +70,7 @@ impl Air for BinaryAir { fn get_assertions(&self) -> Vec> { let mut result = Vec::new(); - result.push(Assertion::single(0, 0, Felt::ZERO)); + result.push(Assertion::single(0, 0, self.stack_inputs[0])); result } diff --git a/air-script/tests/binary/binary_plonky3.rs b/air-script/tests/binary/binary_plonky3.rs new file mode 100644 index 000000000..dc8a32634 --- /dev/null +++ b/air-script/tests/binary/binary_plonky3.rs @@ -0,0 +1,35 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; + +pub const NUM_COLUMNS: usize = 2; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct BinaryAir; + +impl BaseAir for BinaryAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for BinaryAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl Air for BinaryAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values = builder.public_values().to_vec(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[0] - public_values[0].into()); + builder.when_transition().assert_zero::<_>(main_current[0] * main_current[0] - main_current[0]); + builder.when_transition().assert_zero::<_>(main_current[1] * main_current[1] - main_current[1]); + } +} \ No newline at end of file diff --git a/air-script/tests/binary/mod.rs b/air-script/tests/binary/mod.rs index 0582511b7..3aa069368 100644 --- a/air-script/tests/binary/mod.rs +++ b/air-script/tests/binary/mod.rs @@ -2,3 +2,7 @@ #[allow(clippy::all)] mod binary; mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod binary_plonky3; diff --git a/air-script/tests/codegen/helpers.rs b/air-script/tests/codegen/helpers.rs index 3fc9c8aa6..577144d42 100644 --- a/air-script/tests/codegen/helpers.rs +++ b/air-script/tests/codegen/helpers.rs @@ -8,6 +8,7 @@ use miden_diagnostics::{ pub enum Target { Winterfell, + Plonky3, } pub struct Test { @@ -30,6 +31,7 @@ impl Test { let backend: Box> = match target { Target::Winterfell => Box::new(air_codegen_winter::CodeGenerator), + Target::Plonky3 => Box::new(air_codegen_plonky3::CodeGenerator), }; // generate Rust code targeting Winterfell diff --git a/air-script/tests/codegen/mod.rs b/air-script/tests/codegen/mod.rs index d802f40b1..a23676b61 100644 --- a/air-script/tests/codegen/mod.rs +++ b/air-script/tests/codegen/mod.rs @@ -1,2 +1,3 @@ mod helpers; +mod plonky3; mod winterfell; diff --git a/air-script/tests/codegen/plonky3.rs b/air-script/tests/codegen/plonky3.rs new file mode 100644 index 000000000..1c946a90f --- /dev/null +++ b/air-script/tests/codegen/plonky3.rs @@ -0,0 +1,15 @@ +use expect_test::expect_file; + +use super::helpers::{Target, Test}; + +#[test] +fn binary() { + let generated_air = Test::new("tests/binary/binary.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../binary/binary_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +// TODO: add all tests From f15970d9230b02c68526633a23fa1758df06a6e8 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Wed, 17 Sep 2025 08:39:16 +0200 Subject: [PATCH 04/83] tests: Add test of generated code in Plonky3 binary E2E tests --- air-script/tests/binary/mod.rs | 4 +- air-script/tests/binary/test_air_plonky3.rs | 81 +++++++++++++++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 air-script/tests/binary/test_air_plonky3.rs rename air-script/tests/binary/{test_air.rs => test_air_winterfell.rs} (100%) diff --git a/air-script/tests/binary/mod.rs b/air-script/tests/binary/mod.rs index 3aa069368..fa03709f2 100644 --- a/air-script/tests/binary/mod.rs +++ b/air-script/tests/binary/mod.rs @@ -1,7 +1,9 @@ #[rustfmt::skip] #[allow(clippy::all)] mod binary; -mod test_air; +mod test_air_winterfell; + +mod test_air_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] diff --git a/air-script/tests/binary/test_air_plonky3.rs b/air-script/tests/binary/test_air_plonky3.rs new file mode 100644 index 000000000..627acde10 --- /dev/null +++ b/air-script/tests/binary/test_air_plonky3.rs @@ -0,0 +1,81 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::{StarkConfig, prove, verify}; + +use crate::binary::binary_plonky3::{BinaryAir, NUM_COLUMNS}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::from_canonical_checked(inputs[0]).unwrap(); + rows[0][1] = F::ONE; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let a_prev = rows[i - 1][0]; + let b_prev = rows[i - 1][1]; + + // Update current row based on previous values + rows[i][0] = F::ONE - a_prev; + rows[i][1] = F::ONE - b_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + let proof = prove(&config, &BinaryAir {}, trace, &inputs_m31); + verify(&config, &BinaryAir {}, &proof, &inputs_m31).expect("Verification failed"); +} diff --git a/air-script/tests/binary/test_air.rs b/air-script/tests/binary/test_air_winterfell.rs similarity index 100% rename from air-script/tests/binary/test_air.rs rename to air-script/tests/binary/test_air_winterfell.rs From 254cdcc0eaa4699aa719e3402ff0e3a827a6d389 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Wed, 17 Sep 2025 08:43:48 +0200 Subject: [PATCH 05/83] fix: Make docs_sync() test work on windows --- air-script/tests/docs_sync.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/air-script/tests/docs_sync.rs b/air-script/tests/docs_sync.rs index c33ea5512..75bfc9898 100644 --- a/air-script/tests/docs_sync.rs +++ b/air-script/tests/docs_sync.rs @@ -3,7 +3,9 @@ use std::{path::Path, process::Command}; #[test] fn docs_sync() { let examples_dir = Path::new("../docs/examples"); - let airc_path = Path::new("target/release/airc"); + // Use CARGO_MANIFEST_DIR to build an absolute path to airc, needed on Windows to correctly use `current_dir`. + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); + let airc_path = Path::new(&manifest_dir).join("../target/release/airc"); // Build the CLI tool first let build_output = Command::new("cargo") @@ -38,11 +40,11 @@ fn docs_sync() { let file_name = air_file.file_name().unwrap().to_string_lossy(); let output_path = air_file.with_extension("rs"); - let output = Command::new(airc_path) + let output = Command::new(&airc_path) .args(["transpile", air_file.to_str().unwrap(), "-o", output_path.to_str().unwrap()]) .current_dir("../") .output() - .unwrap_or_else(|_| panic!("Failed to transpile {}", file_name)); + .unwrap_or_else(|_| panic!("Failed to transpile {file_name}")); assert!( output.status.success(), @@ -51,7 +53,7 @@ fn docs_sync() { String::from_utf8_lossy(&output.stderr) ); - println!("Successfully transpiled: {}", file_name); + println!("Successfully transpiled: {file_name}"); // Clean up generated Rust files let _ = std::fs::remove_file(output_path); From aaa41ed97d254913505e0cdc5e36100e6bee1490 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Fri, 19 Sep 2025 13:09:16 +0200 Subject: [PATCH 06/83] fix(codegen): differentiate integrity and transition constraints --- air-script/tests/binary/binary_plonky3.rs | 4 ++-- .../plonky3/src/air/integrity_constraints.rs | 22 +++++++++++++++++++ codegen/plonky3/src/air/mod.rs | 6 ++--- .../plonky3/src/air/transition_constraints.rs | 18 --------------- 4 files changed, 27 insertions(+), 23 deletions(-) create mode 100644 codegen/plonky3/src/air/integrity_constraints.rs delete mode 100644 codegen/plonky3/src/air/transition_constraints.rs diff --git a/air-script/tests/binary/binary_plonky3.rs b/air-script/tests/binary/binary_plonky3.rs index dc8a32634..68f620460 100644 --- a/air-script/tests/binary/binary_plonky3.rs +++ b/air-script/tests/binary/binary_plonky3.rs @@ -29,7 +29,7 @@ impl Air for BinaryAir { main.row_slice(1).unwrap(), ); builder.when_first_row().assert_zero::<_>(main_current[0] - public_values[0].into()); - builder.when_transition().assert_zero::<_>(main_current[0] * main_current[0] - main_current[0]); - builder.when_transition().assert_zero::<_>(main_current[1] * main_current[1] - main_current[1]); + builder.assert_zero::<_>(main_current[0] * main_current[0] - main_current[0]); + builder.assert_zero::<_>(main_current[1] * main_current[1] - main_current[1]); } } \ No newline at end of file diff --git a/codegen/plonky3/src/air/integrity_constraints.rs b/codegen/plonky3/src/air/integrity_constraints.rs new file mode 100644 index 000000000..78dae2d55 --- /dev/null +++ b/codegen/plonky3/src/air/integrity_constraints.rs @@ -0,0 +1,22 @@ +use air_ir::{Air, ConstraintDomain, TraceSegmentId}; +use codegen::Function; + +use super::Codegen; + +/// Adds the main integrity constraints to the generated code. +pub(super) fn add_main_integrity_constraints(eval_func: &mut Function, ir: &Air) { + for constraint in ir.integrity_constraints(TraceSegmentId::Main) { + let expr_root = constraint.node_index(); + let expr_root_string = expr_root.to_string(ir, TraceSegmentId::Main); + + // If the constraint is a transition constraint (depends on the next row), we do not + // evaluate it in the last row, with the `when_transition` method. + let assertion = if let ConstraintDomain::EveryFrame(_) = constraint.domain() { + format!("builder.when_transition().assert_zero::<_>({expr_root_string});") + } else { + format!("builder.assert_zero::<_>({expr_root_string});") + }; + + eval_func.line(assertion); + } +} diff --git a/codegen/plonky3/src/air/mod.rs b/codegen/plonky3/src/air/mod.rs index f19230eb8..81a5b4568 100644 --- a/codegen/plonky3/src/air/mod.rs +++ b/codegen/plonky3/src/air/mod.rs @@ -1,14 +1,14 @@ mod boundary_constraints; mod graph; use graph::Codegen; -mod transition_constraints; +mod integrity_constraints; use air_ir::Air; use super::Scope; use crate::air::{ boundary_constraints::add_main_boundary_constraints, - transition_constraints::add_main_transition_constraints, + integrity_constraints::add_main_integrity_constraints, }; // HELPERS TO GENERATE AN IMPLEMENTATION OF THE PLONKY3 AIR TRAIT @@ -70,5 +70,5 @@ fn add_air_trait(scope: &mut Scope, ir: &Air, name: &str) { add_main_boundary_constraints(eval_func, ir); - add_main_transition_constraints(eval_func, ir); + add_main_integrity_constraints(eval_func, ir); } diff --git a/codegen/plonky3/src/air/transition_constraints.rs b/codegen/plonky3/src/air/transition_constraints.rs deleted file mode 100644 index 0a0ad92fa..000000000 --- a/codegen/plonky3/src/air/transition_constraints.rs +++ /dev/null @@ -1,18 +0,0 @@ -use air_ir::{Air, TraceSegmentId}; -use codegen::Function; - -use super::Codegen; - -/// Adds the main transition constraints to the generated code. -pub(super) fn add_main_transition_constraints(eval_func: &mut Function, ir: &Air) { - // add the main integrity constraints - for constraint in ir.integrity_constraints(TraceSegmentId::Main) { - let expr_root = constraint.node_index(); - - let expr_root_string = expr_root.to_string(ir, TraceSegmentId::Main); - - let assertion = format!("builder.when_transition().assert_zero::<_>({expr_root_string});"); - - eval_func.line(assertion); - } -} From 8b3ed7364d2bfac2f5b3c95cd569ad6534f5a37b Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Fri, 19 Sep 2025 13:14:59 +0200 Subject: [PATCH 07/83] refactor(codegen): avoid public_+value Vec allocation --- air-script/tests/binary/binary_plonky3.rs | 2 +- codegen/plonky3/src/air/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/air-script/tests/binary/binary_plonky3.rs b/air-script/tests/binary/binary_plonky3.rs index 68f620460..f06827809 100644 --- a/air-script/tests/binary/binary_plonky3.rs +++ b/air-script/tests/binary/binary_plonky3.rs @@ -23,7 +23,7 @@ impl BaseAirWithPublicValues for BinaryAir { impl Air for BinaryAir { fn eval(&self, builder: &mut AB) { let main = builder.main(); - let public_values = builder.public_values().to_vec(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let (main_current, main_next) = ( main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), diff --git a/codegen/plonky3/src/air/mod.rs b/codegen/plonky3/src/air/mod.rs index 81a5b4568..79c533a73 100644 --- a/codegen/plonky3/src/air/mod.rs +++ b/codegen/plonky3/src/air/mod.rs @@ -62,7 +62,7 @@ fn add_air_trait(scope: &mut Scope, ir: &Air, name: &str) { let eval_func = air_impl.new_fn("eval").arg_ref_self().arg("builder", "&mut AB"); eval_func.line("let main = builder.main();"); - eval_func.line("let public_values = builder.public_values().to_vec();"); + eval_func.line("let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect(\"Wrong number of public values\");"); eval_func.line("let (main_current, main_next) = ("); eval_func.line(" main.row_slice(0).unwrap(),"); eval_func.line(" main.row_slice(1).unwrap(),"); From e628f199d415b9c8fd0402c2c859f706e80bd6cc Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Fri, 19 Sep 2025 13:15:34 +0200 Subject: [PATCH 08/83] chore: changelog, lint fix and removed unused IntegrityConstraintDegree codegen --- CHANGELOG.md | 1 + air-script/tests/docs_sync.rs | 3 ++- codegen/plonky3/src/air/graph.rs | 20 +------------------- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcdaa45ed..43a1720c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Refactored the unrolling pass in MIR (#434). - Fix regressions on MIR and list_comprehensions (#449). - Update documentation and tests thereof (#437). +- Add Plonky3 codegen backend (#461). ## 0.4.0 (2025-06-20) diff --git a/air-script/tests/docs_sync.rs b/air-script/tests/docs_sync.rs index 75bfc9898..c7fc12e72 100644 --- a/air-script/tests/docs_sync.rs +++ b/air-script/tests/docs_sync.rs @@ -3,7 +3,8 @@ use std::{path::Path, process::Command}; #[test] fn docs_sync() { let examples_dir = Path::new("../docs/examples"); - // Use CARGO_MANIFEST_DIR to build an absolute path to airc, needed on Windows to correctly use `current_dir`. + // Use CARGO_MANIFEST_DIR to build an absolute path to airc, needed on Windows to correctly use + // `current_dir`. let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); let airc_path = Path::new(&manifest_dir).join("../target/release/airc"); diff --git a/codegen/plonky3/src/air/graph.rs b/codegen/plonky3/src/air/graph.rs index 8aa05a572..3f987b7d3 100644 --- a/codegen/plonky3/src/air/graph.rs +++ b/codegen/plonky3/src/air/graph.rs @@ -1,6 +1,4 @@ -use air_ir::{ - Air, IntegrityConstraintDegree, NodeIndex, Operation, TraceAccess, TraceSegmentId, Value, -}; +use air_ir::{Air, NodeIndex, Operation, TraceAccess, TraceSegmentId, Value}; // RUST STRING GENERATION FOR THE CONSTRAINT GRAPH // ================================================================================================ @@ -11,22 +9,6 @@ pub trait Codegen { fn to_string(&self, ir: &Air, trace_segment: TraceSegmentId) -> String; } -impl Codegen for IntegrityConstraintDegree { - fn to_string(&self, _ir: &Air, _trace_segment: TraceSegmentId) -> String { - if self.cycles().is_empty() { - format!("TransitionConstraintDegree::new({})", self.base()) - } else { - let cycles = self - .cycles() - .iter() - .map(|cycle_len| cycle_len.to_string()) - .collect::>() - .join(", "); - format!("TransitionConstraintDegree::with_cycles({}, vec![{}])", self.base(), cycles) - } - } -} - impl Codegen for TraceAccess { fn to_string(&self, _ir: &Air, trace_segment: TraceSegmentId) -> String { let frame = self.segment.to_string(); From df72ff42c3729c10900fe30d57144b6f3ba37d3f Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Wed, 24 Sep 2025 11:36:42 +0200 Subject: [PATCH 09/83] feat: add periodic_columns --- air-script/Cargo.toml | 2 +- air-script/tests/binary/binary_plonky3.rs | 11 +- air-script/tests/binary/test_air_plonky3.rs | 17 +- air-script/tests/bitwise/bitwise_plonky3.rs | 61 ++++++++ air-script/tests/bitwise/mod.rs | 7 +- air-script/tests/bitwise/test_air_plonky3.rs | 128 +++++++++++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 air-script/tests/helpers/mod.rs | 76 +-------- .../tests/helpers/plonky3_periodic_columns.rs | 146 ++++++++++++++++++ .../tests/helpers/winterfell_test_helpers.rs | 73 +++++++++ .../plonky3/src/air/boundary_constraints.rs | 2 +- codegen/plonky3/src/air/graph.rs | 58 ++++--- .../plonky3/src/air/integrity_constraints.rs | 2 +- codegen/plonky3/src/air/mod.rs | 24 ++- codegen/plonky3/src/imports.rs | 2 + 15 files changed, 496 insertions(+), 113 deletions(-) create mode 100644 air-script/tests/bitwise/bitwise_plonky3.rs create mode 100644 air-script/tests/bitwise/test_air_plonky3.rs rename air-script/tests/bitwise/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/helpers/plonky3_periodic_columns.rs create mode 100644 air-script/tests/helpers/winterfell_test_helpers.rs diff --git a/air-script/Cargo.toml b/air-script/Cargo.toml index 131e1dbf8..883e5b61a 100644 --- a/air-script/Cargo.toml +++ b/air-script/Cargo.toml @@ -41,7 +41,7 @@ p3-merkle-tree = { package = "p3-merkle-tree", version = "0.3", default-features p3-mersenne-31 = { package = "p3-mersenne-31", version = "0.3", default-features = false } p3-sha256 = { package = "p3-sha256", version = "0.3", default-features = false } p3-symmetric = { package = "p3-symmetric", version = "0.3", default-features = false } -p3-uni-stark = { package = "p3-uni-stark", version = "0.3", default-features = false } +p3-uni-stark = { package = "p3-uni-stark", version = "0.3.0", default-features = false } winter-air = { package = "winter-air", version = "0.12", default-features = false } winter-math = { package = "winter-math", version = "0.12", default-features = false } winter-utils = { package = "winter-utils", version = "0.12", default-features = false } diff --git a/air-script/tests/binary/binary_plonky3.rs b/air-script/tests/binary/binary_plonky3.rs index f06827809..4d89ff939 100644 --- a/air-script/tests/binary/binary_plonky3.rs +++ b/air-script/tests/binary/binary_plonky3.rs @@ -1,6 +1,7 @@ use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; use p3_matrix::Matrix; use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; pub const NUM_COLUMNS: usize = 2; @@ -20,10 +21,18 @@ impl BaseAirWithPublicValues for BinaryAir { } } -impl Air for BinaryAir { +impl BaseAirWithPeriodicColumns for BinaryAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for BinaryAir { fn eval(&self, builder: &mut AB) { let main = builder.main(); let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), diff --git a/air-script/tests/binary/test_air_plonky3.rs b/air-script/tests/binary/test_air_plonky3.rs index 627acde10..da7b12a53 100644 --- a/air-script/tests/binary/test_air_plonky3.rs +++ b/air-script/tests/binary/test_air_plonky3.rs @@ -10,9 +10,12 @@ use p3_merkle_tree::MerkleTreeMmcs; use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::{StarkConfig, prove, verify}; +use p3_uni_stark::StarkConfig; -use crate::binary::binary_plonky3::{BinaryAir, NUM_COLUMNS}; +use crate::{ + binary::binary_plonky3::{BinaryAir, NUM_COLUMNS}, + helpers::check_constraints_with_periodic_columns, +}; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; @@ -76,6 +79,12 @@ fn test_air_plonky3() { let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); let trace = generate_trace_rows::(inputs); - let proof = prove(&config, &BinaryAir {}, trace, &inputs_m31); - verify(&config, &BinaryAir {}, &proof, &inputs_m31).expect("Verification failed"); + + check_constraints_with_periodic_columns(&BinaryAir {}, &trace, &inputs_m31); + + /*let prove_with_periodic_columns = prove_with_periodic_columns(&config, &BinaryAir {}, trace, &inputs_m31); + verify_with_periodic_columns(&config, &BinaryAir {}, &prove_with_periodic_columns, &inputs_m31).expect("Verification failed");*/ + + /*let proof = prove(&config, &BinaryAir {}, trace, &inputs_m31); + verify(&config, &BinaryAir {}, &proof, &inputs_m31).expect("Verification failed");*/ } diff --git a/air-script/tests/bitwise/bitwise_plonky3.rs b/air-script/tests/bitwise/bitwise_plonky3.rs new file mode 100644 index 000000000..e67cde192 --- /dev/null +++ b/air-script/tests/bitwise/bitwise_plonky3.rs @@ -0,0 +1,61 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 14; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct BitwiseAir; + +impl BaseAir for BitwiseAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for BitwiseAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for BitwiseAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + vec![F::from_u64(1), F::from_u64(0), F::from_u64(0), F::from_u64(0), F::from_u64(0), F::from_u64(0), F::from_u64(0), F::from_u64(0)], + vec![F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(0)], + ] + } +} + +impl Air for BitwiseAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[13]); + builder.assert_zero::<_>(main_current[0] * main_current[0] - main_current[0]); + builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[0] - main_current[0])); + builder.assert_zero::<_>(main_current[3] * main_current[3] - main_current[3]); + builder.assert_zero::<_>(main_current[4] * main_current[4] - main_current[4]); + builder.assert_zero::<_>(main_current[5] * main_current[5] - main_current[5]); + builder.assert_zero::<_>(main_current[6] * main_current[6] - main_current[6]); + builder.assert_zero::<_>(main_current[7] * main_current[7] - main_current[7]); + builder.assert_zero::<_>(main_current[8] * main_current[8] - main_current[8]); + builder.assert_zero::<_>(main_current[9] * main_current[9] - main_current[9]); + builder.assert_zero::<_>(main_current[10] * main_current[10] - main_current[10]); + builder.assert_zero::<_>(periodic_values[0].into() * (main_current[1] - (main_current[3] + AB::Expr::from(AB::F::from_u64(2)) * main_current[4] + AB::Expr::from(AB::F::from_u64(4)) * main_current[5] + AB::Expr::from(AB::F::from_u64(8)) * main_current[6]))); + builder.assert_zero::<_>(periodic_values[0].into() * (main_current[2] - (main_current[7] + AB::Expr::from(AB::F::from_u64(2)) * main_current[8] + AB::Expr::from(AB::F::from_u64(4)) * main_current[9] + AB::Expr::from(AB::F::from_u64(8)) * main_current[10]))); + builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[1] - (main_current[1] * AB::Expr::from(AB::F::from_u64(16)) + main_current[3] + AB::Expr::from(AB::F::from_u64(2)) * main_current[4] + AB::Expr::from(AB::F::from_u64(4)) * main_current[5] + AB::Expr::from(AB::F::from_u64(8)) * main_current[6]))); + builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[2] - (main_current[2] * AB::Expr::from(AB::F::from_u64(16)) + main_current[7] + AB::Expr::from(AB::F::from_u64(2)) * main_current[8] + AB::Expr::from(AB::F::from_u64(4)) * main_current[9] + AB::Expr::from(AB::F::from_u64(8)) * main_current[10]))); + builder.assert_zero::<_>(periodic_values[0].into() * main_current[11]); + builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_current[12] - main_next[11])); + builder.assert_zero::<_>((AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * (main_current[12] - (main_current[11] * AB::Expr::from(AB::F::from_u64(16)) + main_current[3] * main_current[7] + AB::Expr::from(AB::F::from_u64(2)) * main_current[4] * main_current[8] + AB::Expr::from(AB::F::from_u64(4)) * main_current[5] * main_current[9] + AB::Expr::from(AB::F::from_u64(8)) * main_current[6] * main_current[10])) + main_current[0] * (main_current[12] - (main_current[11] * AB::Expr::from(AB::F::from_u64(16)) + main_current[3] + main_current[7] - AB::Expr::from(AB::F::from_u64(2)) * main_current[3] * main_current[7] + AB::Expr::from(AB::F::from_u64(2)) * (main_current[4] + main_current[8] - AB::Expr::from(AB::F::from_u64(2)) * main_current[4] * main_current[8]) + AB::Expr::from(AB::F::from_u64(4)) * (main_current[5] + main_current[9] - AB::Expr::from(AB::F::from_u64(2)) * main_current[5] * main_current[9]) + AB::Expr::from(AB::F::from_u64(8)) * (main_current[6] + main_current[10] - AB::Expr::from(AB::F::from_u64(2)) * main_current[6] * main_current[10])))); + } +} \ No newline at end of file diff --git a/air-script/tests/bitwise/mod.rs b/air-script/tests/bitwise/mod.rs index 5798d90dc..31fe7ab14 100644 --- a/air-script/tests/bitwise/mod.rs +++ b/air-script/tests/bitwise/mod.rs @@ -1,4 +1,9 @@ #[rustfmt::skip] #[allow(clippy::all)] mod bitwise; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +mod bitwise_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/bitwise/test_air_plonky3.rs b/air-script/tests/bitwise/test_air_plonky3.rs new file mode 100644 index 000000000..c5c30ed7e --- /dev/null +++ b/air-script/tests/bitwise/test_air_plonky3.rs @@ -0,0 +1,128 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + bitwise::bitwise_plonky3::{BitwiseAir, NUM_COLUMNS}, + helpers::check_constraints_with_periodic_columns, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + let first = F::ZERO; + + // Initialize first row + rows[0][0] = first; + rows[0][1] = first; + rows[0][2] = first; + rows[0][3] = first; + rows[0][4] = first; + rows[0][5] = first; + rows[0][6] = first; + rows[0][7] = first; + rows[0][8] = first; + rows[0][9] = first; + rows[0][10] = first; + rows[0][11] = first; + rows[0][12] = first; + rows[0][13] = first; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + let col_3_prev = rows[i - 1][3]; + let col_4_prev = rows[i - 1][4]; + let col_5_prev = rows[i - 1][5]; + let col_6_prev = rows[i - 1][6]; + let col_7_prev = rows[i - 1][7]; + let col_8_prev = rows[i - 1][8]; + let col_9_prev = rows[i - 1][9]; + let col_10_prev = rows[i - 1][10]; + let col_11_prev = rows[i - 1][11]; + let col_12_prev = rows[i - 1][12]; + let col_13_prev = rows[i - 1][13]; + + // Update current row based on previous values + rows[i][0] = col_0_prev; + rows[i][1] = col_1_prev; + rows[i][2] = col_2_prev; + rows[i][3] = col_3_prev; + rows[i][4] = col_4_prev; + rows[i][5] = col_5_prev; + rows[i][6] = col_6_prev; + rows[i][7] = col_7_prev; + rows[i][8] = col_8_prev; + rows[i][9] = col_9_prev; + rows[i][10] = col_10_prev; + rows[i][11] = col_11_prev; + rows[i][12] = col_12_prev; + rows[i][13] = col_13_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&BitwiseAir {}, &trace, &inputs_m31); + + /*let prove_with_periodic_columns = prove_with_periodic_columns(&config, &BitwiseAir {}, trace, &inputs_m31); + verify_with_periodic_columns(&config, &BitwiseAir {}, &prove_with_periodic_columns, &inputs_m31).expect("Verification failed");*/ + + /*let proof = prove(&config, &BitwiseAir {}, trace, &inputs_m31); + verify(&config, &BitwiseAir {}, &proof, &inputs_m31).expect("Verification failed");*/ +} diff --git a/air-script/tests/bitwise/test_air.rs b/air-script/tests/bitwise/test_air_winterfell.rs similarity index 100% rename from air-script/tests/bitwise/test_air.rs rename to air-script/tests/bitwise/test_air_winterfell.rs diff --git a/air-script/tests/helpers/mod.rs b/air-script/tests/helpers/mod.rs index 5f91493d8..e9760c6d8 100644 --- a/air-script/tests/helpers/mod.rs +++ b/air-script/tests/helpers/mod.rs @@ -1,73 +1,5 @@ -use winter_air::{BatchingMethod, EvaluationFrame, FieldExtension, ProofOptions, TraceInfo}; -use winter_math::fields::f64::BaseElement as Felt; -use winterfell::{AuxTraceWithMetadata, Trace, TraceTable, matrix::ColMatrix}; +mod plonky3_periodic_columns; +mod winterfell_test_helpers; -/// We need to encapsulate the trace table in a struct to manually implement the `aux_trace_width` -/// method of the `Table` trait. Otherwise, using only a TraceTable will return an -/// `aux_trace_width` of 0 even if we provide a non-empty aux trace in `Trace::validate`, -/// and it fails the tests. -pub struct MyTraceTable { - pub trace: TraceTable, - pub aux_width: usize, -} - -impl MyTraceTable { - pub fn new(trace: TraceTable, aux_width: usize) -> Self { - Self { trace, aux_width } - } -} - -impl Trace for MyTraceTable { - type BaseField = Felt; - - fn info(&self) -> &TraceInfo { - self.trace.info() - } - - fn main_segment(&self) -> &ColMatrix { - self.trace.main_segment() - } - - fn read_main_frame(&self, row_idx: usize, frame: &mut EvaluationFrame) { - self.trace.read_main_frame(row_idx, frame); - } - - fn aux_trace_width(&self) -> usize { - self.aux_width - } -} - -pub trait AirTester { - type PubInputs; - - fn build_main_trace(&self, length: usize) -> MyTraceTable; - fn public_inputs(&self) -> Self::PubInputs; - fn build_aux_trace(&self, _length: usize) -> Option> { - None - } - fn build_trace_info(&self, length: usize) -> TraceInfo { - match &self.build_aux_trace(length) { - None => TraceInfo::new(self.build_main_trace(length).trace.width(), length), - Some(aux_trace) => TraceInfo::new_multi_segment( - self.build_main_trace(length).trace.width(), - aux_trace.aux_trace.num_cols(), - aux_trace.aux_rand_elements.rand_elements().len(), - length, - vec![], - ), - } - } - fn build_proof_options(&self) -> ProofOptions { - ProofOptions::new( - 32, // number of queries - 8, // blowup factor - 0, // grinding factor - FieldExtension::None, - 8, // FRI folding factor - 31, // FRI max remainder polynomial degree - BatchingMethod::Linear, /* method of batching used in computing constraint - * composition polynomial */ - BatchingMethod::Linear, // method of batching used in computing DEEP polynomial - ) - } -} +pub use plonky3_periodic_columns::*; +pub use winterfell_test_helpers::*; diff --git a/air-script/tests/helpers/plonky3_periodic_columns.rs b/air-script/tests/helpers/plonky3_periodic_columns.rs new file mode 100644 index 000000000..245a8d037 --- /dev/null +++ b/air-script/tests/helpers/plonky3_periodic_columns.rs @@ -0,0 +1,146 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir}; +use p3_field::Field; +use p3_matrix::{ + Matrix, + dense::{RowMajorMatrix, RowMajorMatrixView}, + stack::VerticalPair, +}; + +pub trait BaseAirWithPeriodicColumns: BaseAir { + fn get_periodic_columns(&self) -> Vec> { + vec![] + } +} + +pub trait AirBuilderWithPeriodicColumns: AirBuilder { + type PeriodicColumnsVar: Field + Into; + + fn periodic_columns(&self) -> Vec { + vec![] + } +} + +pub(crate) fn check_constraints_with_periodic_columns( + air: &A, + main: &RowMajorMatrix, + public_values: &Vec, +) where + F: Field, + A: for<'a> Air> + + BaseAirWithPeriodicColumns, +{ + let height = main.height(); + + (0..height).for_each(|i| { + let i_next = (i + 1) % height; + + let local = main.row_slice(i).unwrap(); // i < height so unwrap should never fail. + let next = main.row_slice(i_next).unwrap(); // i_next < height so unwrap should never fail. + let main = VerticalPair::new( + RowMajorMatrixView::new_row(&*local), + RowMajorMatrixView::new_row(&*next), + ); + let periodic_columns = air.get_periodic_columns(); + + let mut builder = DebugConstraintBuilderWithPeriodicColumns { + row_index: i, + main, + public_values, + is_first_row: F::from_bool(i == 0), + is_last_row: F::from_bool(i == height - 1), + is_transition: F::from_bool(i != height - 1), + periodic_columns, + }; + + air.eval(&mut builder); + }); +} + +/// A builder that runs constraint assertions during testing. +/// +/// Used in conjunction with [`check_constraints`] to simulate +/// an execution trace and verify that the AIR logic enforces all constraints. +#[derive(Debug)] +pub struct DebugConstraintBuilderWithPeriodicColumns<'a, F: Field> { + /// The index of the row currently being evaluated. + row_index: usize, + /// A view of the current and next row as a vertical pair. + main: VerticalPair, RowMajorMatrixView<'a, F>>, + /// The public values provided for constraint validation (e.g. inputs or outputs). + public_values: &'a [F], + /// A flag indicating whether this is the first row. + is_first_row: F, + /// A flag indicating whether this is the last row. + is_last_row: F, + /// A flag indicating whether this is a transition row (not the last row). + is_transition: F, + /// The periodic columns provided for constraint validation. + periodic_columns: Vec>, +} + +impl<'a, F> AirBuilderWithPeriodicColumns for DebugConstraintBuilderWithPeriodicColumns<'a, F> +where + F: Field + Into, +{ + type PeriodicColumnsVar = F; + + fn periodic_columns(&self) -> Vec { + self.periodic_columns + .iter() + .map(|col| col[self.row_index % col.len()]) + .collect::>() + } +} + +impl<'a, F> AirBuilder for DebugConstraintBuilderWithPeriodicColumns<'a, F> +where + F: Field, +{ + type F = F; + type Expr = F; + type Var = F; + type M = VerticalPair, RowMajorMatrixView<'a, F>>; + + fn main(&self) -> Self::M { + self.main + } + + fn is_first_row(&self) -> Self::Expr { + self.is_first_row + } + + fn is_last_row(&self) -> Self::Expr { + self.is_last_row + } + + /// # Panics + /// This function panics if `size` is not `2`. + fn is_transition_window(&self, size: usize) -> Self::Expr { + if size == 2 { + self.is_transition + } else { + panic!("only supports a window size of 2") + } + } + + fn assert_zero>(&mut self, x: I) { + assert_eq!(x.into(), F::ZERO, "constraints had nonzero value on row {}", self.row_index); + } + + fn assert_eq, I2: Into>(&mut self, x: I1, y: I2) { + let x = x.into(); + let y = y.into(); + assert_eq!(x, y, "values didn't match on row {}: {} != {}", self.row_index, x, y); + } +} + +impl<'a, F> AirBuilderWithPublicValues for DebugConstraintBuilderWithPeriodicColumns<'a, F> +where + F: Field, +{ + type PublicVar = Self::F; + + fn public_values(&self) -> &[Self::F] { + self.public_values + } +} diff --git a/air-script/tests/helpers/winterfell_test_helpers.rs b/air-script/tests/helpers/winterfell_test_helpers.rs new file mode 100644 index 000000000..5f91493d8 --- /dev/null +++ b/air-script/tests/helpers/winterfell_test_helpers.rs @@ -0,0 +1,73 @@ +use winter_air::{BatchingMethod, EvaluationFrame, FieldExtension, ProofOptions, TraceInfo}; +use winter_math::fields::f64::BaseElement as Felt; +use winterfell::{AuxTraceWithMetadata, Trace, TraceTable, matrix::ColMatrix}; + +/// We need to encapsulate the trace table in a struct to manually implement the `aux_trace_width` +/// method of the `Table` trait. Otherwise, using only a TraceTable will return an +/// `aux_trace_width` of 0 even if we provide a non-empty aux trace in `Trace::validate`, +/// and it fails the tests. +pub struct MyTraceTable { + pub trace: TraceTable, + pub aux_width: usize, +} + +impl MyTraceTable { + pub fn new(trace: TraceTable, aux_width: usize) -> Self { + Self { trace, aux_width } + } +} + +impl Trace for MyTraceTable { + type BaseField = Felt; + + fn info(&self) -> &TraceInfo { + self.trace.info() + } + + fn main_segment(&self) -> &ColMatrix { + self.trace.main_segment() + } + + fn read_main_frame(&self, row_idx: usize, frame: &mut EvaluationFrame) { + self.trace.read_main_frame(row_idx, frame); + } + + fn aux_trace_width(&self) -> usize { + self.aux_width + } +} + +pub trait AirTester { + type PubInputs; + + fn build_main_trace(&self, length: usize) -> MyTraceTable; + fn public_inputs(&self) -> Self::PubInputs; + fn build_aux_trace(&self, _length: usize) -> Option> { + None + } + fn build_trace_info(&self, length: usize) -> TraceInfo { + match &self.build_aux_trace(length) { + None => TraceInfo::new(self.build_main_trace(length).trace.width(), length), + Some(aux_trace) => TraceInfo::new_multi_segment( + self.build_main_trace(length).trace.width(), + aux_trace.aux_trace.num_cols(), + aux_trace.aux_rand_elements.rand_elements().len(), + length, + vec![], + ), + } + } + fn build_proof_options(&self) -> ProofOptions { + ProofOptions::new( + 32, // number of queries + 8, // blowup factor + 0, // grinding factor + FieldExtension::None, + 8, // FRI folding factor + 31, // FRI max remainder polynomial degree + BatchingMethod::Linear, /* method of batching used in computing constraint + * composition polynomial */ + BatchingMethod::Linear, // method of batching used in computing DEEP polynomial + ) + } +} diff --git a/codegen/plonky3/src/air/boundary_constraints.rs b/codegen/plonky3/src/air/boundary_constraints.rs index 19a3519d1..5ae7d1363 100644 --- a/codegen/plonky3/src/air/boundary_constraints.rs +++ b/codegen/plonky3/src/air/boundary_constraints.rs @@ -8,7 +8,7 @@ pub(super) fn add_main_boundary_constraints(eval_func: &mut Function, ir: &Air) for constraint in ir.boundary_constraints(TraceSegmentId::Main) { let expr_root = constraint.node_index(); - let expr_root_string = expr_root.to_string(ir, TraceSegmentId::Main); + let expr_root_string = expr_root.to_string(ir); let assertion = format!("builder.when_first_row().assert_zero::<_>({expr_root_string});"); diff --git a/codegen/plonky3/src/air/graph.rs b/codegen/plonky3/src/air/graph.rs index 3f987b7d3..56d5a90cf 100644 --- a/codegen/plonky3/src/air/graph.rs +++ b/codegen/plonky3/src/air/graph.rs @@ -1,4 +1,4 @@ -use air_ir::{Air, NodeIndex, Operation, TraceAccess, TraceSegmentId, Value}; +use air_ir::{Air, NodeIndex, Operation, TraceAccess, Value}; // RUST STRING GENERATION FOR THE CONSTRAINT GRAPH // ================================================================================================ @@ -6,11 +6,11 @@ use air_ir::{Air, NodeIndex, Operation, TraceAccess, TraceSegmentId, Value}; /// Code generation trait for generating Rust code strings from IR types related to constraints and /// the [AlgebraicGraph]. pub trait Codegen { - fn to_string(&self, ir: &Air, trace_segment: TraceSegmentId) -> String; + fn to_string(&self, ir: &Air) -> String; } impl Codegen for TraceAccess { - fn to_string(&self, _ir: &Air, trace_segment: TraceSegmentId) -> String { + fn to_string(&self, _ir: &Air) -> String { let frame = self.segment.to_string(); let row_offset = match self.row_offset { 0 => { @@ -21,37 +21,33 @@ impl Codegen for TraceAccess { }, _ => panic!("Plonky3 doesn't support row offsets greater than 1."), }; - if self.segment == TraceSegmentId::Main && self.segment != trace_segment { - format!("E::from({frame}_{row_offset})") - } else { - format!("{frame}_{row_offset}") - } + format!("{frame}_{row_offset}") } } impl Codegen for NodeIndex { - fn to_string(&self, ir: &Air, trace_segment: TraceSegmentId) -> String { + fn to_string(&self, ir: &Air) -> String { let op = ir.constraint_graph().node(self).op(); - op.to_string(ir, trace_segment) + op.to_string(ir) } } impl Codegen for Operation { - fn to_string(&self, ir: &Air, trace_segment: TraceSegmentId) -> String { + fn to_string(&self, ir: &Air) -> String { match self { - Operation::Value(value) => value.to_string(ir, trace_segment), - Operation::Add(..) => binary_op_to_string(ir, self, trace_segment), - Operation::Sub(..) => binary_op_to_string(ir, self, trace_segment), - Operation::Mul(..) => binary_op_to_string(ir, self, trace_segment), + Operation::Value(value) => value.to_string(ir), + Operation::Add(..) => binary_op_to_string(ir, self), + Operation::Sub(..) => binary_op_to_string(ir, self), + Operation::Mul(..) => binary_op_to_string(ir, self), } } } impl Codegen for Value { - fn to_string(&self, ir: &Air, trace_segment: TraceSegmentId) -> String { + fn to_string(&self, ir: &Air) -> String { match self { Value::Constant(value) => format!("AB::Expr::from(AB::F::from_u64({value}))"), - Value::TraceAccess(trace_access) => trace_access.to_string(ir, trace_segment), + Value::TraceAccess(trace_access) => trace_access.to_string(ir), Value::PublicInput(air_ir::PublicInputAccess { name, index }) => { let get_public_input_offset = |name: &str| { ir.public_inputs() @@ -61,13 +57,13 @@ impl Codegen for Value { }; format!("public_values[{}].into()", get_public_input_offset(name.as_str()) + index) }, - _ => todo!(), - /*Value::PeriodicColumn(pc) => { + Value::PeriodicColumn(pc) => { let index = ir.periodic_columns.iter().position(|(qid, _)| qid == &pc.name).unwrap(); - format!("periodic_values[{index}]") + format!("periodic_values[{index}].into()") }, - Value::PublicInputTable(air_ir::PublicInputTableAccess { + _ => todo!(), + /*Value::PublicInputTable(air_ir::PublicInputTableAccess { table_name, bus_type, num_cols: _, @@ -82,32 +78,32 @@ impl Codegen for Value { } /// Returns a string representation of a binary operation. -fn binary_op_to_string(ir: &Air, op: &Operation, trace_segment: TraceSegmentId) -> String { +fn binary_op_to_string(ir: &Air, op: &Operation) -> String { match op { Operation::Add(l_idx, r_idx) => { - let lhs = l_idx.to_string(ir, trace_segment); - let rhs = r_idx.to_string(ir, trace_segment); + let lhs = l_idx.to_string(ir); + let rhs = r_idx.to_string(ir); format!("{lhs} + {rhs}") }, Operation::Sub(l_idx, r_idx) => { - let lhs = l_idx.to_string(ir, trace_segment); + let lhs = l_idx.to_string(ir); let rhs = if ir.constraint_graph().node(r_idx).op().precedence() <= op.precedence() { - format!("({})", r_idx.to_string(ir, trace_segment)) + format!("({})", r_idx.to_string(ir)) } else { - r_idx.to_string(ir, trace_segment) + r_idx.to_string(ir) }; format!("{lhs} - {rhs}") }, Operation::Mul(l_idx, r_idx) => { let lhs = if ir.constraint_graph().node(l_idx).op().precedence() < op.precedence() { - format!("({})", l_idx.to_string(ir, trace_segment)) + format!("({})", l_idx.to_string(ir)) } else { - l_idx.to_string(ir, trace_segment) + l_idx.to_string(ir) }; let rhs = if ir.constraint_graph().node(r_idx).op().precedence() < op.precedence() { - format!("({})", r_idx.to_string(ir, trace_segment)) + format!("({})", r_idx.to_string(ir)) } else { - r_idx.to_string(ir, trace_segment) + r_idx.to_string(ir) }; format!("{lhs} * {rhs}") }, diff --git a/codegen/plonky3/src/air/integrity_constraints.rs b/codegen/plonky3/src/air/integrity_constraints.rs index 78dae2d55..0d79dfcf0 100644 --- a/codegen/plonky3/src/air/integrity_constraints.rs +++ b/codegen/plonky3/src/air/integrity_constraints.rs @@ -7,7 +7,7 @@ use super::Codegen; pub(super) fn add_main_integrity_constraints(eval_func: &mut Function, ir: &Air) { for constraint in ir.integrity_constraints(TraceSegmentId::Main) { let expr_root = constraint.node_index(); - let expr_root_string = expr_root.to_string(ir, TraceSegmentId::Main); + let expr_root_string = expr_root.to_string(ir); // If the constraint is a transition constraint (depends on the next row), we do not // evaluate it in the last row, with the `when_transition` method. diff --git a/codegen/plonky3/src/air/mod.rs b/codegen/plonky3/src/air/mod.rs index 79c533a73..a11287f87 100644 --- a/codegen/plonky3/src/air/mod.rs +++ b/codegen/plonky3/src/air/mod.rs @@ -49,6 +49,27 @@ fn add_air_struct(scope: &mut Scope, ir: &Air, name: &str) { .arg_ref_self() .ret("usize") .line("NUM_PUBLIC_VALUES"); + + // add the custom BaseAirWithPeriodicColumns implementation block + let base_air_with_periodic_columns_impl = scope + .new_impl(name) + .generic("F: PrimeCharacteristicRing") + .impl_trait("BaseAirWithPeriodicColumns"); + let base_air_with_periodic_columns_impl_func = base_air_with_periodic_columns_impl + .new_fn("get_periodic_columns") + .arg_ref_self() + .ret("Vec>"); + base_air_with_periodic_columns_impl_func.line("vec!["); + + for col in ir.periodic_columns() { + let values_str = col.values + .iter() + .map(|v| format!("F::from_u64({v})")) // or use a custom formatter if needed + .collect::>() + .join(", "); + base_air_with_periodic_columns_impl_func.line(format!(" vec![{values_str}],")); + } + base_air_with_periodic_columns_impl_func.line("]"); } /// Updates the provided scope with the custom Air struct and an Air trait implementation based on @@ -57,12 +78,13 @@ fn add_air_trait(scope: &mut Scope, ir: &Air, name: &str) { // add the implementation block for the Air trait. let air_impl = scope .new_impl(name) - .generic("AB: AirBuilderWithPublicValues") + .generic("AB: AirBuilderWithPublicValues + AirBuilderWithPeriodicColumns") .impl_trait("Air"); let eval_func = air_impl.new_fn("eval").arg_ref_self().arg("builder", "&mut AB"); eval_func.line("let main = builder.main();"); eval_func.line("let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect(\"Wrong number of public values\");"); + eval_func.line("let periodic_values = builder.periodic_columns();"); eval_func.line("let (main_current, main_next) = ("); eval_func.line(" main.row_slice(0).unwrap(),"); eval_func.line(" main.row_slice(1).unwrap(),"); diff --git a/codegen/plonky3/src/imports.rs b/codegen/plonky3/src/imports.rs index 7663096bb..28ef3501b 100644 --- a/codegen/plonky3/src/imports.rs +++ b/codegen/plonky3/src/imports.rs @@ -10,4 +10,6 @@ pub(super) fn add_imports(scope: &mut Scope) { scope.import("p3_air", "BaseAirWithPublicValues"); scope.import("p3_matrix", "Matrix"); scope.import("p3_field", "PrimeCharacteristicRing"); + scope.import("crate::helpers", "AirBuilderWithPeriodicColumns"); + scope.import("crate::helpers", "BaseAirWithPeriodicColumns"); } From 6c7c7772cc0aba564cbbeadb018d49e17daffda3 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Wed, 24 Sep 2025 13:45:05 +0200 Subject: [PATCH 10/83] fix(codegen): Correctly handle boundary constraints domain --- codegen/plonky3/src/air/boundary_constraints.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/codegen/plonky3/src/air/boundary_constraints.rs b/codegen/plonky3/src/air/boundary_constraints.rs index 5ae7d1363..4c8fd0047 100644 --- a/codegen/plonky3/src/air/boundary_constraints.rs +++ b/codegen/plonky3/src/air/boundary_constraints.rs @@ -10,8 +10,15 @@ pub(super) fn add_main_boundary_constraints(eval_func: &mut Function, ir: &Air) let expr_root_string = expr_root.to_string(ir); - let assertion = format!("builder.when_first_row().assert_zero::<_>({expr_root_string});"); - + let assertion = match constraint.domain() { + air_ir::ConstraintDomain::FirstRow => { + format!("builder.when_first_row().assert_zero::<_>({expr_root_string});") + }, + air_ir::ConstraintDomain::LastRow => { + format!("builder.when_last_row().assert_zero::<_>({expr_root_string});") + }, + _ => unreachable!("Boundary constraints can only be applied to the first or last row"), + }; eval_func.line(assertion); } } From 96e741c8f4e3c6fcfb4f9ac2039fefb2754ec1ae Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Wed, 24 Sep 2025 13:45:29 +0200 Subject: [PATCH 11/83] tests: generate and test all E2E Plonky3 tests --- air-script/tests/binary/mod.rs | 6 +- .../tests/buses/buses_complex_plonky3.rs | 44 +++ .../tests/buses/buses_simple_plonky3.rs | 41 +++ .../buses_varlen_boundary_both_plonky3.rs | 41 +++ .../buses_varlen_boundary_first_plonky3.rs | 41 +++ .../buses_varlen_boundary_last_plonky3.rs | 41 +++ air-script/tests/buses/mod.rs | 25 +- air-script/tests/buses/test_air_plonky3.rs | 84 +++++ .../{test_air.rs => test_air_winterfell.rs} | 0 air-script/tests/codegen/plonky3.rs | 289 +++++++++++++++++- air-script/tests/codegen/winterfell.rs | 3 - .../constant_in_range_plonky3.rs | 43 +++ air-script/tests/constant_in_range/mod.rs | 8 +- .../constant_in_range/test_air_plonky3.rs | 103 +++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 .../tests/constants/constants_plonky3.rs | 52 ++++ air-script/tests/constants/mod.rs | 8 +- .../tests/constants/test_air_plonky3.rs | 99 ++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 .../constraint_comprehension_plonky3.rs | 46 +++ .../tests/constraint_comprehension/mod.rs | 8 +- .../test_air_plonky3.rs | 86 ++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 .../tests/evaluators/evaluators_plonky3.rs | 52 ++++ air-script/tests/evaluators/mod.rs | 8 +- .../tests/evaluators/test_air_plonky3.rs | 99 ++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 .../tests/fibonacci/fibonacci_plonky3.rs | 46 +++ air-script/tests/fibonacci/mod.rs | 8 +- .../tests/fibonacci/test_air_plonky3.rs | 87 ++++++ .../{test_air.rs => test_air_winterfell.rs} | 2 +- .../functions/functions_complex_plonky3.rs | 44 +++ .../functions/functions_simple_plonky3.rs | 50 +++ air-script/tests/functions/mod.rs | 12 +- .../tests/functions/test_air_plonky3.rs | 129 ++++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 .../indexed_trace_access_plonky3.rs | 43 +++ air-script/tests/indexed_trace_access/mod.rs | 8 +- .../indexed_trace_access/test_air_plonky3.rs | 86 ++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 .../list_comprehension_nested_plonky3.rs | 45 +++ .../list_comprehension_plonky3.rs | 48 +++ air-script/tests/list_comprehension/mod.rs | 8 +- .../list_comprehension/test_air_plonky3.rs | 126 ++++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 .../list_folding/list_folding_plonky3.rs | 46 +++ air-script/tests/list_folding/mod.rs | 8 +- .../tests/list_folding/test_air_plonky3.rs | 129 ++++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 air-script/tests/periodic_columns/mod.rs | 8 +- .../periodic_columns_plonky3.rs | 46 +++ .../periodic_columns/test_air_plonky3.rs | 87 ++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 air-script/tests/pub_inputs/mod.rs | 8 +- .../tests/pub_inputs/pub_inputs_plonky3.rs | 50 +++ .../tests/pub_inputs/test_air_plonky3.rs | 90 ++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 air-script/tests/selectors/mod.rs | 16 +- .../selectors_combine_complex_plonky3.rs | 45 +++ .../selectors_combine_simple_plonky3.rs | 44 +++ .../tests/selectors/selectors_plonky3.rs | 44 +++ .../selectors_with_evaluators_plonky3.rs | 44 +++ .../tests/selectors/test_air_plonky3.rs | 90 ++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 air-script/tests/system/mod.rs | 8 +- air-script/tests/system/system_plonky3.rs | 43 +++ air-script/tests/system/test_air_plonky3.rs | 87 ++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 air-script/tests/trace_col_groups/mod.rs | 8 +- .../trace_col_groups/test_air_plonky3.rs | 105 +++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 .../trace_col_groups_plonky3.rs | 44 +++ air-script/tests/variables/mod.rs | 7 +- .../tests/variables/test_air_plonky3.rs | 90 ++++++ .../{test_air.rs => test_air_winterfell.rs} | 0 .../tests/variables/variables_plonky3.rs | 48 +++ 76 files changed, 3140 insertions(+), 24 deletions(-) create mode 100644 air-script/tests/buses/buses_complex_plonky3.rs create mode 100644 air-script/tests/buses/buses_simple_plonky3.rs create mode 100644 air-script/tests/buses/buses_varlen_boundary_both_plonky3.rs create mode 100644 air-script/tests/buses/buses_varlen_boundary_first_plonky3.rs create mode 100644 air-script/tests/buses/buses_varlen_boundary_last_plonky3.rs create mode 100644 air-script/tests/buses/test_air_plonky3.rs rename air-script/tests/buses/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/constant_in_range/constant_in_range_plonky3.rs create mode 100644 air-script/tests/constant_in_range/test_air_plonky3.rs rename air-script/tests/constant_in_range/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/constants/constants_plonky3.rs create mode 100644 air-script/tests/constants/test_air_plonky3.rs rename air-script/tests/constants/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/constraint_comprehension/constraint_comprehension_plonky3.rs create mode 100644 air-script/tests/constraint_comprehension/test_air_plonky3.rs rename air-script/tests/constraint_comprehension/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/evaluators/evaluators_plonky3.rs create mode 100644 air-script/tests/evaluators/test_air_plonky3.rs rename air-script/tests/evaluators/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/fibonacci/fibonacci_plonky3.rs create mode 100644 air-script/tests/fibonacci/test_air_plonky3.rs rename air-script/tests/fibonacci/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/functions/functions_complex_plonky3.rs create mode 100644 air-script/tests/functions/functions_simple_plonky3.rs create mode 100644 air-script/tests/functions/test_air_plonky3.rs rename air-script/tests/functions/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/indexed_trace_access/indexed_trace_access_plonky3.rs create mode 100644 air-script/tests/indexed_trace_access/test_air_plonky3.rs rename air-script/tests/indexed_trace_access/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/list_comprehension/list_comprehension_nested_plonky3.rs create mode 100644 air-script/tests/list_comprehension/list_comprehension_plonky3.rs create mode 100644 air-script/tests/list_comprehension/test_air_plonky3.rs rename air-script/tests/list_comprehension/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/list_folding/list_folding_plonky3.rs create mode 100644 air-script/tests/list_folding/test_air_plonky3.rs rename air-script/tests/list_folding/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/periodic_columns/periodic_columns_plonky3.rs create mode 100644 air-script/tests/periodic_columns/test_air_plonky3.rs rename air-script/tests/periodic_columns/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/pub_inputs/pub_inputs_plonky3.rs create mode 100644 air-script/tests/pub_inputs/test_air_plonky3.rs rename air-script/tests/pub_inputs/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/selectors/selectors_combine_complex_plonky3.rs create mode 100644 air-script/tests/selectors/selectors_combine_simple_plonky3.rs create mode 100644 air-script/tests/selectors/selectors_plonky3.rs create mode 100644 air-script/tests/selectors/selectors_with_evaluators_plonky3.rs create mode 100644 air-script/tests/selectors/test_air_plonky3.rs rename air-script/tests/selectors/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/system/system_plonky3.rs create mode 100644 air-script/tests/system/test_air_plonky3.rs rename air-script/tests/system/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/trace_col_groups/test_air_plonky3.rs rename air-script/tests/trace_col_groups/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/trace_col_groups/trace_col_groups_plonky3.rs create mode 100644 air-script/tests/variables/test_air_plonky3.rs rename air-script/tests/variables/{test_air.rs => test_air_winterfell.rs} (100%) create mode 100644 air-script/tests/variables/variables_plonky3.rs diff --git a/air-script/tests/binary/mod.rs b/air-script/tests/binary/mod.rs index fa03709f2..08cbf3225 100644 --- a/air-script/tests/binary/mod.rs +++ b/air-script/tests/binary/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] mod binary; -mod test_air_winterfell; - -mod test_air_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] mod binary_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/buses/buses_complex_plonky3.rs b/air-script/tests/buses/buses_complex_plonky3.rs new file mode 100644 index 000000000..76905b3a8 --- /dev/null +++ b/air-script/tests/buses/buses_complex_plonky3.rs @@ -0,0 +1,44 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 5; + +pub const NUM_PUBLIC_VALUES: usize = 2; + +pub struct BusesAir; + +impl BaseAir for BusesAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for BusesAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for BusesAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for BusesAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[0]); + builder.assert_zero::<_>(main_current[2] * main_current[2] - main_current[2]); + builder.assert_zero::<_>(main_current[3] * main_current[3] - main_current[3]); + } +} \ No newline at end of file diff --git a/air-script/tests/buses/buses_simple_plonky3.rs b/air-script/tests/buses/buses_simple_plonky3.rs new file mode 100644 index 000000000..6ea46ea4b --- /dev/null +++ b/air-script/tests/buses/buses_simple_plonky3.rs @@ -0,0 +1,41 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 1; + +pub const NUM_PUBLIC_VALUES: usize = 2; + +pub struct BusesAir; + +impl BaseAir for BusesAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for BusesAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for BusesAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for BusesAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + } +} \ No newline at end of file diff --git a/air-script/tests/buses/buses_varlen_boundary_both_plonky3.rs b/air-script/tests/buses/buses_varlen_boundary_both_plonky3.rs new file mode 100644 index 000000000..e8f0c1253 --- /dev/null +++ b/air-script/tests/buses/buses_varlen_boundary_both_plonky3.rs @@ -0,0 +1,41 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 1; + +pub const NUM_PUBLIC_VALUES: usize = 6; + +pub struct BusesAir; + +impl BaseAir for BusesAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for BusesAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for BusesAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for BusesAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + } +} \ No newline at end of file diff --git a/air-script/tests/buses/buses_varlen_boundary_first_plonky3.rs b/air-script/tests/buses/buses_varlen_boundary_first_plonky3.rs new file mode 100644 index 000000000..6ea46ea4b --- /dev/null +++ b/air-script/tests/buses/buses_varlen_boundary_first_plonky3.rs @@ -0,0 +1,41 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 1; + +pub const NUM_PUBLIC_VALUES: usize = 2; + +pub struct BusesAir; + +impl BaseAir for BusesAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for BusesAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for BusesAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for BusesAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + } +} \ No newline at end of file diff --git a/air-script/tests/buses/buses_varlen_boundary_last_plonky3.rs b/air-script/tests/buses/buses_varlen_boundary_last_plonky3.rs new file mode 100644 index 000000000..6ea46ea4b --- /dev/null +++ b/air-script/tests/buses/buses_varlen_boundary_last_plonky3.rs @@ -0,0 +1,41 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 1; + +pub const NUM_PUBLIC_VALUES: usize = 2; + +pub struct BusesAir; + +impl BaseAir for BusesAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for BusesAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for BusesAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for BusesAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + } +} \ No newline at end of file diff --git a/air-script/tests/buses/mod.rs b/air-script/tests/buses/mod.rs index 933d13ee5..95eeeb07b 100644 --- a/air-script/tests/buses/mod.rs +++ b/air-script/tests/buses/mod.rs @@ -13,4 +13,27 @@ mod buses_varlen_boundary_first; #[rustfmt::skip] #[allow(clippy::all)] mod buses_varlen_boundary_last; -mod test_air; + +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod buses_complex_plonky3; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod buses_simple_plonky3; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod buses_varlen_boundary_both_plonky3; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod buses_varlen_boundary_first_plonky3; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod buses_varlen_boundary_last_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/buses/test_air_plonky3.rs b/air-script/tests/buses/test_air_plonky3.rs new file mode 100644 index 000000000..e465396df --- /dev/null +++ b/air-script/tests/buses/test_air_plonky3.rs @@ -0,0 +1,84 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + buses::buses_complex_plonky3::{BusesAir, NUM_COLUMNS}, + helpers::check_constraints_with_periodic_columns, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let a_prev = rows[i - 1][0]; + let b_prev = rows[i - 1][1]; + + // Update current row based on previous values + rows[i][0] = F::ONE - a_prev; + rows[i][1] = F::ONE - b_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 2]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&BusesAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/buses/test_air.rs b/air-script/tests/buses/test_air_winterfell.rs similarity index 100% rename from air-script/tests/buses/test_air.rs rename to air-script/tests/buses/test_air_winterfell.rs diff --git a/air-script/tests/codegen/plonky3.rs b/air-script/tests/codegen/plonky3.rs index 1c946a90f..bb7b3a24c 100644 --- a/air-script/tests/codegen/plonky3.rs +++ b/air-script/tests/codegen/plonky3.rs @@ -12,4 +12,291 @@ fn binary() { expected.assert_eq(&generated_air); } -// TODO: add all tests +#[test] +fn buses_simple() { + let generated_air = Test::new("tests/buses/buses_simple.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../buses/buses_simple_plonky3.rs"]; + expected.assert_eq(&generated_air); +} +#[test] +fn buses_simple_with_evaluators() { + let generated_air = Test::new("tests/buses/buses_simple_with_evaluators.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../buses/buses_simple_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_complex() { + let generated_air = Test::new("tests/buses/buses_complex.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../buses/buses_complex_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_varlen_boundary_first() { + let generated_air = Test::new("tests/buses/buses_varlen_boundary_first.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../buses/buses_varlen_boundary_first_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_varlen_boundary_last() { + let generated_air = Test::new("tests/buses/buses_varlen_boundary_last.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../buses/buses_varlen_boundary_last_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_varlen_boundary_both() { + let generated_air = Test::new("tests/buses/buses_varlen_boundary_both.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../buses/buses_varlen_boundary_both_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn periodic_columns() { + let generated_air = Test::new("tests/periodic_columns/periodic_columns.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../periodic_columns/periodic_columns_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn pub_inputs() { + let generated_air = Test::new("tests/pub_inputs/pub_inputs.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../pub_inputs/pub_inputs_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn system() { + let generated_air = Test::new("tests/system/system.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../system/system_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn bitwise() { + let generated_air = Test::new("tests/bitwise/bitwise.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../bitwise/bitwise_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn constants() { + let generated_air = Test::new("tests/constants/constants.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../constants/constants_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn constant_in_range() { + let generated_air = Test::new("tests/constant_in_range/constant_in_range.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../constant_in_range/constant_in_range_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn evaluators() { + let generated_air = Test::new("tests/evaluators/evaluators.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../evaluators/evaluators_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn fibonacci() { + let generated_air = Test::new("tests/fibonacci/fibonacci.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../fibonacci/fibonacci_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn functions_simple() { + let generated_air = Test::new("tests/functions/functions_simple.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../functions/functions_simple_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn functions_simple_inlined() { + // make sure that the constraints generated using inlined functions are the same as the ones + // generated using regular functions + let generated_air = Test::new("tests/functions/inlined_functions_simple.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../functions/functions_simple_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn functions_complex() { + let generated_air = Test::new("tests/functions/functions_complex.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../functions/functions_complex_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn variables() { + let generated_air = Test::new("tests/variables/variables.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../variables/variables_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn trace_col_groups() { + let generated_air = Test::new("tests/trace_col_groups/trace_col_groups.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../trace_col_groups/trace_col_groups_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn indexed_trace_access() { + let generated_air = + Test::new("tests/indexed_trace_access/indexed_trace_access.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../indexed_trace_access/indexed_trace_access_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn list_comprehension() { + let generated_air = Test::new("tests/list_comprehension/list_comprehension.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../list_comprehension/list_comprehension_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn list_comprehension_nested() { + let generated_air = + Test::new("tests/list_comprehension/list_comprehension_nested.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../list_comprehension/list_comprehension_nested_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn list_folding() { + let generated_air = Test::new("tests/list_folding/list_folding.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../list_folding/list_folding_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors() { + let generated_air = Test::new("tests/selectors/selectors.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../selectors/selectors_plonky3.rs"]; + expected.assert_eq(&generated_air); + + let generated_air = Test::new("tests/selectors/selectors_with_evaluators.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../selectors/selectors_with_evaluators_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors_combine_simple() { + let generated_air = Test::new("tests/selectors/selectors_combine_simple.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../selectors/selectors_combine_simple_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors_combine_complex() { + let generated_air = Test::new("tests/selectors/selectors_combine_complex.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../selectors/selectors_combine_complex_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn constraint_comprehension() { + let generated_air = + Test::new("tests/constraint_comprehension/constraint_comprehension.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../constraint_comprehension/constraint_comprehension_plonky3.rs"]; + expected.assert_eq(&generated_air); + + let generated_air = + Test::new("tests/constraint_comprehension/cc_with_evaluators.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../constraint_comprehension/constraint_comprehension_plonky3.rs"]; + expected.assert_eq(&generated_air); +} diff --git a/air-script/tests/codegen/winterfell.rs b/air-script/tests/codegen/winterfell.rs index 1b2348274..aa3f71086 100644 --- a/air-script/tests/codegen/winterfell.rs +++ b/air-script/tests/codegen/winterfell.rs @@ -2,9 +2,6 @@ use expect_test::expect_file; use super::helpers::{Target, Test}; -// tests_wo_mir -// ================================================================================================ - #[test] fn binary() { let generated_air = Test::new("tests/binary/binary.air".to_string()) diff --git a/air-script/tests/constant_in_range/constant_in_range_plonky3.rs b/air-script/tests/constant_in_range/constant_in_range_plonky3.rs new file mode 100644 index 000000000..6b23d4b17 --- /dev/null +++ b/air-script/tests/constant_in_range/constant_in_range_plonky3.rs @@ -0,0 +1,43 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 12; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct ConstantInRangeAir; + +impl BaseAir for ConstantInRangeAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for ConstantInRangeAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for ConstantInRangeAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for ConstantInRangeAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[6]); + builder.assert_zero::<_>(main_current[0] - (main_current[1] - main_current[4] - main_current[8] + AB::Expr::from(AB::F::from_u64(1)) + main_current[2] - main_current[5] - main_current[9] + AB::Expr::from(AB::F::from_u64(2)) + main_current[3] - main_current[6] - main_current[10])); + } +} \ No newline at end of file diff --git a/air-script/tests/constant_in_range/mod.rs b/air-script/tests/constant_in_range/mod.rs index a071cd958..68a0d2cbd 100644 --- a/air-script/tests/constant_in_range/mod.rs +++ b/air-script/tests/constant_in_range/mod.rs @@ -1,4 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] mod constant_in_range; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod constant_in_range_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/constant_in_range/test_air_plonky3.rs b/air-script/tests/constant_in_range/test_air_plonky3.rs new file mode 100644 index 000000000..15af390e6 --- /dev/null +++ b/air-script/tests/constant_in_range/test_air_plonky3.rs @@ -0,0 +1,103 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + constant_in_range::constant_in_range_plonky3::{ConstantInRangeAir, NUM_COLUMNS}, + helpers::check_constraints_with_periodic_columns, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::from_canonical_checked(3).unwrap(); + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + let col_3_prev = rows[i - 1][3]; + let col_4_prev = rows[i - 1][4]; + let col_5_prev = rows[i - 1][5]; + let col_6_prev = rows[i - 1][6]; + let col_7_prev = rows[i - 1][7]; + let col_8_prev = rows[i - 1][8]; + let col_9_prev = rows[i - 1][9]; + let col_10_prev = rows[i - 1][10]; + let col_11_prev = rows[i - 1][11]; + + // Update current row based on previous values + rows[i][0] = col_0_prev; + rows[i][1] = col_1_prev; + rows[i][2] = col_2_prev; + rows[i][3] = col_3_prev; + rows[i][4] = col_4_prev; + rows[i][5] = col_5_prev; + rows[i][6] = col_6_prev; + rows[i][7] = col_7_prev; + rows[i][8] = col_8_prev; + rows[i][9] = col_9_prev; + rows[i][10] = col_10_prev; + rows[i][11] = col_11_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&ConstantInRangeAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/constant_in_range/test_air.rs b/air-script/tests/constant_in_range/test_air_winterfell.rs similarity index 100% rename from air-script/tests/constant_in_range/test_air.rs rename to air-script/tests/constant_in_range/test_air_winterfell.rs diff --git a/air-script/tests/constants/constants_plonky3.rs b/air-script/tests/constants/constants_plonky3.rs new file mode 100644 index 000000000..9361c0228 --- /dev/null +++ b/air-script/tests/constants/constants_plonky3.rs @@ -0,0 +1,52 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 7; + +pub const NUM_PUBLIC_VALUES: usize = 32; + +pub struct ConstantsAir; + +impl BaseAir for ConstantsAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for ConstantsAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for ConstantsAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for ConstantsAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[0] - AB::Expr::from(AB::F::from_u64(1))); + builder.when_first_row().assert_zero::<_>(main_current[1] - AB::Expr::from(AB::F::from_u64(1))); + builder.when_first_row().assert_zero::<_>(main_current[2]); + builder.when_first_row().assert_zero::<_>(main_current[3] - AB::Expr::from(AB::F::from_u64(1))); + builder.when_first_row().assert_zero::<_>(main_current[4] - AB::Expr::from(AB::F::from_u64(1))); + builder.when_last_row().assert_zero::<_>(main_current[6]); + builder.when_transition().assert_zero::<_>(main_next[0] - (main_current[0] + AB::Expr::from(AB::F::from_u64(1)))); + builder.when_transition().assert_zero::<_>(main_next[1]); + builder.when_transition().assert_zero::<_>(main_next[2] - main_current[2]); + builder.when_transition().assert_zero::<_>(main_next[5] - (main_current[5] + AB::Expr::from(AB::F::from_u64(1)))); + builder.assert_zero::<_>(main_current[4] - AB::Expr::from(AB::F::from_u64(1))); + } +} \ No newline at end of file diff --git a/air-script/tests/constants/mod.rs b/air-script/tests/constants/mod.rs index 526f76f5e..1540ba337 100644 --- a/air-script/tests/constants/mod.rs +++ b/air-script/tests/constants/mod.rs @@ -1,4 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] mod constants; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod constants_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/constants/test_air_plonky3.rs b/air-script/tests/constants/test_air_plonky3.rs new file mode 100644 index 000000000..f4d12edd5 --- /dev/null +++ b/air-script/tests/constants/test_air_plonky3.rs @@ -0,0 +1,99 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + constants::constants_plonky3::{ConstantsAir, NUM_COLUMNS}, + helpers::check_constraints_with_periodic_columns, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ONE; + rows[0][1] = F::ONE; + rows[0][2] = F::ZERO; + rows[0][3] = F::ONE; + rows[0][4] = F::ONE; + rows[0][5] = F::ZERO; + rows[0][6] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + let col_3_prev = rows[i - 1][3]; + let col_4_prev = rows[i - 1][4]; + let col_5_prev = rows[i - 1][5]; + let col_6_prev = rows[i - 1][6]; + + // Update current row based on previous values + rows[i][0] = col_0_prev + F::ONE; + rows[i][1] = F::ZERO; + rows[i][2] = col_2_prev; + rows[i][3] = col_3_prev; + rows[i][4] = col_4_prev; + rows[i][5] = col_5_prev + F::ONE; + rows[i][6] = col_6_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 32]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&ConstantsAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/constants/test_air.rs b/air-script/tests/constants/test_air_winterfell.rs similarity index 100% rename from air-script/tests/constants/test_air.rs rename to air-script/tests/constants/test_air_winterfell.rs diff --git a/air-script/tests/constraint_comprehension/constraint_comprehension_plonky3.rs b/air-script/tests/constraint_comprehension/constraint_comprehension_plonky3.rs new file mode 100644 index 000000000..ef6d701c1 --- /dev/null +++ b/air-script/tests/constraint_comprehension/constraint_comprehension_plonky3.rs @@ -0,0 +1,46 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 14; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct ConstraintComprehensionAir; + +impl BaseAir for ConstraintComprehensionAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for ConstraintComprehensionAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for ConstraintComprehensionAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for ConstraintComprehensionAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[8]); + builder.assert_zero::<_>(main_current[6] - main_current[10]); + builder.assert_zero::<_>(main_current[7] - main_current[11]); + builder.assert_zero::<_>(main_current[8] - main_current[12]); + builder.assert_zero::<_>(main_current[9] - main_current[13]); + } +} \ No newline at end of file diff --git a/air-script/tests/constraint_comprehension/mod.rs b/air-script/tests/constraint_comprehension/mod.rs index 16222fbd1..960d9a5a6 100644 --- a/air-script/tests/constraint_comprehension/mod.rs +++ b/air-script/tests/constraint_comprehension/mod.rs @@ -1,4 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] mod constraint_comprehension; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod constraint_comprehension_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/constraint_comprehension/test_air_plonky3.rs b/air-script/tests/constraint_comprehension/test_air_plonky3.rs new file mode 100644 index 000000000..c2243f2ea --- /dev/null +++ b/air-script/tests/constraint_comprehension/test_air_plonky3.rs @@ -0,0 +1,86 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + constraint_comprehension::constraint_comprehension_plonky3::{ + ConstraintComprehensionAir, NUM_COLUMNS, + }, + helpers::check_constraints_with_periodic_columns, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::from_canonical_checked(inputs[0]).unwrap(); + rows[0][1] = F::ONE; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let a_prev = rows[i - 1][0]; + let b_prev = rows[i - 1][1]; + + // Update current row based on previous values + rows[i][0] = F::ONE - a_prev; + rows[i][1] = F::ONE - b_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&ConstraintComprehensionAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/constraint_comprehension/test_air.rs b/air-script/tests/constraint_comprehension/test_air_winterfell.rs similarity index 100% rename from air-script/tests/constraint_comprehension/test_air.rs rename to air-script/tests/constraint_comprehension/test_air_winterfell.rs diff --git a/air-script/tests/evaluators/evaluators_plonky3.rs b/air-script/tests/evaluators/evaluators_plonky3.rs new file mode 100644 index 000000000..046eb2a1f --- /dev/null +++ b/air-script/tests/evaluators/evaluators_plonky3.rs @@ -0,0 +1,52 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 7; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct EvaluatorsAir; + +impl BaseAir for EvaluatorsAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for EvaluatorsAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for EvaluatorsAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for EvaluatorsAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[0]); + builder.when_transition().assert_zero::<_>(main_next[0] - main_current[0]); + builder.when_transition().assert_zero::<_>(main_next[2] - main_current[2]); + builder.when_transition().assert_zero::<_>(main_next[6] - main_current[6]); + builder.assert_zero::<_>(main_current[0] * main_current[0] - main_current[0]); + builder.assert_zero::<_>(main_current[1] * main_current[1] - main_current[1]); + builder.assert_zero::<_>(main_current[2] * main_current[2] - main_current[2]); + builder.assert_zero::<_>(main_current[3] * main_current[3] - main_current[3]); + builder.assert_zero::<_>(main_current[4]); + builder.assert_zero::<_>(main_current[5] - AB::Expr::from(AB::F::from_u64(1))); + builder.assert_zero::<_>(main_current[6] - AB::Expr::from(AB::F::from_u64(4))); + } +} \ No newline at end of file diff --git a/air-script/tests/evaluators/mod.rs b/air-script/tests/evaluators/mod.rs index cf380f979..8513a7ba3 100644 --- a/air-script/tests/evaluators/mod.rs +++ b/air-script/tests/evaluators/mod.rs @@ -1,4 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] mod evaluators; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod evaluators_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/evaluators/test_air_plonky3.rs b/air-script/tests/evaluators/test_air_plonky3.rs new file mode 100644 index 000000000..9ba08a264 --- /dev/null +++ b/air-script/tests/evaluators/test_air_plonky3.rs @@ -0,0 +1,99 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + evaluators::evaluators_plonky3::{EvaluatorsAir, NUM_COLUMNS}, + helpers::check_constraints_with_periodic_columns, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::ZERO; + rows[0][2] = F::ZERO; + rows[0][3] = F::ZERO; + rows[0][4] = F::ZERO; + rows[0][5] = F::ONE; + rows[0][6] = F::from_canonical_checked(4).unwrap(); + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + let col_3_prev = rows[i - 1][3]; + let col_4_prev = rows[i - 1][4]; + let col_5_prev = rows[i - 1][5]; + let col_6_prev = rows[i - 1][6]; + + // Update current row based on previous values + rows[i][0] = col_0_prev; + rows[i][1] = col_1_prev; + rows[i][2] = col_2_prev; + rows[i][3] = col_3_prev; + rows[i][4] = col_4_prev; + rows[i][5] = col_5_prev; + rows[i][6] = col_6_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&EvaluatorsAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/evaluators/test_air.rs b/air-script/tests/evaluators/test_air_winterfell.rs similarity index 100% rename from air-script/tests/evaluators/test_air.rs rename to air-script/tests/evaluators/test_air_winterfell.rs diff --git a/air-script/tests/fibonacci/fibonacci_plonky3.rs b/air-script/tests/fibonacci/fibonacci_plonky3.rs new file mode 100644 index 000000000..a00d0c1a1 --- /dev/null +++ b/air-script/tests/fibonacci/fibonacci_plonky3.rs @@ -0,0 +1,46 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 2; + +pub const NUM_PUBLIC_VALUES: usize = 3; + +pub struct FibonacciAir; + +impl BaseAir for FibonacciAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for FibonacciAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for FibonacciAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for FibonacciAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[0] - public_values[0].into()); + builder.when_first_row().assert_zero::<_>(main_current[1] - public_values[1].into()); + builder.when_last_row().assert_zero::<_>(main_current[1] - public_values[2].into()); + builder.when_transition().assert_zero::<_>(main_next[1] - (main_current[0] + main_current[1])); + builder.when_transition().assert_zero::<_>(main_next[0] - main_current[1]); + } +} \ No newline at end of file diff --git a/air-script/tests/fibonacci/mod.rs b/air-script/tests/fibonacci/mod.rs index 8fa9af072..57c53c30e 100644 --- a/air-script/tests/fibonacci/mod.rs +++ b/air-script/tests/fibonacci/mod.rs @@ -1,4 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] mod fibonacci; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod fibonacci_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/fibonacci/test_air_plonky3.rs b/air-script/tests/fibonacci/test_air_plonky3.rs new file mode 100644 index 000000000..fdfbd8132 --- /dev/null +++ b/air-script/tests/fibonacci/test_air_plonky3.rs @@ -0,0 +1,87 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + fibonacci::fibonacci_plonky3::{FibonacciAir, NUM_COLUMNS}, + helpers::check_constraints_with_periodic_columns, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 31; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::from_canonical_checked(inputs[0]).unwrap(); + rows[0][1] = F::from_canonical_checked(inputs[1]).unwrap(); + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let cur_a = rows[i - 1][0]; + let cur_b = rows[i - 1][1]; + + // Update current row based on previous values + rows[i][0] = cur_b; + rows[i][1] = cur_a + cur_b; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let one = 1; + let last = 2178309; // 32nd Fibonacci number + let inputs = vec![one, one, last]; + + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&FibonacciAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/fibonacci/test_air.rs b/air-script/tests/fibonacci/test_air_winterfell.rs similarity index 100% rename from air-script/tests/fibonacci/test_air.rs rename to air-script/tests/fibonacci/test_air_winterfell.rs index 4ba4674e2..e71be12d2 100644 --- a/air-script/tests/fibonacci/test_air.rs +++ b/air-script/tests/fibonacci/test_air_winterfell.rs @@ -29,8 +29,8 @@ impl AirTester for FibonacciAirTester { |_, state| { let cur_a = state[0]; let cur_b = state[1]; - state[1] = cur_a + cur_b; state[0] = cur_b; + state[1] = cur_a + cur_b; }, ); diff --git a/air-script/tests/functions/functions_complex_plonky3.rs b/air-script/tests/functions/functions_complex_plonky3.rs new file mode 100644 index 000000000..014020bd9 --- /dev/null +++ b/air-script/tests/functions/functions_complex_plonky3.rs @@ -0,0 +1,44 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 17; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct FunctionsAir; + +impl BaseAir for FunctionsAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for FunctionsAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for FunctionsAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for FunctionsAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[3]); + builder.when_transition().assert_zero::<_>(main_next[16] - main_current[16] * ((main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[1] * main_current[2] + main_current[3] * main_current[3] * (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * main_current[2] + main_current[3] * main_current[1] * (AB::Expr::from(AB::F::from_u64(1)) - main_current[2]) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * (AB::Expr::from(AB::F::from_u64(1)) - main_current[2])) * main_current[0] - main_current[0] + AB::Expr::from(AB::F::from_u64(1)))); + builder.when_transition().assert_zero::<_>(main_next[3] - (main_current[4] + main_current[5] + main_current[6] + main_current[7] + main_current[8] + main_current[9] + main_current[10] + main_current[11] + main_current[12] + main_current[13] + main_current[14] + main_current[15] + AB::Expr::from(AB::F::from_u64(1))) * AB::Expr::from(AB::F::from_u64(2))); + } +} \ No newline at end of file diff --git a/air-script/tests/functions/functions_simple_plonky3.rs b/air-script/tests/functions/functions_simple_plonky3.rs new file mode 100644 index 000000000..2702a6f56 --- /dev/null +++ b/air-script/tests/functions/functions_simple_plonky3.rs @@ -0,0 +1,50 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 9; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct FunctionsAir; + +impl BaseAir for FunctionsAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for FunctionsAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for FunctionsAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for FunctionsAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[3]); + builder.assert_zero::<_>(main_current[0] * main_current[3] - AB::Expr::from(AB::F::from_u64(1))); + builder.assert_zero::<_>(main_current[4] * main_current[5] * main_current[6] * main_current[7] * main_current[3] - AB::Expr::from(AB::F::from_u64(1))); + builder.assert_zero::<_>((main_current[4] + main_current[5] + main_current[6] + main_current[7]) * main_current[4] * main_current[5] * main_current[6] * main_current[7] - AB::Expr::from(AB::F::from_u64(1))); + builder.assert_zero::<_>(main_current[4] * main_current[5] * main_current[6] * main_current[7] - AB::Expr::from(AB::F::from_u64(1))); + builder.assert_zero::<_>(main_current[0] * main_current[4] * main_current[5] * main_current[6] * main_current[7] - AB::Expr::from(AB::F::from_u64(1))); + builder.assert_zero::<_>(main_current[1] + (main_current[4] + main_current[5] + main_current[6] + main_current[7]) * main_current[4] * main_current[5] * main_current[6] * main_current[7] - AB::Expr::from(AB::F::from_u64(1))); + builder.assert_zero::<_>(main_current[4] + main_current[5] + main_current[6] + main_current[7] - AB::Expr::from(AB::F::from_u64(1))); + builder.assert_zero::<_>((main_current[4] + main_current[5] + main_current[6] + main_current[7]) * AB::Expr::from(AB::F::from_u64(4)) - AB::Expr::from(AB::F::from_u64(1))); + } +} \ No newline at end of file diff --git a/air-script/tests/functions/mod.rs b/air-script/tests/functions/mod.rs index 4a85b16cc..af284a496 100644 --- a/air-script/tests/functions/mod.rs +++ b/air-script/tests/functions/mod.rs @@ -4,4 +4,14 @@ mod functions_complex; #[rustfmt::skip] #[allow(clippy::all)] mod functions_simple; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod functions_complex_plonky3; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod functions_simple_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/functions/test_air_plonky3.rs b/air-script/tests/functions/test_air_plonky3.rs new file mode 100644 index 000000000..84da79962 --- /dev/null +++ b/air-script/tests/functions/test_air_plonky3.rs @@ -0,0 +1,129 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + functions::functions_complex_plonky3::{FunctionsAir, NUM_COLUMNS}, + helpers::check_constraints_with_periodic_columns, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::ZERO; + rows[0][2] = F::ZERO; + rows[0][3] = F::ZERO; + rows[0][4] = F::ZERO; + rows[0][5] = F::ZERO; + rows[0][6] = F::ZERO; + rows[0][7] = F::ZERO; + rows[0][8] = F::ZERO; + rows[0][9] = F::ZERO; + rows[0][10] = F::ZERO; + rows[0][11] = F::ZERO; + rows[0][12] = F::ZERO; + rows[0][13] = F::ZERO; + rows[0][14] = F::ZERO; + rows[0][15] = F::ZERO; + rows[0][16] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + let col_3_prev = rows[i - 1][3]; + let col_4_prev = rows[i - 1][4]; + let col_5_prev = rows[i - 1][5]; + let col_6_prev = rows[i - 1][6]; + let col_7_prev = rows[i - 1][7]; + let col_8_prev = rows[i - 1][8]; + let col_9_prev = rows[i - 1][9]; + let col_10_prev = rows[i - 1][10]; + let col_11_prev = rows[i - 1][11]; + let col_12_prev = rows[i - 1][12]; + let col_13_prev = rows[i - 1][13]; + let col_14_prev = rows[i - 1][14]; + let col_15_prev = rows[i - 1][15]; + let col_16_prev = rows[i - 1][16]; + + // Update current row based on previous values + rows[i][0] = col_0_prev; + rows[i][1] = col_1_prev; + rows[i][2] = col_2_prev; + rows[i][3] = F::from_canonical_checked(2).unwrap(); + rows[i][4] = col_4_prev; + rows[i][5] = col_5_prev; + rows[i][6] = col_6_prev; + rows[i][7] = col_7_prev; + rows[i][8] = col_8_prev; + rows[i][9] = col_9_prev; + rows[i][10] = col_10_prev; + rows[i][11] = col_11_prev; + rows[i][12] = col_12_prev; + rows[i][13] = col_13_prev; + rows[i][14] = col_14_prev; + rows[i][15] = col_15_prev; + rows[i][16] = col_16_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&FunctionsAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/functions/test_air.rs b/air-script/tests/functions/test_air_winterfell.rs similarity index 100% rename from air-script/tests/functions/test_air.rs rename to air-script/tests/functions/test_air_winterfell.rs diff --git a/air-script/tests/indexed_trace_access/indexed_trace_access_plonky3.rs b/air-script/tests/indexed_trace_access/indexed_trace_access_plonky3.rs new file mode 100644 index 000000000..c4fa5cd08 --- /dev/null +++ b/air-script/tests/indexed_trace_access/indexed_trace_access_plonky3.rs @@ -0,0 +1,43 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 4; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct TraceAccessAir; + +impl BaseAir for TraceAccessAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for TraceAccessAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for TraceAccessAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for TraceAccessAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[0]); + builder.when_transition().assert_zero::<_>(main_next[0] - (main_current[1] + AB::Expr::from(AB::F::from_u64(1)))); + } +} \ No newline at end of file diff --git a/air-script/tests/indexed_trace_access/mod.rs b/air-script/tests/indexed_trace_access/mod.rs index 7aee9d4e8..f23106386 100644 --- a/air-script/tests/indexed_trace_access/mod.rs +++ b/air-script/tests/indexed_trace_access/mod.rs @@ -1,4 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] mod indexed_trace_access; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod indexed_trace_access_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/indexed_trace_access/test_air_plonky3.rs b/air-script/tests/indexed_trace_access/test_air_plonky3.rs new file mode 100644 index 000000000..8f1f7dfd3 --- /dev/null +++ b/air-script/tests/indexed_trace_access/test_air_plonky3.rs @@ -0,0 +1,86 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + helpers::check_constraints_with_periodic_columns, + indexed_trace_access::indexed_trace_access_plonky3::{NUM_COLUMNS, TraceAccessAir}, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::ZERO; + rows[0][2] = F::ZERO; + rows[0][3] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + #[allow(clippy::needless_range_loop)] + for i in 1..num_rows { + // Update current row + rows[i][0] = F::ONE; + rows[i][1] = F::ZERO; + rows[i][2] = F::ZERO; + rows[i][3] = F::ZERO; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&TraceAccessAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/indexed_trace_access/test_air.rs b/air-script/tests/indexed_trace_access/test_air_winterfell.rs similarity index 100% rename from air-script/tests/indexed_trace_access/test_air.rs rename to air-script/tests/indexed_trace_access/test_air_winterfell.rs diff --git a/air-script/tests/list_comprehension/list_comprehension_nested_plonky3.rs b/air-script/tests/list_comprehension/list_comprehension_nested_plonky3.rs new file mode 100644 index 000000000..990eb5946 --- /dev/null +++ b/air-script/tests/list_comprehension/list_comprehension_nested_plonky3.rs @@ -0,0 +1,45 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 2; + +pub const NUM_PUBLIC_VALUES: usize = 1; + +pub struct ListComprehensionAir; + +impl BaseAir for ListComprehensionAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for ListComprehensionAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for ListComprehensionAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for ListComprehensionAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[0]); + builder.assert_zero::<_>(main_current[0] + main_current[1] * AB::Expr::from(AB::F::from_u64(2)) - AB::Expr::from(AB::F::from_u64(3))); + builder.assert_zero::<_>(main_current[0] * AB::Expr::from(AB::F::from_u64(2)) + main_current[1] * AB::Expr::from(AB::F::from_u64(3)) - AB::Expr::from(AB::F::from_u64(5))); + builder.assert_zero::<_>(main_current[0] * AB::Expr::from(AB::F::from_u64(3)) + main_current[1] * AB::Expr::from(AB::F::from_u64(4)) - AB::Expr::from(AB::F::from_u64(7))); + } +} \ No newline at end of file diff --git a/air-script/tests/list_comprehension/list_comprehension_plonky3.rs b/air-script/tests/list_comprehension/list_comprehension_plonky3.rs new file mode 100644 index 000000000..519559827 --- /dev/null +++ b/air-script/tests/list_comprehension/list_comprehension_plonky3.rs @@ -0,0 +1,48 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 16; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct ListComprehensionAir; + +impl BaseAir for ListComprehensionAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for ListComprehensionAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for ListComprehensionAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for ListComprehensionAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[10]); + builder.assert_zero::<_>(main_current[0] - main_current[2]); + builder.assert_zero::<_>(main_current[4] - main_current[0] * AB::Expr::from(AB::F::from_u64(8)) * main_current[11]); + builder.when_transition().assert_zero::<_>(main_current[4] - main_current[0] * (main_next[8] - main_next[12])); + builder.assert_zero::<_>(main_current[6] - main_current[0] * (main_current[9] - main_current[14])); + builder.assert_zero::<_>(main_current[1] - (main_current[5] - main_current[8] - main_current[12] + AB::Expr::from(AB::F::from_u64(10)) + main_current[6] - main_current[9] - main_current[13] + AB::Expr::from(AB::F::from_u64(20)) + main_current[7] - main_current[10] - main_current[14])); + builder.assert_zero::<_>(main_current[14] - AB::Expr::from(AB::F::from_u64(10))); + } +} \ No newline at end of file diff --git a/air-script/tests/list_comprehension/mod.rs b/air-script/tests/list_comprehension/mod.rs index 5f090bea2..eb8e94340 100644 --- a/air-script/tests/list_comprehension/mod.rs +++ b/air-script/tests/list_comprehension/mod.rs @@ -1,4 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] mod list_comprehension; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod list_comprehension_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/list_comprehension/test_air_plonky3.rs b/air-script/tests/list_comprehension/test_air_plonky3.rs new file mode 100644 index 000000000..0f8e6f879 --- /dev/null +++ b/air-script/tests/list_comprehension/test_air_plonky3.rs @@ -0,0 +1,126 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + helpers::check_constraints_with_periodic_columns, + list_comprehension::list_comprehension_plonky3::{ListComprehensionAir, NUM_COLUMNS}, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::from_canonical_checked(20).unwrap(); + rows[0][2] = F::ZERO; + rows[0][3] = F::ZERO; + rows[0][4] = F::ZERO; + rows[0][5] = F::ZERO; + rows[0][6] = F::ZERO; + rows[0][7] = F::ZERO; + rows[0][8] = F::ZERO; + rows[0][9] = F::ZERO; + rows[0][10] = F::ZERO; + rows[0][11] = F::ZERO; + rows[0][12] = F::ZERO; + rows[0][13] = F::ZERO; + rows[0][14] = F::from_canonical_checked(10).unwrap(); + rows[0][15] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + let col_3_prev = rows[i - 1][3]; + let col_4_prev = rows[i - 1][4]; + let col_5_prev = rows[i - 1][5]; + let col_6_prev = rows[i - 1][6]; + let col_7_prev = rows[i - 1][7]; + let col_8_prev = rows[i - 1][8]; + let col_9_prev = rows[i - 1][9]; + let col_10_prev = rows[i - 1][10]; + let col_11_prev = rows[i - 1][11]; + let col_12_prev = rows[i - 1][12]; + let col_13_prev = rows[i - 1][13]; + let col_14_prev = rows[i - 1][14]; + let col_15_prev = rows[i - 1][15]; + + // Update current row based on previous values + rows[i][0] = col_0_prev; + rows[i][1] = col_1_prev; + rows[i][2] = col_2_prev; + rows[i][3] = F::from_canonical_checked(2).unwrap(); + rows[i][4] = col_4_prev; + rows[i][5] = col_5_prev; + rows[i][6] = col_6_prev; + rows[i][7] = col_7_prev; + rows[i][8] = col_8_prev; + rows[i][9] = col_9_prev; + rows[i][10] = col_10_prev; + rows[i][11] = col_11_prev; + rows[i][12] = col_12_prev; + rows[i][13] = col_13_prev; + rows[i][14] = col_14_prev; + rows[i][15] = col_15_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&ListComprehensionAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/list_comprehension/test_air.rs b/air-script/tests/list_comprehension/test_air_winterfell.rs similarity index 100% rename from air-script/tests/list_comprehension/test_air.rs rename to air-script/tests/list_comprehension/test_air_winterfell.rs diff --git a/air-script/tests/list_folding/list_folding_plonky3.rs b/air-script/tests/list_folding/list_folding_plonky3.rs new file mode 100644 index 000000000..1c4f68dca --- /dev/null +++ b/air-script/tests/list_folding/list_folding_plonky3.rs @@ -0,0 +1,46 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 17; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct ListFoldingAir; + +impl BaseAir for ListFoldingAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for ListFoldingAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for ListFoldingAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for ListFoldingAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[11]); + builder.when_transition().assert_zero::<_>(main_next[5] - (main_current[9] + main_current[10] + main_current[11] + main_current[12] + main_current[13] * main_current[14] * main_current[15] * main_current[16])); + builder.when_transition().assert_zero::<_>(main_next[6] - (main_current[9] + main_current[10] + main_current[11] + main_current[12] + main_current[13] * main_current[14] * main_current[15] * main_current[16])); + builder.when_transition().assert_zero::<_>(main_next[7] - (main_current[9] * main_current[13] + main_current[10] * main_current[14] + main_current[11] * main_current[15] + main_current[12] * main_current[16] + (main_current[9] + main_current[13]) * (main_current[10] + main_current[14]) * (main_current[11] + main_current[15]) * (main_current[12] + main_current[16]))); + builder.when_transition().assert_zero::<_>(main_next[8] - (main_current[1] + main_current[9] * main_current[13] + main_current[10] * main_current[14] + main_current[11] * main_current[15] + main_current[12] * main_current[16] + main_current[9] * main_current[13] + main_current[10] * main_current[14] + main_current[11] * main_current[15] + main_current[12] * main_current[16])); + } +} \ No newline at end of file diff --git a/air-script/tests/list_folding/mod.rs b/air-script/tests/list_folding/mod.rs index 5e992bc22..0339dce98 100644 --- a/air-script/tests/list_folding/mod.rs +++ b/air-script/tests/list_folding/mod.rs @@ -1,4 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] mod list_folding; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod list_folding_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/list_folding/test_air_plonky3.rs b/air-script/tests/list_folding/test_air_plonky3.rs new file mode 100644 index 000000000..695d3906f --- /dev/null +++ b/air-script/tests/list_folding/test_air_plonky3.rs @@ -0,0 +1,129 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + helpers::check_constraints_with_periodic_columns, + list_folding::list_folding_plonky3::{ListFoldingAir, NUM_COLUMNS}, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::ZERO; + rows[0][2] = F::ZERO; + rows[0][3] = F::ZERO; + rows[0][4] = F::ZERO; + rows[0][5] = F::ZERO; + rows[0][6] = F::ZERO; + rows[0][7] = F::ZERO; + rows[0][8] = F::ZERO; + rows[0][9] = F::ZERO; + rows[0][10] = F::ZERO; + rows[0][11] = F::ZERO; + rows[0][12] = F::ZERO; + rows[0][13] = F::ZERO; + rows[0][14] = F::ZERO; + rows[0][15] = F::ZERO; + rows[0][16] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + let col_3_prev = rows[i - 1][3]; + let col_4_prev = rows[i - 1][4]; + let col_5_prev = rows[i - 1][5]; + let col_6_prev = rows[i - 1][6]; + let col_7_prev = rows[i - 1][7]; + let col_8_prev = rows[i - 1][8]; + let col_9_prev = rows[i - 1][9]; + let col_10_prev = rows[i - 1][10]; + let col_11_prev = rows[i - 1][11]; + let col_12_prev = rows[i - 1][12]; + let col_13_prev = rows[i - 1][13]; + let col_14_prev = rows[i - 1][14]; + let col_15_prev = rows[i - 1][15]; + let col_16_prev = rows[i - 1][16]; + + // Update current row based on previous values + rows[i][0] = col_0_prev; + rows[i][1] = col_1_prev; + rows[i][2] = col_2_prev; + rows[i][3] = F::from_canonical_checked(2).unwrap(); + rows[i][4] = col_4_prev; + rows[i][5] = col_5_prev; + rows[i][6] = col_6_prev; + rows[i][7] = col_7_prev; + rows[i][8] = col_8_prev; + rows[i][9] = col_9_prev; + rows[i][10] = col_10_prev; + rows[i][11] = col_11_prev; + rows[i][12] = col_12_prev; + rows[i][13] = col_13_prev; + rows[i][14] = col_14_prev; + rows[i][15] = col_15_prev; + rows[i][16] = col_16_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&ListFoldingAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/list_folding/test_air.rs b/air-script/tests/list_folding/test_air_winterfell.rs similarity index 100% rename from air-script/tests/list_folding/test_air.rs rename to air-script/tests/list_folding/test_air_winterfell.rs diff --git a/air-script/tests/periodic_columns/mod.rs b/air-script/tests/periodic_columns/mod.rs index fd9a501e7..0053e1aa8 100644 --- a/air-script/tests/periodic_columns/mod.rs +++ b/air-script/tests/periodic_columns/mod.rs @@ -1,4 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] mod periodic_columns; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod periodic_columns_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/periodic_columns/periodic_columns_plonky3.rs b/air-script/tests/periodic_columns/periodic_columns_plonky3.rs new file mode 100644 index 000000000..9eb4f5447 --- /dev/null +++ b/air-script/tests/periodic_columns/periodic_columns_plonky3.rs @@ -0,0 +1,46 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 3; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct PeriodicColumnsAir; + +impl BaseAir for PeriodicColumnsAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for PeriodicColumnsAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for PeriodicColumnsAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + vec![F::from_u64(1), F::from_u64(0), F::from_u64(0), F::from_u64(0)], + vec![F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(0)], + ] + } +} + +impl Air for PeriodicColumnsAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[0]); + builder.assert_zero::<_>(periodic_values[0].into() * (main_current[1] + main_current[2])); + builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[0] - main_current[0])); + } +} \ No newline at end of file diff --git a/air-script/tests/periodic_columns/test_air_plonky3.rs b/air-script/tests/periodic_columns/test_air_plonky3.rs new file mode 100644 index 000000000..c380f7e88 --- /dev/null +++ b/air-script/tests/periodic_columns/test_air_plonky3.rs @@ -0,0 +1,87 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + helpers::check_constraints_with_periodic_columns, + periodic_columns::periodic_columns_plonky3::{NUM_COLUMNS, PeriodicColumnsAir}, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::ZERO; + rows[0][2] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + + // Update current row based on previous values + rows[i][0] = col_0_prev; + rows[i][1] = col_1_prev; + rows[i][2] = col_2_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&PeriodicColumnsAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/periodic_columns/test_air.rs b/air-script/tests/periodic_columns/test_air_winterfell.rs similarity index 100% rename from air-script/tests/periodic_columns/test_air.rs rename to air-script/tests/periodic_columns/test_air_winterfell.rs diff --git a/air-script/tests/pub_inputs/mod.rs b/air-script/tests/pub_inputs/mod.rs index afb27e904..139601bc2 100644 --- a/air-script/tests/pub_inputs/mod.rs +++ b/air-script/tests/pub_inputs/mod.rs @@ -1,4 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] mod pub_inputs; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod pub_inputs_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/pub_inputs/pub_inputs_plonky3.rs b/air-script/tests/pub_inputs/pub_inputs_plonky3.rs new file mode 100644 index 000000000..a34e8aca4 --- /dev/null +++ b/air-script/tests/pub_inputs/pub_inputs_plonky3.rs @@ -0,0 +1,50 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 4; + +pub const NUM_PUBLIC_VALUES: usize = 32; + +pub struct PubInputsAir; + +impl BaseAir for PubInputsAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for PubInputsAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for PubInputsAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for PubInputsAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[0] - public_values[8].into()); + builder.when_first_row().assert_zero::<_>(main_current[1] - public_values[9].into()); + builder.when_first_row().assert_zero::<_>(main_current[2] - public_values[10].into()); + builder.when_first_row().assert_zero::<_>(main_current[3] - public_values[11].into()); + builder.when_last_row().assert_zero::<_>(main_current[0] - public_values[12].into()); + builder.when_last_row().assert_zero::<_>(main_current[1] - public_values[13].into()); + builder.when_last_row().assert_zero::<_>(main_current[2] - public_values[14].into()); + builder.when_last_row().assert_zero::<_>(main_current[3] - public_values[15].into()); + builder.when_transition().assert_zero::<_>(main_next[0] - (main_current[1] + main_current[2])); + } +} \ No newline at end of file diff --git a/air-script/tests/pub_inputs/test_air_plonky3.rs b/air-script/tests/pub_inputs/test_air_plonky3.rs new file mode 100644 index 000000000..071828dfb --- /dev/null +++ b/air-script/tests/pub_inputs/test_air_plonky3.rs @@ -0,0 +1,90 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + helpers::check_constraints_with_periodic_columns, + pub_inputs::pub_inputs_plonky3::{NUM_COLUMNS, PubInputsAir}, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::ZERO; + rows[0][2] = F::ZERO; + rows[0][3] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + let col_3_prev = rows[i - 1][3]; + + // Update current row based on previous values + rows[i][0] = col_0_prev; + rows[i][1] = col_1_prev; + rows[i][2] = col_2_prev; + rows[i][3] = col_3_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![0; 32]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&PubInputsAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/pub_inputs/test_air.rs b/air-script/tests/pub_inputs/test_air_winterfell.rs similarity index 100% rename from air-script/tests/pub_inputs/test_air.rs rename to air-script/tests/pub_inputs/test_air_winterfell.rs diff --git a/air-script/tests/selectors/mod.rs b/air-script/tests/selectors/mod.rs index ebb9eb59a..88bbcd79d 100644 --- a/air-script/tests/selectors/mod.rs +++ b/air-script/tests/selectors/mod.rs @@ -7,4 +7,18 @@ mod selectors_combine_complex; #[rustfmt::skip] #[allow(clippy::all)] mod selectors_with_evaluators; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod selectors_combine_simple_plonky3; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod selectors_combine_complex_plonky3; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod selectors_with_evaluators_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/selectors/selectors_combine_complex_plonky3.rs b/air-script/tests/selectors/selectors_combine_complex_plonky3.rs new file mode 100644 index 000000000..5db82c3d8 --- /dev/null +++ b/air-script/tests/selectors/selectors_combine_complex_plonky3.rs @@ -0,0 +1,45 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 6; + +pub const NUM_PUBLIC_VALUES: usize = 1; + +pub struct SelectorsAir; + +impl BaseAir for SelectorsAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for SelectorsAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for SelectorsAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for SelectorsAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[5]); + builder.assert_zero::<_>((main_current[0] + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * main_current[1]) * (main_current[3] - AB::Expr::from(AB::F::from_u64(16))) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * (main_current[4] - AB::Expr::from(AB::F::from_u64(5)))); + builder.assert_zero::<_>((AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * (main_current[5] - AB::Expr::from(AB::F::from_u64(5))) + main_current[0] * (main_current[4] - AB::Expr::from(AB::F::from_u64(4)))); + builder.assert_zero::<_>(main_current[0] * (main_current[5] - AB::Expr::from(AB::F::from_u64(20))) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * main_current[1] * (main_current[4] - AB::Expr::from(AB::F::from_u64(31)))); + } +} \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_combine_simple_plonky3.rs b/air-script/tests/selectors/selectors_combine_simple_plonky3.rs new file mode 100644 index 000000000..de42005a7 --- /dev/null +++ b/air-script/tests/selectors/selectors_combine_simple_plonky3.rs @@ -0,0 +1,44 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 4; + +pub const NUM_PUBLIC_VALUES: usize = 1; + +pub struct SelectorsAir; + +impl BaseAir for SelectorsAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for SelectorsAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for SelectorsAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for SelectorsAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[3]); + builder.when_transition().assert_zero::<_>(main_next[1] - main_current[2]); + builder.when_transition().assert_zero::<_>(main_current[3] * (main_next[0] - (main_current[0] + main_current[1])) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[3]) * (main_next[0] - main_current[0] * main_current[1])); + } +} \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_plonky3.rs b/air-script/tests/selectors/selectors_plonky3.rs new file mode 100644 index 000000000..9f0ff795b --- /dev/null +++ b/air-script/tests/selectors/selectors_plonky3.rs @@ -0,0 +1,44 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 4; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct SelectorsAir; + +impl BaseAir for SelectorsAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for SelectorsAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for SelectorsAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for SelectorsAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[3]); + builder.when_transition().assert_zero::<_>(main_current[0] * (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * main_next[3]); + builder.when_transition().assert_zero::<_>(main_current[0] * main_current[1] * main_current[2] * (main_next[3] - main_current[3]) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * (AB::Expr::from(AB::F::from_u64(1)) - main_current[2]) * (main_next[3] - AB::Expr::from(AB::F::from_u64(1)))); + } +} \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_with_evaluators_plonky3.rs b/air-script/tests/selectors/selectors_with_evaluators_plonky3.rs new file mode 100644 index 000000000..1dd734eec --- /dev/null +++ b/air-script/tests/selectors/selectors_with_evaluators_plonky3.rs @@ -0,0 +1,44 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 4; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct SelectorsAir; + +impl BaseAir for SelectorsAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for SelectorsAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for SelectorsAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for SelectorsAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[3]); + builder.when_transition().assert_zero::<_>(main_current[0] * (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * main_next[3]); + builder.when_transition().assert_zero::<_>(main_current[1] * main_current[2] * main_current[0] * (main_next[3] - main_current[3]) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * (AB::Expr::from(AB::F::from_u64(1)) - main_current[2]) * (main_next[3] - AB::Expr::from(AB::F::from_u64(1)))); + } +} \ No newline at end of file diff --git a/air-script/tests/selectors/test_air_plonky3.rs b/air-script/tests/selectors/test_air_plonky3.rs new file mode 100644 index 000000000..2b2366fe8 --- /dev/null +++ b/air-script/tests/selectors/test_air_plonky3.rs @@ -0,0 +1,90 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + helpers::check_constraints_with_periodic_columns, + selectors::selectors_with_evaluators_plonky3::{NUM_COLUMNS, SelectorsAir}, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::ZERO; + rows[0][2] = F::ZERO; + rows[0][3] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + let col_3_prev = rows[i - 1][3]; + + // Update current row based on previous values + rows[i][0] = col_0_prev; + rows[i][1] = col_1_prev; + rows[i][2] = col_2_prev; + rows[i][3] = F::ONE; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&SelectorsAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/selectors/test_air.rs b/air-script/tests/selectors/test_air_winterfell.rs similarity index 100% rename from air-script/tests/selectors/test_air.rs rename to air-script/tests/selectors/test_air_winterfell.rs diff --git a/air-script/tests/system/mod.rs b/air-script/tests/system/mod.rs index 10cac5f2b..7d8522c99 100644 --- a/air-script/tests/system/mod.rs +++ b/air-script/tests/system/mod.rs @@ -1,4 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] mod system; -mod test_air; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod system_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/tests/system/system_plonky3.rs b/air-script/tests/system/system_plonky3.rs new file mode 100644 index 000000000..10af52c3a --- /dev/null +++ b/air-script/tests/system/system_plonky3.rs @@ -0,0 +1,43 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 3; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct SystemAir; + +impl BaseAir for SystemAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for SystemAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for SystemAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for SystemAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[0]); + builder.when_transition().assert_zero::<_>(main_next[0] - (main_current[0] + AB::Expr::from(AB::F::from_u64(1)))); + } +} \ No newline at end of file diff --git a/air-script/tests/system/test_air_plonky3.rs b/air-script/tests/system/test_air_plonky3.rs new file mode 100644 index 000000000..5bee89503 --- /dev/null +++ b/air-script/tests/system/test_air_plonky3.rs @@ -0,0 +1,87 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + helpers::check_constraints_with_periodic_columns, + system::system_plonky3::{NUM_COLUMNS, SystemAir}, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::ZERO; + rows[0][2] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + + // Update current row based on previous values + rows[i][0] = col_0_prev + F::ONE; + rows[i][1] = col_1_prev; + rows[i][2] = col_2_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&SystemAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/system/test_air.rs b/air-script/tests/system/test_air_winterfell.rs similarity index 100% rename from air-script/tests/system/test_air.rs rename to air-script/tests/system/test_air_winterfell.rs diff --git a/air-script/tests/trace_col_groups/mod.rs b/air-script/tests/trace_col_groups/mod.rs index 706feb822..c2bb1aa0a 100644 --- a/air-script/tests/trace_col_groups/mod.rs +++ b/air-script/tests/trace_col_groups/mod.rs @@ -1,4 +1,10 @@ -mod test_air; +mod test_air_plonky3; +mod test_air_winterfell; + #[rustfmt::skip] #[allow(clippy::all)] mod trace_col_groups; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod trace_col_groups_plonky3; diff --git a/air-script/tests/trace_col_groups/test_air_plonky3.rs b/air-script/tests/trace_col_groups/test_air_plonky3.rs new file mode 100644 index 000000000..b1c2c7f67 --- /dev/null +++ b/air-script/tests/trace_col_groups/test_air_plonky3.rs @@ -0,0 +1,105 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + helpers::check_constraints_with_periodic_columns, + trace_col_groups::trace_col_groups_plonky3::{NUM_COLUMNS, TraceColGroupAir}, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::ZERO; + rows[0][2] = F::ZERO; + rows[0][3] = F::ZERO; + rows[0][4] = F::ZERO; + rows[0][5] = F::ZERO; + rows[0][6] = F::ZERO; + rows[0][7] = F::ZERO; + rows[0][8] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + let col_3_prev = rows[i - 1][3]; + let col_4_prev = rows[i - 1][4]; + let col_5_prev = rows[i - 1][5]; + let col_6_prev = rows[i - 1][6]; + let col_7_prev = rows[i - 1][7]; + let col_8_prev = rows[i - 1][8]; + + // Update current row based on previous values + rows[i][0] = col_0_prev; + rows[i][1] = col_1_prev - F::ONE; + rows[i][2] = col_2_prev + F::ONE; + rows[i][3] = col_3_prev; + rows[i][4] = col_4_prev; + rows[i][5] = col_5_prev; + rows[i][6] = col_6_prev; + rows[i][7] = col_7_prev; + rows[i][8] = col_8_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 16]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&TraceColGroupAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/trace_col_groups/test_air.rs b/air-script/tests/trace_col_groups/test_air_winterfell.rs similarity index 100% rename from air-script/tests/trace_col_groups/test_air.rs rename to air-script/tests/trace_col_groups/test_air_winterfell.rs diff --git a/air-script/tests/trace_col_groups/trace_col_groups_plonky3.rs b/air-script/tests/trace_col_groups/trace_col_groups_plonky3.rs new file mode 100644 index 000000000..bb500087e --- /dev/null +++ b/air-script/tests/trace_col_groups/trace_col_groups_plonky3.rs @@ -0,0 +1,44 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 9; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct TraceColGroupAir; + +impl BaseAir for TraceColGroupAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for TraceColGroupAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for TraceColGroupAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for TraceColGroupAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[8]); + builder.when_transition().assert_zero::<_>(main_next[2] - (main_current[2] + AB::Expr::from(AB::F::from_u64(1)))); + builder.when_transition().assert_zero::<_>(main_next[1] - (main_current[1] - AB::Expr::from(AB::F::from_u64(1)))); + } +} \ No newline at end of file diff --git a/air-script/tests/variables/mod.rs b/air-script/tests/variables/mod.rs index 31656aa8c..98685f537 100644 --- a/air-script/tests/variables/mod.rs +++ b/air-script/tests/variables/mod.rs @@ -1,4 +1,9 @@ -mod test_air; +mod test_air_plonky3; +mod test_air_winterfell; #[rustfmt::skip] #[allow(clippy::all)] mod variables; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod variables_plonky3; diff --git a/air-script/tests/variables/test_air_plonky3.rs b/air-script/tests/variables/test_air_plonky3.rs new file mode 100644 index 000000000..e1e615220 --- /dev/null +++ b/air-script/tests/variables/test_air_plonky3.rs @@ -0,0 +1,90 @@ +use std::marker::PhantomData; + +use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_circle::CirclePcs; +use p3_commit::ExtensionMmcs; +use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_fri::create_benchmark_fri_params; +use p3_matrix::dense::RowMajorMatrix; +use p3_merkle_tree::MerkleTreeMmcs; +use p3_mersenne_31::Mersenne31; +use p3_sha256::Sha256; +use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; +use p3_uni_stark::StarkConfig; + +use crate::{ + helpers::check_constraints_with_periodic_columns, + variables::variables_plonky3::{NUM_COLUMNS, VariablesAir}, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * NUM_COLUMNS; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ONE; + rows[0][1] = F::ZERO; + rows[0][2] = F::ZERO; + rows[0][3] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + let col_3_prev = rows[i - 1][3]; + + // Update current row based on previous values + rows[i][0] = col_0_prev; + rows[i][1] = F::ONE; + rows[i][2] = col_2_prev; + rows[i][3] = col_3_prev; + } + + trace +} + +#[test] +fn test_air_plonky3() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = vec![1; 32]; + let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&VariablesAir {}, &trace, &inputs_m31); +} diff --git a/air-script/tests/variables/test_air.rs b/air-script/tests/variables/test_air_winterfell.rs similarity index 100% rename from air-script/tests/variables/test_air.rs rename to air-script/tests/variables/test_air_winterfell.rs diff --git a/air-script/tests/variables/variables_plonky3.rs b/air-script/tests/variables/variables_plonky3.rs new file mode 100644 index 000000000..73408ce98 --- /dev/null +++ b/air-script/tests/variables/variables_plonky3.rs @@ -0,0 +1,48 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 4; + +pub const NUM_PUBLIC_VALUES: usize = 32; + +pub struct VariablesAir; + +impl BaseAir for VariablesAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for VariablesAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for VariablesAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + vec![F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(0)], + ] + } +} + +impl Air for VariablesAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[1]); + builder.when_last_row().assert_zero::<_>(main_current[1] - AB::Expr::from(AB::F::from_u64(1))); + builder.assert_zero::<_>(main_current[0] * main_current[0] - main_current[0]); + builder.when_transition().assert_zero::<_>(periodic_values[0].into() * (main_next[0] - main_current[0])); + builder.assert_zero::<_>((AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * (main_current[3] - main_current[1] - main_current[2]) - (AB::Expr::from(AB::F::from_u64(6)) - (AB::Expr::from(AB::F::from_u64(7)) - main_current[0]))); + builder.when_transition().assert_zero::<_>(main_current[0] * (main_current[3] - main_current[1] * main_current[2]) - (AB::Expr::from(AB::F::from_u64(1)) - main_next[0])); + } +} \ No newline at end of file From 4875e6c758035a36f342df14aee6a2ad960fb750 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Fri, 26 Sep 2025 08:58:37 +0200 Subject: [PATCH 12/83] tests: add E2E test for selectors combine with list comprehensions --- ...ombine_with_list_comprehensions_plonky3.rs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 air-script/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs diff --git a/air-script/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs b/air-script/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs new file mode 100644 index 000000000..0f5f4f571 --- /dev/null +++ b/air-script/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs @@ -0,0 +1,46 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 6; + +pub const NUM_PUBLIC_VALUES: usize = 1; + +pub struct SelectorsAir; + +impl BaseAir for SelectorsAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for SelectorsAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for SelectorsAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for SelectorsAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[5]); + builder.assert_zero::<_>(main_current[0] * main_current[3] + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * main_current[1] * main_current[3] + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * (main_current[4] - AB::Expr::from(AB::F::from_u64(8)))); + builder.assert_zero::<_>(main_current[0] * (main_current[4] - AB::Expr::from(AB::F::from_u64(2))) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * main_current[1] * (main_current[4] - AB::Expr::from(AB::F::from_u64(6))) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * (main_current[5] - AB::Expr::from(AB::F::from_u64(8)))); + builder.assert_zero::<_>(main_current[0] * main_current[3] + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * main_current[1] * (main_current[5] - AB::Expr::from(AB::F::from_u64(8)))); + builder.assert_zero::<_>(main_current[0] * (main_current[5] - AB::Expr::from(AB::F::from_u64(4)))); + } +} \ No newline at end of file From d8c60cc63f0f81440094f4cd91bb5c2f944f34a8 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Tue, 14 Oct 2025 09:49:17 +0200 Subject: [PATCH 13/83] refactor(plonky3): use AB::Expr::ZERO, ONE, from_u64 and double --- codegen/plonky3/src/air/graph.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/codegen/plonky3/src/air/graph.rs b/codegen/plonky3/src/air/graph.rs index 56d5a90cf..fb9e816b6 100644 --- a/codegen/plonky3/src/air/graph.rs +++ b/codegen/plonky3/src/air/graph.rs @@ -14,10 +14,10 @@ impl Codegen for TraceAccess { let frame = self.segment.to_string(); let row_offset = match self.row_offset { 0 => { - format!("current[{}]", self.column) + format!("current[{}].into()", self.column) }, 1 => { - format!("next[{}]", self.column) + format!("next[{}].into()", self.column) }, _ => panic!("Plonky3 doesn't support row offsets greater than 1."), }; @@ -46,7 +46,9 @@ impl Codegen for Operation { impl Codegen for Value { fn to_string(&self, ir: &Air) -> String { match self { - Value::Constant(value) => format!("AB::Expr::from(AB::F::from_u64({value}))"), + Value::Constant(0) => format!("AB::Expr::ZERO"), + Value::Constant(1) => format!("AB::Expr::ONE"), + Value::Constant(value) => format!("AB::Expr::from_u64({value})"), Value::TraceAccess(trace_access) => trace_access.to_string(ir), Value::PublicInput(air_ir::PublicInputAccess { name, index }) => { let get_public_input_offset = |name: &str| { @@ -95,17 +97,25 @@ fn binary_op_to_string(ir: &Air, op: &Operation) -> String { format!("{lhs} - {rhs}") }, Operation::Mul(l_idx, r_idx) => { - let lhs = if ir.constraint_graph().node(l_idx).op().precedence() < op.precedence() { + let lhs_op = ir.constraint_graph().node(l_idx).op(); + let rhs_op = ir.constraint_graph().node(r_idx).op(); + + let lhs = if lhs_op.precedence() < op.precedence() { format!("({})", l_idx.to_string(ir)) } else { l_idx.to_string(ir) }; - let rhs = if ir.constraint_graph().node(r_idx).op().precedence() < op.precedence() { + let rhs = if rhs_op.precedence() < op.precedence() { format!("({})", r_idx.to_string(ir)) } else { r_idx.to_string(ir) }; - format!("{lhs} * {rhs}") + + match (lhs_op, rhs_op) { + (_, Operation::Value(Value::Constant(2))) => format!("{lhs}.double()"), + (Operation::Value(Value::Constant(2)), _) => format!("{rhs}.double()"), + _ => format!("{lhs} * {rhs}"), + } }, _ => panic!("unsupported operation"), } From ab4c4e8dd0ea50b8166c0be1d45f8097f0cdc034 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Tue, 14 Oct 2025 09:49:39 +0200 Subject: [PATCH 14/83] tests: update plonky3 E2E tests following codegen refactor --- air-script/tests/binary/binary_plonky3.rs | 6 ++-- air-script/tests/bitwise/bitwise_plonky3.rs | 36 +++++++++---------- .../tests/buses/buses_complex_plonky3.rs | 6 ++-- .../constant_in_range_plonky3.rs | 4 +-- .../tests/constants/constants_plonky3.rs | 22 ++++++------ .../constraint_comprehension_plonky3.rs | 10 +++--- .../tests/evaluators/evaluators_plonky3.rs | 22 ++++++------ .../tests/fibonacci/fibonacci_plonky3.rs | 10 +++--- .../functions/functions_complex_plonky3.rs | 6 ++-- .../functions/functions_simple_plonky3.rs | 18 +++++----- .../indexed_trace_access_plonky3.rs | 4 +-- .../list_comprehension_nested_plonky3.rs | 8 ++--- .../list_comprehension_plonky3.rs | 14 ++++---- .../list_folding/list_folding_plonky3.rs | 10 +++--- .../periodic_columns_plonky3.rs | 6 ++-- .../tests/pub_inputs/pub_inputs_plonky3.rs | 18 +++++----- .../selectors_combine_complex_plonky3.rs | 8 ++--- .../selectors_combine_simple_plonky3.rs | 6 ++-- ...ombine_with_list_comprehensions_plonky3.rs | 9 +++-- .../tests/selectors/selectors_plonky3.rs | 6 ++-- .../selectors_with_evaluators_plonky3.rs | 6 ++-- air-script/tests/system/system_plonky3.rs | 4 +-- .../trace_col_groups_plonky3.rs | 6 ++-- .../tests/variables/variables_plonky3.rs | 12 +++---- 24 files changed, 128 insertions(+), 129 deletions(-) diff --git a/air-script/tests/binary/binary_plonky3.rs b/air-script/tests/binary/binary_plonky3.rs index 4d89ff939..df44bcb6d 100644 --- a/air-script/tests/binary/binary_plonky3.rs +++ b/air-script/tests/binary/binary_plonky3.rs @@ -37,8 +37,8 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[0] - public_values[0].into()); - builder.assert_zero::<_>(main_current[0] * main_current[0] - main_current[0]); - builder.assert_zero::<_>(main_current[1] * main_current[1] - main_current[1]); + builder.when_first_row().assert_zero::<_>(main_current[0].into() - public_values[0].into()); + builder.assert_zero::<_>(main_current[0].into() * main_current[0].into() - main_current[0].into()); + builder.assert_zero::<_>(main_current[1].into() * main_current[1].into() - main_current[1].into()); } } \ No newline at end of file diff --git a/air-script/tests/bitwise/bitwise_plonky3.rs b/air-script/tests/bitwise/bitwise_plonky3.rs index e67cde192..ef5d5d992 100644 --- a/air-script/tests/bitwise/bitwise_plonky3.rs +++ b/air-script/tests/bitwise/bitwise_plonky3.rs @@ -39,23 +39,23 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[13]); - builder.assert_zero::<_>(main_current[0] * main_current[0] - main_current[0]); - builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[0] - main_current[0])); - builder.assert_zero::<_>(main_current[3] * main_current[3] - main_current[3]); - builder.assert_zero::<_>(main_current[4] * main_current[4] - main_current[4]); - builder.assert_zero::<_>(main_current[5] * main_current[5] - main_current[5]); - builder.assert_zero::<_>(main_current[6] * main_current[6] - main_current[6]); - builder.assert_zero::<_>(main_current[7] * main_current[7] - main_current[7]); - builder.assert_zero::<_>(main_current[8] * main_current[8] - main_current[8]); - builder.assert_zero::<_>(main_current[9] * main_current[9] - main_current[9]); - builder.assert_zero::<_>(main_current[10] * main_current[10] - main_current[10]); - builder.assert_zero::<_>(periodic_values[0].into() * (main_current[1] - (main_current[3] + AB::Expr::from(AB::F::from_u64(2)) * main_current[4] + AB::Expr::from(AB::F::from_u64(4)) * main_current[5] + AB::Expr::from(AB::F::from_u64(8)) * main_current[6]))); - builder.assert_zero::<_>(periodic_values[0].into() * (main_current[2] - (main_current[7] + AB::Expr::from(AB::F::from_u64(2)) * main_current[8] + AB::Expr::from(AB::F::from_u64(4)) * main_current[9] + AB::Expr::from(AB::F::from_u64(8)) * main_current[10]))); - builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[1] - (main_current[1] * AB::Expr::from(AB::F::from_u64(16)) + main_current[3] + AB::Expr::from(AB::F::from_u64(2)) * main_current[4] + AB::Expr::from(AB::F::from_u64(4)) * main_current[5] + AB::Expr::from(AB::F::from_u64(8)) * main_current[6]))); - builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[2] - (main_current[2] * AB::Expr::from(AB::F::from_u64(16)) + main_current[7] + AB::Expr::from(AB::F::from_u64(2)) * main_current[8] + AB::Expr::from(AB::F::from_u64(4)) * main_current[9] + AB::Expr::from(AB::F::from_u64(8)) * main_current[10]))); - builder.assert_zero::<_>(periodic_values[0].into() * main_current[11]); - builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_current[12] - main_next[11])); - builder.assert_zero::<_>((AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * (main_current[12] - (main_current[11] * AB::Expr::from(AB::F::from_u64(16)) + main_current[3] * main_current[7] + AB::Expr::from(AB::F::from_u64(2)) * main_current[4] * main_current[8] + AB::Expr::from(AB::F::from_u64(4)) * main_current[5] * main_current[9] + AB::Expr::from(AB::F::from_u64(8)) * main_current[6] * main_current[10])) + main_current[0] * (main_current[12] - (main_current[11] * AB::Expr::from(AB::F::from_u64(16)) + main_current[3] + main_current[7] - AB::Expr::from(AB::F::from_u64(2)) * main_current[3] * main_current[7] + AB::Expr::from(AB::F::from_u64(2)) * (main_current[4] + main_current[8] - AB::Expr::from(AB::F::from_u64(2)) * main_current[4] * main_current[8]) + AB::Expr::from(AB::F::from_u64(4)) * (main_current[5] + main_current[9] - AB::Expr::from(AB::F::from_u64(2)) * main_current[5] * main_current[9]) + AB::Expr::from(AB::F::from_u64(8)) * (main_current[6] + main_current[10] - AB::Expr::from(AB::F::from_u64(2)) * main_current[6] * main_current[10])))); + builder.when_first_row().assert_zero::<_>(main_current[13].into()); + builder.assert_zero::<_>(main_current[0].into() * main_current[0].into() - main_current[0].into()); + builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[0].into() - main_current[0].into())); + builder.assert_zero::<_>(main_current[3].into() * main_current[3].into() - main_current[3].into()); + builder.assert_zero::<_>(main_current[4].into() * main_current[4].into() - main_current[4].into()); + builder.assert_zero::<_>(main_current[5].into() * main_current[5].into() - main_current[5].into()); + builder.assert_zero::<_>(main_current[6].into() * main_current[6].into() - main_current[6].into()); + builder.assert_zero::<_>(main_current[7].into() * main_current[7].into() - main_current[7].into()); + builder.assert_zero::<_>(main_current[8].into() * main_current[8].into() - main_current[8].into()); + builder.assert_zero::<_>(main_current[9].into() * main_current[9].into() - main_current[9].into()); + builder.assert_zero::<_>(main_current[10].into() * main_current[10].into() - main_current[10].into()); + builder.assert_zero::<_>(periodic_values[0].into() * (main_current[1].into() - (main_current[3].into() + main_current[4].into().double() + AB::Expr::from_u64(4) * main_current[5].into() + AB::Expr::from_u64(8) * main_current[6].into()))); + builder.assert_zero::<_>(periodic_values[0].into() * (main_current[2].into() - (main_current[7].into() + main_current[8].into().double() + AB::Expr::from_u64(4) * main_current[9].into() + AB::Expr::from_u64(8) * main_current[10].into()))); + builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[1].into() - (main_current[1].into() * AB::Expr::from_u64(16) + main_current[3].into() + main_current[4].into().double() + AB::Expr::from_u64(4) * main_current[5].into() + AB::Expr::from_u64(8) * main_current[6].into()))); + builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[2].into() - (main_current[2].into() * AB::Expr::from_u64(16) + main_current[7].into() + main_current[8].into().double() + AB::Expr::from_u64(4) * main_current[9].into() + AB::Expr::from_u64(8) * main_current[10].into()))); + builder.assert_zero::<_>(periodic_values[0].into() * main_current[11].into()); + builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_current[12].into() - main_next[11].into())); + builder.assert_zero::<_>((AB::Expr::ONE - main_current[0].into()) * (main_current[12].into() - (main_current[11].into() * AB::Expr::from_u64(16) + main_current[3].into() * main_current[7].into() + main_current[4].into().double() * main_current[8].into() + AB::Expr::from_u64(4) * main_current[5].into() * main_current[9].into() + AB::Expr::from_u64(8) * main_current[6].into() * main_current[10].into())) + main_current[0].into() * (main_current[12].into() - (main_current[11].into() * AB::Expr::from_u64(16) + main_current[3].into() + main_current[7].into() - main_current[3].into().double() * main_current[7].into() + (main_current[4].into() + main_current[8].into() - main_current[4].into().double() * main_current[8].into()).double() + AB::Expr::from_u64(4) * (main_current[5].into() + main_current[9].into() - main_current[5].into().double() * main_current[9].into()) + AB::Expr::from_u64(8) * (main_current[6].into() + main_current[10].into() - main_current[6].into().double() * main_current[10].into())))); } } \ No newline at end of file diff --git a/air-script/tests/buses/buses_complex_plonky3.rs b/air-script/tests/buses/buses_complex_plonky3.rs index 76905b3a8..462ef499a 100644 --- a/air-script/tests/buses/buses_complex_plonky3.rs +++ b/air-script/tests/buses/buses_complex_plonky3.rs @@ -37,8 +37,8 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[0]); - builder.assert_zero::<_>(main_current[2] * main_current[2] - main_current[2]); - builder.assert_zero::<_>(main_current[3] * main_current[3] - main_current[3]); + builder.when_first_row().assert_zero::<_>(main_current[0].into()); + builder.assert_zero::<_>(main_current[2].into() * main_current[2].into() - main_current[2].into()); + builder.assert_zero::<_>(main_current[3].into() * main_current[3].into() - main_current[3].into()); } } \ No newline at end of file diff --git a/air-script/tests/constant_in_range/constant_in_range_plonky3.rs b/air-script/tests/constant_in_range/constant_in_range_plonky3.rs index 6b23d4b17..5f657e987 100644 --- a/air-script/tests/constant_in_range/constant_in_range_plonky3.rs +++ b/air-script/tests/constant_in_range/constant_in_range_plonky3.rs @@ -37,7 +37,7 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[6]); - builder.assert_zero::<_>(main_current[0] - (main_current[1] - main_current[4] - main_current[8] + AB::Expr::from(AB::F::from_u64(1)) + main_current[2] - main_current[5] - main_current[9] + AB::Expr::from(AB::F::from_u64(2)) + main_current[3] - main_current[6] - main_current[10])); + builder.when_first_row().assert_zero::<_>(main_current[6].into()); + builder.assert_zero::<_>(main_current[0].into() - (main_current[1].into() - main_current[4].into() - main_current[8].into() + AB::Expr::ONE + main_current[2].into() - main_current[5].into() - main_current[9].into() + AB::Expr::from_u64(2) + main_current[3].into() - main_current[6].into() - main_current[10].into())); } } \ No newline at end of file diff --git a/air-script/tests/constants/constants_plonky3.rs b/air-script/tests/constants/constants_plonky3.rs index 9361c0228..fa2ffccac 100644 --- a/air-script/tests/constants/constants_plonky3.rs +++ b/air-script/tests/constants/constants_plonky3.rs @@ -37,16 +37,16 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[0] - AB::Expr::from(AB::F::from_u64(1))); - builder.when_first_row().assert_zero::<_>(main_current[1] - AB::Expr::from(AB::F::from_u64(1))); - builder.when_first_row().assert_zero::<_>(main_current[2]); - builder.when_first_row().assert_zero::<_>(main_current[3] - AB::Expr::from(AB::F::from_u64(1))); - builder.when_first_row().assert_zero::<_>(main_current[4] - AB::Expr::from(AB::F::from_u64(1))); - builder.when_last_row().assert_zero::<_>(main_current[6]); - builder.when_transition().assert_zero::<_>(main_next[0] - (main_current[0] + AB::Expr::from(AB::F::from_u64(1)))); - builder.when_transition().assert_zero::<_>(main_next[1]); - builder.when_transition().assert_zero::<_>(main_next[2] - main_current[2]); - builder.when_transition().assert_zero::<_>(main_next[5] - (main_current[5] + AB::Expr::from(AB::F::from_u64(1)))); - builder.assert_zero::<_>(main_current[4] - AB::Expr::from(AB::F::from_u64(1))); + builder.when_first_row().assert_zero::<_>(main_current[0].into() - AB::Expr::ONE); + builder.when_first_row().assert_zero::<_>(main_current[1].into() - AB::Expr::ONE); + builder.when_first_row().assert_zero::<_>(main_current[2].into()); + builder.when_first_row().assert_zero::<_>(main_current[3].into() - AB::Expr::ONE); + builder.when_first_row().assert_zero::<_>(main_current[4].into() - AB::Expr::ONE); + builder.when_last_row().assert_zero::<_>(main_current[6].into()); + builder.when_transition().assert_zero::<_>(main_next[0].into() - (main_current[0].into() + AB::Expr::ONE)); + builder.when_transition().assert_zero::<_>(main_next[1].into()); + builder.when_transition().assert_zero::<_>(main_next[2].into() - main_current[2].into()); + builder.when_transition().assert_zero::<_>(main_next[5].into() - (main_current[5].into() + AB::Expr::ONE)); + builder.assert_zero::<_>(main_current[4].into() - AB::Expr::ONE); } } \ No newline at end of file diff --git a/air-script/tests/constraint_comprehension/constraint_comprehension_plonky3.rs b/air-script/tests/constraint_comprehension/constraint_comprehension_plonky3.rs index ef6d701c1..b501fe9d3 100644 --- a/air-script/tests/constraint_comprehension/constraint_comprehension_plonky3.rs +++ b/air-script/tests/constraint_comprehension/constraint_comprehension_plonky3.rs @@ -37,10 +37,10 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[8]); - builder.assert_zero::<_>(main_current[6] - main_current[10]); - builder.assert_zero::<_>(main_current[7] - main_current[11]); - builder.assert_zero::<_>(main_current[8] - main_current[12]); - builder.assert_zero::<_>(main_current[9] - main_current[13]); + builder.when_first_row().assert_zero::<_>(main_current[8].into()); + builder.assert_zero::<_>(main_current[6].into() - main_current[10].into()); + builder.assert_zero::<_>(main_current[7].into() - main_current[11].into()); + builder.assert_zero::<_>(main_current[8].into() - main_current[12].into()); + builder.assert_zero::<_>(main_current[9].into() - main_current[13].into()); } } \ No newline at end of file diff --git a/air-script/tests/evaluators/evaluators_plonky3.rs b/air-script/tests/evaluators/evaluators_plonky3.rs index 046eb2a1f..becb57b5a 100644 --- a/air-script/tests/evaluators/evaluators_plonky3.rs +++ b/air-script/tests/evaluators/evaluators_plonky3.rs @@ -37,16 +37,16 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[0]); - builder.when_transition().assert_zero::<_>(main_next[0] - main_current[0]); - builder.when_transition().assert_zero::<_>(main_next[2] - main_current[2]); - builder.when_transition().assert_zero::<_>(main_next[6] - main_current[6]); - builder.assert_zero::<_>(main_current[0] * main_current[0] - main_current[0]); - builder.assert_zero::<_>(main_current[1] * main_current[1] - main_current[1]); - builder.assert_zero::<_>(main_current[2] * main_current[2] - main_current[2]); - builder.assert_zero::<_>(main_current[3] * main_current[3] - main_current[3]); - builder.assert_zero::<_>(main_current[4]); - builder.assert_zero::<_>(main_current[5] - AB::Expr::from(AB::F::from_u64(1))); - builder.assert_zero::<_>(main_current[6] - AB::Expr::from(AB::F::from_u64(4))); + builder.when_first_row().assert_zero::<_>(main_current[0].into()); + builder.when_transition().assert_zero::<_>(main_next[0].into() - main_current[0].into()); + builder.when_transition().assert_zero::<_>(main_next[2].into() - main_current[2].into()); + builder.when_transition().assert_zero::<_>(main_next[6].into() - main_current[6].into()); + builder.assert_zero::<_>(main_current[0].into() * main_current[0].into() - main_current[0].into()); + builder.assert_zero::<_>(main_current[1].into() * main_current[1].into() - main_current[1].into()); + builder.assert_zero::<_>(main_current[2].into() * main_current[2].into() - main_current[2].into()); + builder.assert_zero::<_>(main_current[3].into() * main_current[3].into() - main_current[3].into()); + builder.assert_zero::<_>(main_current[4].into()); + builder.assert_zero::<_>(main_current[5].into() - AB::Expr::ONE); + builder.assert_zero::<_>(main_current[6].into() - AB::Expr::from_u64(4)); } } \ No newline at end of file diff --git a/air-script/tests/fibonacci/fibonacci_plonky3.rs b/air-script/tests/fibonacci/fibonacci_plonky3.rs index a00d0c1a1..262dbb610 100644 --- a/air-script/tests/fibonacci/fibonacci_plonky3.rs +++ b/air-script/tests/fibonacci/fibonacci_plonky3.rs @@ -37,10 +37,10 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[0] - public_values[0].into()); - builder.when_first_row().assert_zero::<_>(main_current[1] - public_values[1].into()); - builder.when_last_row().assert_zero::<_>(main_current[1] - public_values[2].into()); - builder.when_transition().assert_zero::<_>(main_next[1] - (main_current[0] + main_current[1])); - builder.when_transition().assert_zero::<_>(main_next[0] - main_current[1]); + builder.when_first_row().assert_zero::<_>(main_current[0].into() - public_values[0].into()); + builder.when_first_row().assert_zero::<_>(main_current[1].into() - public_values[1].into()); + builder.when_last_row().assert_zero::<_>(main_current[1].into() - public_values[2].into()); + builder.when_transition().assert_zero::<_>(main_next[1].into() - (main_current[0].into() + main_current[1].into())); + builder.when_transition().assert_zero::<_>(main_next[0].into() - main_current[1].into()); } } \ No newline at end of file diff --git a/air-script/tests/functions/functions_complex_plonky3.rs b/air-script/tests/functions/functions_complex_plonky3.rs index 014020bd9..cca15366b 100644 --- a/air-script/tests/functions/functions_complex_plonky3.rs +++ b/air-script/tests/functions/functions_complex_plonky3.rs @@ -37,8 +37,8 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[3]); - builder.when_transition().assert_zero::<_>(main_next[16] - main_current[16] * ((main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[1] * main_current[2] + main_current[3] * main_current[3] * (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * main_current[2] + main_current[3] * main_current[1] * (AB::Expr::from(AB::F::from_u64(1)) - main_current[2]) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * (AB::Expr::from(AB::F::from_u64(1)) - main_current[2])) * main_current[0] - main_current[0] + AB::Expr::from(AB::F::from_u64(1)))); - builder.when_transition().assert_zero::<_>(main_next[3] - (main_current[4] + main_current[5] + main_current[6] + main_current[7] + main_current[8] + main_current[9] + main_current[10] + main_current[11] + main_current[12] + main_current[13] + main_current[14] + main_current[15] + AB::Expr::from(AB::F::from_u64(1))) * AB::Expr::from(AB::F::from_u64(2))); + builder.when_first_row().assert_zero::<_>(main_current[3].into()); + builder.when_transition().assert_zero::<_>(main_next[16].into() - main_current[16].into() * ((main_current[3].into() * main_current[3].into() * main_current[3].into() * main_current[3].into() * main_current[3].into() * main_current[3].into() * main_current[3].into() * main_current[1].into() * main_current[2].into() + main_current[3].into() * main_current[3].into() * (AB::Expr::ONE - main_current[1].into()) * main_current[2].into() + main_current[3].into() * main_current[1].into() * (AB::Expr::ONE - main_current[2].into()) + (AB::Expr::ONE - main_current[1].into()) * (AB::Expr::ONE - main_current[2].into())) * main_current[0].into() - main_current[0].into() + AB::Expr::ONE)); + builder.when_transition().assert_zero::<_>(main_next[3].into() - (main_current[4].into() + main_current[5].into() + main_current[6].into() + main_current[7].into() + main_current[8].into() + main_current[9].into() + main_current[10].into() + main_current[11].into() + main_current[12].into() + main_current[13].into() + main_current[14].into() + main_current[15].into() + AB::Expr::ONE).double()); } } \ No newline at end of file diff --git a/air-script/tests/functions/functions_simple_plonky3.rs b/air-script/tests/functions/functions_simple_plonky3.rs index 2702a6f56..7d19e8a4b 100644 --- a/air-script/tests/functions/functions_simple_plonky3.rs +++ b/air-script/tests/functions/functions_simple_plonky3.rs @@ -37,14 +37,14 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[3]); - builder.assert_zero::<_>(main_current[0] * main_current[3] - AB::Expr::from(AB::F::from_u64(1))); - builder.assert_zero::<_>(main_current[4] * main_current[5] * main_current[6] * main_current[7] * main_current[3] - AB::Expr::from(AB::F::from_u64(1))); - builder.assert_zero::<_>((main_current[4] + main_current[5] + main_current[6] + main_current[7]) * main_current[4] * main_current[5] * main_current[6] * main_current[7] - AB::Expr::from(AB::F::from_u64(1))); - builder.assert_zero::<_>(main_current[4] * main_current[5] * main_current[6] * main_current[7] - AB::Expr::from(AB::F::from_u64(1))); - builder.assert_zero::<_>(main_current[0] * main_current[4] * main_current[5] * main_current[6] * main_current[7] - AB::Expr::from(AB::F::from_u64(1))); - builder.assert_zero::<_>(main_current[1] + (main_current[4] + main_current[5] + main_current[6] + main_current[7]) * main_current[4] * main_current[5] * main_current[6] * main_current[7] - AB::Expr::from(AB::F::from_u64(1))); - builder.assert_zero::<_>(main_current[4] + main_current[5] + main_current[6] + main_current[7] - AB::Expr::from(AB::F::from_u64(1))); - builder.assert_zero::<_>((main_current[4] + main_current[5] + main_current[6] + main_current[7]) * AB::Expr::from(AB::F::from_u64(4)) - AB::Expr::from(AB::F::from_u64(1))); + builder.when_first_row().assert_zero::<_>(main_current[3].into()); + builder.assert_zero::<_>(main_current[0].into() * main_current[3].into() - AB::Expr::ONE); + builder.assert_zero::<_>(main_current[4].into() * main_current[5].into() * main_current[6].into() * main_current[7].into() * main_current[3].into() - AB::Expr::ONE); + builder.assert_zero::<_>((main_current[4].into() + main_current[5].into() + main_current[6].into() + main_current[7].into()) * main_current[4].into() * main_current[5].into() * main_current[6].into() * main_current[7].into() - AB::Expr::ONE); + builder.assert_zero::<_>(main_current[4].into() * main_current[5].into() * main_current[6].into() * main_current[7].into() - AB::Expr::ONE); + builder.assert_zero::<_>(main_current[0].into() * main_current[4].into() * main_current[5].into() * main_current[6].into() * main_current[7].into() - AB::Expr::ONE); + builder.assert_zero::<_>(main_current[1].into() + (main_current[4].into() + main_current[5].into() + main_current[6].into() + main_current[7].into()) * main_current[4].into() * main_current[5].into() * main_current[6].into() * main_current[7].into() - AB::Expr::ONE); + builder.assert_zero::<_>(main_current[4].into() + main_current[5].into() + main_current[6].into() + main_current[7].into() - AB::Expr::ONE); + builder.assert_zero::<_>((main_current[4].into() + main_current[5].into() + main_current[6].into() + main_current[7].into()) * AB::Expr::from_u64(4) - AB::Expr::ONE); } } \ No newline at end of file diff --git a/air-script/tests/indexed_trace_access/indexed_trace_access_plonky3.rs b/air-script/tests/indexed_trace_access/indexed_trace_access_plonky3.rs index c4fa5cd08..0bd768900 100644 --- a/air-script/tests/indexed_trace_access/indexed_trace_access_plonky3.rs +++ b/air-script/tests/indexed_trace_access/indexed_trace_access_plonky3.rs @@ -37,7 +37,7 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[0]); - builder.when_transition().assert_zero::<_>(main_next[0] - (main_current[1] + AB::Expr::from(AB::F::from_u64(1)))); + builder.when_first_row().assert_zero::<_>(main_current[0].into()); + builder.when_transition().assert_zero::<_>(main_next[0].into() - (main_current[1].into() + AB::Expr::ONE)); } } \ No newline at end of file diff --git a/air-script/tests/list_comprehension/list_comprehension_nested_plonky3.rs b/air-script/tests/list_comprehension/list_comprehension_nested_plonky3.rs index 990eb5946..2a8b9181f 100644 --- a/air-script/tests/list_comprehension/list_comprehension_nested_plonky3.rs +++ b/air-script/tests/list_comprehension/list_comprehension_nested_plonky3.rs @@ -37,9 +37,9 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[0]); - builder.assert_zero::<_>(main_current[0] + main_current[1] * AB::Expr::from(AB::F::from_u64(2)) - AB::Expr::from(AB::F::from_u64(3))); - builder.assert_zero::<_>(main_current[0] * AB::Expr::from(AB::F::from_u64(2)) + main_current[1] * AB::Expr::from(AB::F::from_u64(3)) - AB::Expr::from(AB::F::from_u64(5))); - builder.assert_zero::<_>(main_current[0] * AB::Expr::from(AB::F::from_u64(3)) + main_current[1] * AB::Expr::from(AB::F::from_u64(4)) - AB::Expr::from(AB::F::from_u64(7))); + builder.when_first_row().assert_zero::<_>(main_current[0].into()); + builder.assert_zero::<_>(main_current[0].into() + main_current[1].into().double() - AB::Expr::from_u64(3)); + builder.assert_zero::<_>(main_current[0].into().double() + main_current[1].into() * AB::Expr::from_u64(3) - AB::Expr::from_u64(5)); + builder.assert_zero::<_>(main_current[0].into() * AB::Expr::from_u64(3) + main_current[1].into() * AB::Expr::from_u64(4) - AB::Expr::from_u64(7)); } } \ No newline at end of file diff --git a/air-script/tests/list_comprehension/list_comprehension_plonky3.rs b/air-script/tests/list_comprehension/list_comprehension_plonky3.rs index 519559827..b2133e873 100644 --- a/air-script/tests/list_comprehension/list_comprehension_plonky3.rs +++ b/air-script/tests/list_comprehension/list_comprehension_plonky3.rs @@ -37,12 +37,12 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[10]); - builder.assert_zero::<_>(main_current[0] - main_current[2]); - builder.assert_zero::<_>(main_current[4] - main_current[0] * AB::Expr::from(AB::F::from_u64(8)) * main_current[11]); - builder.when_transition().assert_zero::<_>(main_current[4] - main_current[0] * (main_next[8] - main_next[12])); - builder.assert_zero::<_>(main_current[6] - main_current[0] * (main_current[9] - main_current[14])); - builder.assert_zero::<_>(main_current[1] - (main_current[5] - main_current[8] - main_current[12] + AB::Expr::from(AB::F::from_u64(10)) + main_current[6] - main_current[9] - main_current[13] + AB::Expr::from(AB::F::from_u64(20)) + main_current[7] - main_current[10] - main_current[14])); - builder.assert_zero::<_>(main_current[14] - AB::Expr::from(AB::F::from_u64(10))); + builder.when_first_row().assert_zero::<_>(main_current[10].into()); + builder.assert_zero::<_>(main_current[0].into() - main_current[2].into()); + builder.assert_zero::<_>(main_current[4].into() - main_current[0].into() * AB::Expr::from_u64(8) * main_current[11].into()); + builder.when_transition().assert_zero::<_>(main_current[4].into() - main_current[0].into() * (main_next[8].into() - main_next[12].into())); + builder.assert_zero::<_>(main_current[6].into() - main_current[0].into() * (main_current[9].into() - main_current[14].into())); + builder.assert_zero::<_>(main_current[1].into() - (main_current[5].into() - main_current[8].into() - main_current[12].into() + AB::Expr::from_u64(10) + main_current[6].into() - main_current[9].into() - main_current[13].into() + AB::Expr::from_u64(20) + main_current[7].into() - main_current[10].into() - main_current[14].into())); + builder.assert_zero::<_>(main_current[14].into() - AB::Expr::from_u64(10)); } } \ No newline at end of file diff --git a/air-script/tests/list_folding/list_folding_plonky3.rs b/air-script/tests/list_folding/list_folding_plonky3.rs index 1c4f68dca..c1cd5e912 100644 --- a/air-script/tests/list_folding/list_folding_plonky3.rs +++ b/air-script/tests/list_folding/list_folding_plonky3.rs @@ -37,10 +37,10 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[11]); - builder.when_transition().assert_zero::<_>(main_next[5] - (main_current[9] + main_current[10] + main_current[11] + main_current[12] + main_current[13] * main_current[14] * main_current[15] * main_current[16])); - builder.when_transition().assert_zero::<_>(main_next[6] - (main_current[9] + main_current[10] + main_current[11] + main_current[12] + main_current[13] * main_current[14] * main_current[15] * main_current[16])); - builder.when_transition().assert_zero::<_>(main_next[7] - (main_current[9] * main_current[13] + main_current[10] * main_current[14] + main_current[11] * main_current[15] + main_current[12] * main_current[16] + (main_current[9] + main_current[13]) * (main_current[10] + main_current[14]) * (main_current[11] + main_current[15]) * (main_current[12] + main_current[16]))); - builder.when_transition().assert_zero::<_>(main_next[8] - (main_current[1] + main_current[9] * main_current[13] + main_current[10] * main_current[14] + main_current[11] * main_current[15] + main_current[12] * main_current[16] + main_current[9] * main_current[13] + main_current[10] * main_current[14] + main_current[11] * main_current[15] + main_current[12] * main_current[16])); + builder.when_first_row().assert_zero::<_>(main_current[11].into()); + builder.when_transition().assert_zero::<_>(main_next[5].into() - (main_current[9].into() + main_current[10].into() + main_current[11].into() + main_current[12].into() + main_current[13].into() * main_current[14].into() * main_current[15].into() * main_current[16].into())); + builder.when_transition().assert_zero::<_>(main_next[6].into() - (main_current[9].into() + main_current[10].into() + main_current[11].into() + main_current[12].into() + main_current[13].into() * main_current[14].into() * main_current[15].into() * main_current[16].into())); + builder.when_transition().assert_zero::<_>(main_next[7].into() - (main_current[9].into() * main_current[13].into() + main_current[10].into() * main_current[14].into() + main_current[11].into() * main_current[15].into() + main_current[12].into() * main_current[16].into() + (main_current[9].into() + main_current[13].into()) * (main_current[10].into() + main_current[14].into()) * (main_current[11].into() + main_current[15].into()) * (main_current[12].into() + main_current[16].into()))); + builder.when_transition().assert_zero::<_>(main_next[8].into() - (main_current[1].into() + main_current[9].into() * main_current[13].into() + main_current[10].into() * main_current[14].into() + main_current[11].into() * main_current[15].into() + main_current[12].into() * main_current[16].into() + main_current[9].into() * main_current[13].into() + main_current[10].into() * main_current[14].into() + main_current[11].into() * main_current[15].into() + main_current[12].into() * main_current[16].into())); } } \ No newline at end of file diff --git a/air-script/tests/periodic_columns/periodic_columns_plonky3.rs b/air-script/tests/periodic_columns/periodic_columns_plonky3.rs index 9eb4f5447..fb42592e1 100644 --- a/air-script/tests/periodic_columns/periodic_columns_plonky3.rs +++ b/air-script/tests/periodic_columns/periodic_columns_plonky3.rs @@ -39,8 +39,8 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[0]); - builder.assert_zero::<_>(periodic_values[0].into() * (main_current[1] + main_current[2])); - builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[0] - main_current[0])); + builder.when_first_row().assert_zero::<_>(main_current[0].into()); + builder.assert_zero::<_>(periodic_values[0].into() * (main_current[1].into() + main_current[2].into())); + builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[0].into() - main_current[0].into())); } } \ No newline at end of file diff --git a/air-script/tests/pub_inputs/pub_inputs_plonky3.rs b/air-script/tests/pub_inputs/pub_inputs_plonky3.rs index a34e8aca4..cb2ac03b7 100644 --- a/air-script/tests/pub_inputs/pub_inputs_plonky3.rs +++ b/air-script/tests/pub_inputs/pub_inputs_plonky3.rs @@ -37,14 +37,14 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[0] - public_values[8].into()); - builder.when_first_row().assert_zero::<_>(main_current[1] - public_values[9].into()); - builder.when_first_row().assert_zero::<_>(main_current[2] - public_values[10].into()); - builder.when_first_row().assert_zero::<_>(main_current[3] - public_values[11].into()); - builder.when_last_row().assert_zero::<_>(main_current[0] - public_values[12].into()); - builder.when_last_row().assert_zero::<_>(main_current[1] - public_values[13].into()); - builder.when_last_row().assert_zero::<_>(main_current[2] - public_values[14].into()); - builder.when_last_row().assert_zero::<_>(main_current[3] - public_values[15].into()); - builder.when_transition().assert_zero::<_>(main_next[0] - (main_current[1] + main_current[2])); + builder.when_first_row().assert_zero::<_>(main_current[0].into() - public_values[8].into()); + builder.when_first_row().assert_zero::<_>(main_current[1].into() - public_values[9].into()); + builder.when_first_row().assert_zero::<_>(main_current[2].into() - public_values[10].into()); + builder.when_first_row().assert_zero::<_>(main_current[3].into() - public_values[11].into()); + builder.when_last_row().assert_zero::<_>(main_current[0].into() - public_values[12].into()); + builder.when_last_row().assert_zero::<_>(main_current[1].into() - public_values[13].into()); + builder.when_last_row().assert_zero::<_>(main_current[2].into() - public_values[14].into()); + builder.when_last_row().assert_zero::<_>(main_current[3].into() - public_values[15].into()); + builder.when_transition().assert_zero::<_>(main_next[0].into() - (main_current[1].into() + main_current[2].into())); } } \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_combine_complex_plonky3.rs b/air-script/tests/selectors/selectors_combine_complex_plonky3.rs index 5db82c3d8..c8115936b 100644 --- a/air-script/tests/selectors/selectors_combine_complex_plonky3.rs +++ b/air-script/tests/selectors/selectors_combine_complex_plonky3.rs @@ -37,9 +37,9 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[5]); - builder.assert_zero::<_>((main_current[0] + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * main_current[1]) * (main_current[3] - AB::Expr::from(AB::F::from_u64(16))) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * (main_current[4] - AB::Expr::from(AB::F::from_u64(5)))); - builder.assert_zero::<_>((AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * (main_current[5] - AB::Expr::from(AB::F::from_u64(5))) + main_current[0] * (main_current[4] - AB::Expr::from(AB::F::from_u64(4)))); - builder.assert_zero::<_>(main_current[0] * (main_current[5] - AB::Expr::from(AB::F::from_u64(20))) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * main_current[1] * (main_current[4] - AB::Expr::from(AB::F::from_u64(31)))); + builder.when_first_row().assert_zero::<_>(main_current[5].into()); + builder.assert_zero::<_>((main_current[0].into() + (AB::Expr::ONE - main_current[0].into()) * main_current[1].into()) * (main_current[3].into() - AB::Expr::from_u64(16)) + (AB::Expr::ONE - main_current[0].into()) * (AB::Expr::ONE - main_current[1].into()) * (main_current[4].into() - AB::Expr::from_u64(5))); + builder.assert_zero::<_>((AB::Expr::ONE - main_current[0].into()) * (main_current[5].into() - AB::Expr::from_u64(5)) + main_current[0].into() * (main_current[4].into() - AB::Expr::from_u64(4))); + builder.assert_zero::<_>(main_current[0].into() * (main_current[5].into() - AB::Expr::from_u64(20)) + (AB::Expr::ONE - main_current[0].into()) * main_current[1].into() * (main_current[4].into() - AB::Expr::from_u64(31))); } } \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_combine_simple_plonky3.rs b/air-script/tests/selectors/selectors_combine_simple_plonky3.rs index de42005a7..c981e950b 100644 --- a/air-script/tests/selectors/selectors_combine_simple_plonky3.rs +++ b/air-script/tests/selectors/selectors_combine_simple_plonky3.rs @@ -37,8 +37,8 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[3]); - builder.when_transition().assert_zero::<_>(main_next[1] - main_current[2]); - builder.when_transition().assert_zero::<_>(main_current[3] * (main_next[0] - (main_current[0] + main_current[1])) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[3]) * (main_next[0] - main_current[0] * main_current[1])); + builder.when_first_row().assert_zero::<_>(main_current[3].into()); + builder.when_transition().assert_zero::<_>(main_next[1].into() - main_current[2].into()); + builder.when_transition().assert_zero::<_>(main_current[3].into() * (main_next[0].into() - (main_current[0].into() + main_current[1].into())) + (AB::Expr::ONE - main_current[3].into()) * (main_next[0].into() - main_current[0].into() * main_current[1].into())); } } \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs b/air-script/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs index 0f5f4f571..1a937a640 100644 --- a/air-script/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs +++ b/air-script/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs @@ -37,10 +37,9 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[5]); - builder.assert_zero::<_>(main_current[0] * main_current[3] + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * main_current[1] * main_current[3] + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * (main_current[4] - AB::Expr::from(AB::F::from_u64(8)))); - builder.assert_zero::<_>(main_current[0] * (main_current[4] - AB::Expr::from(AB::F::from_u64(2))) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * main_current[1] * (main_current[4] - AB::Expr::from(AB::F::from_u64(6))) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * (main_current[5] - AB::Expr::from(AB::F::from_u64(8)))); - builder.assert_zero::<_>(main_current[0] * main_current[3] + (AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * main_current[1] * (main_current[5] - AB::Expr::from(AB::F::from_u64(8)))); - builder.assert_zero::<_>(main_current[0] * (main_current[5] - AB::Expr::from(AB::F::from_u64(4)))); + builder.when_first_row().assert_zero::<_>(main_current[5].into()); + builder.assert_zero::<_>((main_current[0].into() + (AB::Expr::ONE - main_current[0].into()) * main_current[1].into()) * main_current[3].into() + (AB::Expr::ONE - main_current[0].into()) * (AB::Expr::ONE - main_current[1].into()) * (main_current[4].into() - AB::Expr::from_u64(8))); + builder.assert_zero::<_>((AB::Expr::ONE - main_current[0].into()) * (main_current[5].into() - AB::Expr::from_u64(8)) + main_current[0].into() * (main_current[4].into() - AB::Expr::from_u64(2))); + builder.assert_zero::<_>(main_current[0].into() * (main_current[5].into() - AB::Expr::from_u64(4)) + (AB::Expr::ONE - main_current[0].into()) * main_current[1].into() * (main_current[4].into() - AB::Expr::from_u64(6))); } } \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_plonky3.rs b/air-script/tests/selectors/selectors_plonky3.rs index 9f0ff795b..7b0b2f210 100644 --- a/air-script/tests/selectors/selectors_plonky3.rs +++ b/air-script/tests/selectors/selectors_plonky3.rs @@ -37,8 +37,8 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[3]); - builder.when_transition().assert_zero::<_>(main_current[0] * (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * main_next[3]); - builder.when_transition().assert_zero::<_>(main_current[0] * main_current[1] * main_current[2] * (main_next[3] - main_current[3]) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * (AB::Expr::from(AB::F::from_u64(1)) - main_current[2]) * (main_next[3] - AB::Expr::from(AB::F::from_u64(1)))); + builder.when_first_row().assert_zero::<_>(main_current[3].into()); + builder.when_transition().assert_zero::<_>(main_current[0].into() * (AB::Expr::ONE - main_current[1].into()) * main_next[3].into()); + builder.when_transition().assert_zero::<_>(main_current[0].into() * main_current[1].into() * main_current[2].into() * (main_next[3].into() - main_current[3].into()) + (AB::Expr::ONE - main_current[1].into()) * (AB::Expr::ONE - main_current[2].into()) * (main_next[3].into() - AB::Expr::ONE)); } } \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_with_evaluators_plonky3.rs b/air-script/tests/selectors/selectors_with_evaluators_plonky3.rs index 1dd734eec..10fd5fb98 100644 --- a/air-script/tests/selectors/selectors_with_evaluators_plonky3.rs +++ b/air-script/tests/selectors/selectors_with_evaluators_plonky3.rs @@ -37,8 +37,8 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[3]); - builder.when_transition().assert_zero::<_>(main_current[0] * (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * main_next[3]); - builder.when_transition().assert_zero::<_>(main_current[1] * main_current[2] * main_current[0] * (main_next[3] - main_current[3]) + (AB::Expr::from(AB::F::from_u64(1)) - main_current[1]) * (AB::Expr::from(AB::F::from_u64(1)) - main_current[2]) * (main_next[3] - AB::Expr::from(AB::F::from_u64(1)))); + builder.when_first_row().assert_zero::<_>(main_current[3].into()); + builder.when_transition().assert_zero::<_>(main_current[0].into() * (AB::Expr::ONE - main_current[1].into()) * main_next[3].into()); + builder.when_transition().assert_zero::<_>(main_current[1].into() * main_current[2].into() * main_current[0].into() * (main_next[3].into() - main_current[3].into()) + (AB::Expr::ONE - main_current[1].into()) * (AB::Expr::ONE - main_current[2].into()) * (main_next[3].into() - AB::Expr::ONE)); } } \ No newline at end of file diff --git a/air-script/tests/system/system_plonky3.rs b/air-script/tests/system/system_plonky3.rs index 10af52c3a..ca2204da0 100644 --- a/air-script/tests/system/system_plonky3.rs +++ b/air-script/tests/system/system_plonky3.rs @@ -37,7 +37,7 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[0]); - builder.when_transition().assert_zero::<_>(main_next[0] - (main_current[0] + AB::Expr::from(AB::F::from_u64(1)))); + builder.when_first_row().assert_zero::<_>(main_current[0].into()); + builder.when_transition().assert_zero::<_>(main_next[0].into() - (main_current[0].into() + AB::Expr::ONE)); } } \ No newline at end of file diff --git a/air-script/tests/trace_col_groups/trace_col_groups_plonky3.rs b/air-script/tests/trace_col_groups/trace_col_groups_plonky3.rs index bb500087e..08bf27975 100644 --- a/air-script/tests/trace_col_groups/trace_col_groups_plonky3.rs +++ b/air-script/tests/trace_col_groups/trace_col_groups_plonky3.rs @@ -37,8 +37,8 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[8]); - builder.when_transition().assert_zero::<_>(main_next[2] - (main_current[2] + AB::Expr::from(AB::F::from_u64(1)))); - builder.when_transition().assert_zero::<_>(main_next[1] - (main_current[1] - AB::Expr::from(AB::F::from_u64(1)))); + builder.when_first_row().assert_zero::<_>(main_current[8].into()); + builder.when_transition().assert_zero::<_>(main_next[2].into() - (main_current[2].into() + AB::Expr::ONE)); + builder.when_transition().assert_zero::<_>(main_next[1].into() - (main_current[1].into() - AB::Expr::ONE)); } } \ No newline at end of file diff --git a/air-script/tests/variables/variables_plonky3.rs b/air-script/tests/variables/variables_plonky3.rs index 73408ce98..48ce365c8 100644 --- a/air-script/tests/variables/variables_plonky3.rs +++ b/air-script/tests/variables/variables_plonky3.rs @@ -38,11 +38,11 @@ impl Air for main.row_slice(0).unwrap(), main.row_slice(1).unwrap(), ); - builder.when_first_row().assert_zero::<_>(main_current[1]); - builder.when_last_row().assert_zero::<_>(main_current[1] - AB::Expr::from(AB::F::from_u64(1))); - builder.assert_zero::<_>(main_current[0] * main_current[0] - main_current[0]); - builder.when_transition().assert_zero::<_>(periodic_values[0].into() * (main_next[0] - main_current[0])); - builder.assert_zero::<_>((AB::Expr::from(AB::F::from_u64(1)) - main_current[0]) * (main_current[3] - main_current[1] - main_current[2]) - (AB::Expr::from(AB::F::from_u64(6)) - (AB::Expr::from(AB::F::from_u64(7)) - main_current[0]))); - builder.when_transition().assert_zero::<_>(main_current[0] * (main_current[3] - main_current[1] * main_current[2]) - (AB::Expr::from(AB::F::from_u64(1)) - main_next[0])); + builder.when_first_row().assert_zero::<_>(main_current[1].into()); + builder.when_last_row().assert_zero::<_>(main_current[1].into() - AB::Expr::ONE); + builder.assert_zero::<_>(main_current[0].into() * main_current[0].into() - main_current[0].into()); + builder.when_transition().assert_zero::<_>(periodic_values[0].into() * (main_next[0].into() - main_current[0].into())); + builder.assert_zero::<_>((AB::Expr::ONE - main_current[0].into()) * (main_current[3].into() - main_current[1].into() - main_current[2].into()) - (AB::Expr::from_u64(6) - (AB::Expr::from_u64(7) - main_current[0].into()))); + builder.when_transition().assert_zero::<_>(main_current[0].into() * (main_current[3].into() - main_current[1].into() * main_current[2].into()) - (AB::Expr::ONE - main_next[0].into())); } } \ No newline at end of file From dee6af7b216e9b3495c7e912166c1370e7f7415b Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Tue, 14 Oct 2025 11:15:17 +0200 Subject: [PATCH 15/83] refactor(tests): Use macro for plonky3 test boilerplate --- air-script/tests/binary/test_air_plonky3.rs | 45 ++------------ .../tests/binary/test_air_winterfell.rs | 9 ++- air-script/tests/bitwise/test_air_plonky3.rs | 45 ++------------ .../tests/bitwise/test_air_winterfell.rs | 9 ++- air-script/tests/buses/test_air_plonky3.rs | 39 ++---------- air-script/tests/buses/test_air_winterfell.rs | 9 ++- .../constant_in_range/test_air_plonky3.rs | 39 ++---------- .../constant_in_range/test_air_winterfell.rs | 4 +- .../tests/constants/test_air_plonky3.rs | 39 ++---------- .../tests/constants/test_air_winterfell.rs | 4 +- .../test_air_plonky3.rs | 39 ++---------- .../test_air_winterfell.rs | 4 +- .../tests/evaluators/test_air_plonky3.rs | 39 ++---------- .../tests/evaluators/test_air_winterfell.rs | 4 +- .../tests/fibonacci/test_air_plonky3.rs | 40 ++---------- .../tests/fibonacci/test_air_winterfell.rs | 4 +- .../tests/functions/test_air_plonky3.rs | 39 ++---------- .../tests/functions/test_air_winterfell.rs | 4 +- air-script/tests/helpers/macros.rs | 61 ++++++++++++++++++- .../indexed_trace_access/test_air_plonky3.rs | 39 ++---------- .../test_air_winterfell.rs | 4 +- .../list_comprehension/test_air_plonky3.rs | 38 ++---------- .../list_comprehension/test_air_winterfell.rs | 4 +- .../tests/list_folding/test_air_plonky3.rs | 39 ++---------- .../tests/list_folding/test_air_winterfell.rs | 4 +- .../periodic_columns/test_air_plonky3.rs | 39 ++---------- .../periodic_columns/test_air_winterfell.rs | 4 +- .../tests/pub_inputs/test_air_plonky3.rs | 39 ++---------- .../tests/pub_inputs/test_air_winterfell.rs | 4 +- .../tests/selectors/test_air_plonky3.rs | 39 ++---------- .../tests/selectors/test_air_winterfell.rs | 4 +- air-script/tests/system/test_air_plonky3.rs | 39 ++---------- .../tests/system/test_air_winterfell.rs | 9 ++- .../trace_col_groups/test_air_plonky3.rs | 39 ++---------- .../trace_col_groups/test_air_winterfell.rs | 4 +- .../tests/variables/test_air_plonky3.rs | 39 ++---------- .../tests/variables/test_air_winterfell.rs | 4 +- 37 files changed, 203 insertions(+), 664 deletions(-) diff --git a/air-script/tests/binary/test_air_plonky3.rs b/air-script/tests/binary/test_air_plonky3.rs index da7b12a53..ac4e20894 100644 --- a/air-script/tests/binary/test_air_plonky3.rs +++ b/air-script/tests/binary/test_air_plonky3.rs @@ -14,6 +14,7 @@ use p3_uni_stark::StarkConfig; use crate::{ binary::binary_plonky3::{BinaryAir, NUM_COLUMNS}, + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, }; @@ -47,44 +48,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&BinaryAir {}, &trace, &inputs_m31); - - /*let prove_with_periodic_columns = prove_with_periodic_columns(&config, &BinaryAir {}, trace, &inputs_m31); - verify_with_periodic_columns(&config, &BinaryAir {}, &prove_with_periodic_columns, &inputs_m31).expect("Verification failed");*/ - - /*let proof = prove(&config, &BinaryAir {}, trace, &inputs_m31); - verify(&config, &BinaryAir {}, &proof, &inputs_m31).expect("Verification failed");*/ +fn generate_inputs() -> Vec { + vec![1; 16] } + +generate_air_plonky3_test!(test_air_plonky3, BinaryAir); diff --git a/air-script/tests/binary/test_air_winterfell.rs b/air-script/tests/binary/test_air_winterfell.rs index 4f40af405..eca996b11 100644 --- a/air-script/tests/binary/test_air_winterfell.rs +++ b/air-script/tests/binary/test_air_winterfell.rs @@ -4,7 +4,7 @@ use winterfell::{Trace, TraceTable}; use crate::{ binary::binary::PublicInputs, - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, }; @@ -39,4 +39,9 @@ impl AirTester for BinaryAirTester { } } -generate_air_test!(test_binary_air, crate::binary::binary::BinaryAir, BinaryAirTester, 1024); +generate_air_winterfell_test!( + test_binary_air, + crate::binary::binary::BinaryAir, + BinaryAirTester, + 1024 +); diff --git a/air-script/tests/bitwise/test_air_plonky3.rs b/air-script/tests/bitwise/test_air_plonky3.rs index c5c30ed7e..1bb3a9f89 100644 --- a/air-script/tests/bitwise/test_air_plonky3.rs +++ b/air-script/tests/bitwise/test_air_plonky3.rs @@ -14,6 +14,7 @@ use p3_uni_stark::StarkConfig; use crate::{ bitwise::bitwise_plonky3::{BitwiseAir, NUM_COLUMNS}, + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, }; @@ -85,44 +86,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&BitwiseAir {}, &trace, &inputs_m31); - - /*let prove_with_periodic_columns = prove_with_periodic_columns(&config, &BitwiseAir {}, trace, &inputs_m31); - verify_with_periodic_columns(&config, &BitwiseAir {}, &prove_with_periodic_columns, &inputs_m31).expect("Verification failed");*/ - - /*let proof = prove(&config, &BitwiseAir {}, trace, &inputs_m31); - verify(&config, &BitwiseAir {}, &proof, &inputs_m31).expect("Verification failed");*/ +fn generate_inputs() -> Vec { + vec![1; 16] } + +generate_air_plonky3_test!(test_air_plonky3, BitwiseAir); diff --git a/air-script/tests/bitwise/test_air_winterfell.rs b/air-script/tests/bitwise/test_air_winterfell.rs index 16daca900..c5166f091 100644 --- a/air-script/tests/bitwise/test_air_winterfell.rs +++ b/air-script/tests/bitwise/test_air_winterfell.rs @@ -6,7 +6,7 @@ use winterfell::{AuxTraceWithMetadata, Trace, TraceTable, matrix::ColMatrix}; use crate::{ bitwise::bitwise::PublicInputs, - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, }; @@ -50,4 +50,9 @@ impl AirTester for BitwiseAirTester { } } -generate_air_test!(test_bitwise_air, crate::bitwise::bitwise::BitwiseAir, BitwiseAirTester, 1024); +generate_air_winterfell_test!( + test_bitwise_air, + crate::bitwise::bitwise::BitwiseAir, + BitwiseAirTester, + 1024 +); diff --git a/air-script/tests/buses/test_air_plonky3.rs b/air-script/tests/buses/test_air_plonky3.rs index e465396df..a6671ba82 100644 --- a/air-script/tests/buses/test_air_plonky3.rs +++ b/air-script/tests/buses/test_air_plonky3.rs @@ -14,6 +14,7 @@ use p3_uni_stark::StarkConfig; use crate::{ buses::buses_complex_plonky3::{BusesAir, NUM_COLUMNS}, + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, }; @@ -47,38 +48,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 2]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&BusesAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 2] } + +generate_air_plonky3_test!(test_air_plonky3, BusesAir); diff --git a/air-script/tests/buses/test_air_winterfell.rs b/air-script/tests/buses/test_air_winterfell.rs index 4c27304a2..7bf2ce5ea 100644 --- a/air-script/tests/buses/test_air_winterfell.rs +++ b/air-script/tests/buses/test_air_winterfell.rs @@ -4,7 +4,7 @@ use winterfell::{AuxTraceWithMetadata, Trace, TraceTable, matrix::ColMatrix}; use crate::{ buses::buses_complex::PublicInputs, - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, }; @@ -57,4 +57,9 @@ impl AirTester for BusesAirTester { } } -generate_air_test!(test_buses_air, crate::buses::buses_complex::BusesAir, BusesAirTester, 1024); +generate_air_winterfell_test!( + test_buses_air, + crate::buses::buses_complex::BusesAir, + BusesAirTester, + 1024 +); diff --git a/air-script/tests/constant_in_range/test_air_plonky3.rs b/air-script/tests/constant_in_range/test_air_plonky3.rs index 15af390e6..2518eaf03 100644 --- a/air-script/tests/constant_in_range/test_air_plonky3.rs +++ b/air-script/tests/constant_in_range/test_air_plonky3.rs @@ -14,6 +14,7 @@ use p3_uni_stark::StarkConfig; use crate::{ constant_in_range::constant_in_range_plonky3::{ConstantInRangeAir, NUM_COLUMNS}, + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, }; @@ -66,38 +67,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&ConstantInRangeAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 16] } + +generate_air_plonky3_test!(test_air_plonky3, ConstantInRangeAir); diff --git a/air-script/tests/constant_in_range/test_air_winterfell.rs b/air-script/tests/constant_in_range/test_air_winterfell.rs index 064e5eb19..3672378c8 100644 --- a/air-script/tests/constant_in_range/test_air_winterfell.rs +++ b/air-script/tests/constant_in_range/test_air_winterfell.rs @@ -4,7 +4,7 @@ use winterfell::{Trace, TraceTable}; use crate::{ constant_in_range::constant_in_range::PublicInputs, - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, }; @@ -46,7 +46,7 @@ impl AirTester for ConstantInRangeAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_constant_in_range_air, crate::constant_in_range::constant_in_range::ConstantInRangeAir, ConstantInRangeAirTester, diff --git a/air-script/tests/constants/test_air_plonky3.rs b/air-script/tests/constants/test_air_plonky3.rs index f4d12edd5..04c4b3e16 100644 --- a/air-script/tests/constants/test_air_plonky3.rs +++ b/air-script/tests/constants/test_air_plonky3.rs @@ -14,6 +14,7 @@ use p3_uni_stark::StarkConfig; use crate::{ constants::constants_plonky3::{ConstantsAir, NUM_COLUMNS}, + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, }; @@ -62,38 +63,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 32]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&ConstantsAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 32] } + +generate_air_plonky3_test!(test_air_plonky3, ConstantsAir); diff --git a/air-script/tests/constants/test_air_winterfell.rs b/air-script/tests/constants/test_air_winterfell.rs index 97a048a31..9459000d7 100644 --- a/air-script/tests/constants/test_air_winterfell.rs +++ b/air-script/tests/constants/test_air_winterfell.rs @@ -4,7 +4,7 @@ use winterfell::{Trace, TraceTable}; use crate::{ constants::constants::PublicInputs, - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, }; @@ -44,7 +44,7 @@ impl AirTester for ConstantsAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_constants_air, crate::constants::constants::ConstantsAir, ConstantsAirTester, diff --git a/air-script/tests/constraint_comprehension/test_air_plonky3.rs b/air-script/tests/constraint_comprehension/test_air_plonky3.rs index c2243f2ea..593107425 100644 --- a/air-script/tests/constraint_comprehension/test_air_plonky3.rs +++ b/air-script/tests/constraint_comprehension/test_air_plonky3.rs @@ -16,6 +16,7 @@ use crate::{ constraint_comprehension::constraint_comprehension_plonky3::{ ConstraintComprehensionAir, NUM_COLUMNS, }, + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, }; @@ -49,38 +50,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&ConstraintComprehensionAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 16] } + +generate_air_plonky3_test!(test_air_plonky3, ConstraintComprehensionAir); diff --git a/air-script/tests/constraint_comprehension/test_air_winterfell.rs b/air-script/tests/constraint_comprehension/test_air_winterfell.rs index 6bdb9cc8d..73a24bf0f 100644 --- a/air-script/tests/constraint_comprehension/test_air_winterfell.rs +++ b/air-script/tests/constraint_comprehension/test_air_winterfell.rs @@ -4,7 +4,7 @@ use winterfell::{Trace, TraceTable}; use crate::{ constraint_comprehension::constraint_comprehension::PublicInputs, - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, }; @@ -48,7 +48,7 @@ impl AirTester for ConstraintComprehensionAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_constraint_comprehension_air, crate::constraint_comprehension::constraint_comprehension::ConstraintComprehensionAir, ConstraintComprehensionAirTester, diff --git a/air-script/tests/evaluators/test_air_plonky3.rs b/air-script/tests/evaluators/test_air_plonky3.rs index 9ba08a264..76e8ffc82 100644 --- a/air-script/tests/evaluators/test_air_plonky3.rs +++ b/air-script/tests/evaluators/test_air_plonky3.rs @@ -14,6 +14,7 @@ use p3_uni_stark::StarkConfig; use crate::{ evaluators::evaluators_plonky3::{EvaluatorsAir, NUM_COLUMNS}, + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, }; @@ -62,38 +63,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&EvaluatorsAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 16] } + +generate_air_plonky3_test!(test_air_plonky3, EvaluatorsAir); diff --git a/air-script/tests/evaluators/test_air_winterfell.rs b/air-script/tests/evaluators/test_air_winterfell.rs index 6bf42ed83..fa8f6f6d8 100644 --- a/air-script/tests/evaluators/test_air_winterfell.rs +++ b/air-script/tests/evaluators/test_air_winterfell.rs @@ -4,7 +4,7 @@ use winterfell::{Trace, TraceTable}; use crate::{ evaluators::evaluators::PublicInputs, - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, }; @@ -41,7 +41,7 @@ impl AirTester for EvaluatorsAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_evaluators_air, crate::evaluators::evaluators::EvaluatorsAir, EvaluatorsAirTester, diff --git a/air-script/tests/fibonacci/test_air_plonky3.rs b/air-script/tests/fibonacci/test_air_plonky3.rs index fdfbd8132..10e671df8 100644 --- a/air-script/tests/fibonacci/test_air_plonky3.rs +++ b/air-script/tests/fibonacci/test_air_plonky3.rs @@ -14,6 +14,7 @@ use p3_uni_stark::StarkConfig; use crate::{ fibonacci::fibonacci_plonky3::{FibonacciAir, NUM_COLUMNS}, + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, }; @@ -47,41 +48,10 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - +fn generate_inputs() -> Vec { let one = 1; let last = 2178309; // 32nd Fibonacci number - let inputs = vec![one, one, last]; - - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&FibonacciAir {}, &trace, &inputs_m31); + vec![one, one, last] } + +generate_air_plonky3_test!(test_air_plonky3, FibonacciAir); diff --git a/air-script/tests/fibonacci/test_air_winterfell.rs b/air-script/tests/fibonacci/test_air_winterfell.rs index 10162f645..5839643d7 100644 --- a/air-script/tests/fibonacci/test_air_winterfell.rs +++ b/air-script/tests/fibonacci/test_air_winterfell.rs @@ -6,7 +6,7 @@ use winterfell::{AuxTraceWithMetadata, Trace, TraceTable, matrix::ColMatrix}; use crate::{ fibonacci::fibonacci::PublicInputs, - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, }; @@ -45,7 +45,7 @@ impl AirTester for FibonacciAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_fibonacci_air, crate::fibonacci::fibonacci::FibonacciAir, FibonacciAirTester, diff --git a/air-script/tests/functions/test_air_plonky3.rs b/air-script/tests/functions/test_air_plonky3.rs index 84da79962..d9a590894 100644 --- a/air-script/tests/functions/test_air_plonky3.rs +++ b/air-script/tests/functions/test_air_plonky3.rs @@ -14,6 +14,7 @@ use p3_uni_stark::StarkConfig; use crate::{ functions::functions_complex_plonky3::{FunctionsAir, NUM_COLUMNS}, + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, }; @@ -92,38 +93,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&FunctionsAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 16] } + +generate_air_plonky3_test!(test_air_plonky3, FunctionsAir); diff --git a/air-script/tests/functions/test_air_winterfell.rs b/air-script/tests/functions/test_air_winterfell.rs index ae578bf0d..0357e9039 100644 --- a/air-script/tests/functions/test_air_winterfell.rs +++ b/air-script/tests/functions/test_air_winterfell.rs @@ -4,7 +4,7 @@ use winterfell::{Trace, TraceTable}; use crate::{ functions::functions_complex::PublicInputs, - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, }; @@ -68,7 +68,7 @@ impl AirTester for FunctionsAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_functions_complex_air, crate::functions::functions_complex::FunctionsAir, FunctionsAirTester, diff --git a/air-script/tests/helpers/macros.rs b/air-script/tests/helpers/macros.rs index e134b8d48..3b4ebad6e 100644 --- a/air-script/tests/helpers/macros.rs +++ b/air-script/tests/helpers/macros.rs @@ -1,6 +1,6 @@ -// Helper macros for test generation +// Helper macros for Winterfell test generation -/// Generates an AIR test function with the standard boilerplate +/// Generates a Winterfell AIR test function with the standard boilerplate /// /// # Arguments /// * `test_name` - The identifier for the test function (e.g., `test_binary_air`) @@ -8,7 +8,7 @@ /// * `tester_name` - The identifier for the `AirTester` struct (e.g., `BinaryAirTester`) /// * `trace_length` - The length of the trace for the test (e.g., `32` or `1024`) #[macro_export] -macro_rules! generate_air_test { +macro_rules! generate_air_winterfell_test { ($test_name:ident, $air_name:path, $tester_name:ident, $trace_length:expr) => { #[test] fn $test_name() { @@ -27,3 +27,58 @@ macro_rules! generate_air_test { } }; } + +// Helper macros for Plonky3 test generation + +/// Generates a Plonky3 AIR test function with the standard boilerplate +/// +/// # Arguments +/// * `test_name` - The identifier for the test function (e.g., `test_binary_air`) +/// * `air_name` - The identifier for the AIR struct (e.g., `BinaryAir`) +#[macro_export] +macro_rules! generate_air_plonky3_test { + ($test_name:ident, $air_name:ident) => { + #[test] + fn $test_name() { + type Val = Mersenne31; + type Challenge = BinomialExtensionField; + + type ByteHash = Sha256; + type FieldHash = SerializingHasher; + type MyCompress = CompressionFunctionFromHasher; + type ValMmcs = MerkleTreeMmcs; + type ChallengeMmcs = ExtensionMmcs; + type Challenger = SerializingChallenger32>; + type Pcs = CirclePcs; + type MyConfig = StarkConfig; + + let byte_hash = ByteHash {}; + let field_hash = FieldHash::new(Sha256); + let compress = MyCompress::new(byte_hash); + let val_mmcs = ValMmcs::new(field_hash, compress); + let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); + let challenger = Challenger::from_hasher(vec![], byte_hash); + let fri_params = create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs { + mmcs: val_mmcs, + fri_params, + _phantom: PhantomData, + }; + let config = MyConfig::new(pcs, challenger); + + let inputs = generate_inputs(); + let inputs_m31: Vec = + inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + + let trace = generate_trace_rows::(inputs); + + check_constraints_with_periodic_columns(&$air_name {}, &trace, &inputs_m31); + + /*let prove_with_periodic_columns = prove_with_periodic_columns(&config, &BitwiseAir {}, trace, &inputs_m31); + verify_with_periodic_columns(&config, &BitwiseAir {}, &prove_with_periodic_columns, &inputs_m31).expect("Verification failed");*/ + + /*let proof = prove(&config, &BitwiseAir {}, trace, &inputs_m31); + verify(&config, &BitwiseAir {}, &proof, &inputs_m31).expect("Verification failed");*/ + } + }; +} diff --git a/air-script/tests/indexed_trace_access/test_air_plonky3.rs b/air-script/tests/indexed_trace_access/test_air_plonky3.rs index 8f1f7dfd3..cef3b72e8 100644 --- a/air-script/tests/indexed_trace_access/test_air_plonky3.rs +++ b/air-script/tests/indexed_trace_access/test_air_plonky3.rs @@ -13,6 +13,7 @@ use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; use crate::{ + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, indexed_trace_access::indexed_trace_access_plonky3::{NUM_COLUMNS, TraceAccessAir}, }; @@ -49,38 +50,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&TraceAccessAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 16] } + +generate_air_plonky3_test!(test_air_plonky3, TraceAccessAir); diff --git a/air-script/tests/indexed_trace_access/test_air_winterfell.rs b/air-script/tests/indexed_trace_access/test_air_winterfell.rs index 707dbba10..9d7e64bcb 100644 --- a/air-script/tests/indexed_trace_access/test_air_winterfell.rs +++ b/air-script/tests/indexed_trace_access/test_air_winterfell.rs @@ -3,7 +3,7 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, indexed_trace_access::indexed_trace_access::PublicInputs, }; @@ -40,7 +40,7 @@ impl AirTester for TraceAccessAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_indexed_trace_access_air, crate::indexed_trace_access::indexed_trace_access::TraceAccessAir, TraceAccessAirTester, diff --git a/air-script/tests/list_comprehension/test_air_plonky3.rs b/air-script/tests/list_comprehension/test_air_plonky3.rs index 0f8e6f879..70277f2c3 100644 --- a/air-script/tests/list_comprehension/test_air_plonky3.rs +++ b/air-script/tests/list_comprehension/test_air_plonky3.rs @@ -13,6 +13,7 @@ use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; use crate::{ + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, list_comprehension::list_comprehension_plonky3::{ListComprehensionAir, NUM_COLUMNS}, }; @@ -89,38 +90,7 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&ListComprehensionAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 16] } +generate_air_plonky3_test!(test_air_plonky3, ListComprehensionAir); diff --git a/air-script/tests/list_comprehension/test_air_winterfell.rs b/air-script/tests/list_comprehension/test_air_winterfell.rs index c3e9b396f..8fef13b07 100644 --- a/air-script/tests/list_comprehension/test_air_winterfell.rs +++ b/air-script/tests/list_comprehension/test_air_winterfell.rs @@ -3,7 +3,7 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, list_comprehension::list_comprehension::PublicInputs, }; @@ -52,7 +52,7 @@ impl AirTester for ListComprehensionAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_list_comprehension_air, crate::list_comprehension::list_comprehension::ListComprehensionAir, ListComprehensionAirTester, diff --git a/air-script/tests/list_folding/test_air_plonky3.rs b/air-script/tests/list_folding/test_air_plonky3.rs index 695d3906f..45871c969 100644 --- a/air-script/tests/list_folding/test_air_plonky3.rs +++ b/air-script/tests/list_folding/test_air_plonky3.rs @@ -13,6 +13,7 @@ use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; use crate::{ + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, list_folding::list_folding_plonky3::{ListFoldingAir, NUM_COLUMNS}, }; @@ -92,38 +93,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&ListFoldingAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 16] } + +generate_air_plonky3_test!(test_air_plonky3, ListFoldingAir); diff --git a/air-script/tests/list_folding/test_air_winterfell.rs b/air-script/tests/list_folding/test_air_winterfell.rs index 9d79fad28..f8376848d 100644 --- a/air-script/tests/list_folding/test_air_winterfell.rs +++ b/air-script/tests/list_folding/test_air_winterfell.rs @@ -3,7 +3,7 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, list_folding::list_folding::PublicInputs, }; @@ -53,7 +53,7 @@ impl AirTester for ListFoldingAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_list_folding_air, crate::list_folding::list_folding::ListFoldingAir, ListFoldingAirTester, diff --git a/air-script/tests/periodic_columns/test_air_plonky3.rs b/air-script/tests/periodic_columns/test_air_plonky3.rs index c380f7e88..589fab8e8 100644 --- a/air-script/tests/periodic_columns/test_air_plonky3.rs +++ b/air-script/tests/periodic_columns/test_air_plonky3.rs @@ -13,6 +13,7 @@ use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; use crate::{ + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, periodic_columns::periodic_columns_plonky3::{NUM_COLUMNS, PeriodicColumnsAir}, }; @@ -50,38 +51,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&PeriodicColumnsAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 16] } + +generate_air_plonky3_test!(test_air_plonky3, PeriodicColumnsAir); diff --git a/air-script/tests/periodic_columns/test_air_winterfell.rs b/air-script/tests/periodic_columns/test_air_winterfell.rs index f157a98df..0adc8364f 100644 --- a/air-script/tests/periodic_columns/test_air_winterfell.rs +++ b/air-script/tests/periodic_columns/test_air_winterfell.rs @@ -3,7 +3,7 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, periodic_columns::periodic_columns::PublicInputs, }; @@ -37,7 +37,7 @@ impl AirTester for PeriodicColumnsAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_periodic_columns_air, crate::periodic_columns::periodic_columns::PeriodicColumnsAir, PeriodicColumnsAirTester, diff --git a/air-script/tests/pub_inputs/test_air_plonky3.rs b/air-script/tests/pub_inputs/test_air_plonky3.rs index 071828dfb..a29b06143 100644 --- a/air-script/tests/pub_inputs/test_air_plonky3.rs +++ b/air-script/tests/pub_inputs/test_air_plonky3.rs @@ -13,6 +13,7 @@ use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; use crate::{ + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, pub_inputs::pub_inputs_plonky3::{NUM_COLUMNS, PubInputsAir}, }; @@ -53,38 +54,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![0; 32]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&PubInputsAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![0; 32] } + +generate_air_plonky3_test!(test_air_plonky3, PubInputsAir); diff --git a/air-script/tests/pub_inputs/test_air_winterfell.rs b/air-script/tests/pub_inputs/test_air_winterfell.rs index ad929a857..947f4bbac 100644 --- a/air-script/tests/pub_inputs/test_air_winterfell.rs +++ b/air-script/tests/pub_inputs/test_air_winterfell.rs @@ -3,7 +3,7 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, pub_inputs::pub_inputs::PublicInputs, }; @@ -38,7 +38,7 @@ impl AirTester for PubInputsAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_pub_inputs_air, crate::pub_inputs::pub_inputs::PubInputsAir, PubInputsAirTester, diff --git a/air-script/tests/selectors/test_air_plonky3.rs b/air-script/tests/selectors/test_air_plonky3.rs index 2b2366fe8..ad757c2fb 100644 --- a/air-script/tests/selectors/test_air_plonky3.rs +++ b/air-script/tests/selectors/test_air_plonky3.rs @@ -13,6 +13,7 @@ use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; use crate::{ + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, selectors::selectors_with_evaluators_plonky3::{NUM_COLUMNS, SelectorsAir}, }; @@ -53,38 +54,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&SelectorsAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 16] } + +generate_air_plonky3_test!(test_air_plonky3, SelectorsAir); diff --git a/air-script/tests/selectors/test_air_winterfell.rs b/air-script/tests/selectors/test_air_winterfell.rs index 4f2f3378b..19f683ef6 100644 --- a/air-script/tests/selectors/test_air_winterfell.rs +++ b/air-script/tests/selectors/test_air_winterfell.rs @@ -3,7 +3,7 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, selectors::selectors_with_evaluators::PublicInputs, }; @@ -40,7 +40,7 @@ impl AirTester for SelectorsAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_selectors_with_evaluators_air, crate::selectors::selectors_with_evaluators::SelectorsAir, SelectorsAirTester, diff --git a/air-script/tests/system/test_air_plonky3.rs b/air-script/tests/system/test_air_plonky3.rs index 5bee89503..93f0d769e 100644 --- a/air-script/tests/system/test_air_plonky3.rs +++ b/air-script/tests/system/test_air_plonky3.rs @@ -13,6 +13,7 @@ use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; use crate::{ + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, system::system_plonky3::{NUM_COLUMNS, SystemAir}, }; @@ -50,38 +51,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&SystemAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 16] } + +generate_air_plonky3_test!(test_air_plonky3, SystemAir); diff --git a/air-script/tests/system/test_air_winterfell.rs b/air-script/tests/system/test_air_winterfell.rs index 3aa6815c6..9327f6e98 100644 --- a/air-script/tests/system/test_air_winterfell.rs +++ b/air-script/tests/system/test_air_winterfell.rs @@ -3,7 +3,7 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, system::system::PublicInputs, }; @@ -39,4 +39,9 @@ impl AirTester for SystemAirTester { } } -generate_air_test!(test_system_air, crate::system::system::SystemAir, SystemAirTester, 1024); +generate_air_winterfell_test!( + test_system_air, + crate::system::system::SystemAir, + SystemAirTester, + 1024 +); diff --git a/air-script/tests/trace_col_groups/test_air_plonky3.rs b/air-script/tests/trace_col_groups/test_air_plonky3.rs index b1c2c7f67..225222609 100644 --- a/air-script/tests/trace_col_groups/test_air_plonky3.rs +++ b/air-script/tests/trace_col_groups/test_air_plonky3.rs @@ -13,6 +13,7 @@ use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; use crate::{ + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, trace_col_groups::trace_col_groups_plonky3::{NUM_COLUMNS, TraceColGroupAir}, }; @@ -68,38 +69,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 16]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&TraceColGroupAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 16] } + +generate_air_plonky3_test!(test_air_plonky3, TraceColGroupAir); diff --git a/air-script/tests/trace_col_groups/test_air_winterfell.rs b/air-script/tests/trace_col_groups/test_air_winterfell.rs index cb62434e9..92400a4f2 100644 --- a/air-script/tests/trace_col_groups/test_air_winterfell.rs +++ b/air-script/tests/trace_col_groups/test_air_winterfell.rs @@ -3,7 +3,7 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, trace_col_groups::trace_col_groups::PublicInputs, }; @@ -46,7 +46,7 @@ impl AirTester for TraceColGroupAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_trace_col_groups_air, crate::trace_col_groups::trace_col_groups::TraceColGroupAir, TraceColGroupAirTester, diff --git a/air-script/tests/variables/test_air_plonky3.rs b/air-script/tests/variables/test_air_plonky3.rs index e1e615220..7c01c92d5 100644 --- a/air-script/tests/variables/test_air_plonky3.rs +++ b/air-script/tests/variables/test_air_plonky3.rs @@ -13,6 +13,7 @@ use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; use crate::{ + generate_air_plonky3_test, helpers::check_constraints_with_periodic_columns, variables::variables_plonky3::{NUM_COLUMNS, VariablesAir}, }; @@ -53,38 +54,8 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -#[test] -fn test_air_plonky3() { - type Val = Mersenne31; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; - - let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); - let compress = MyCompress::new(byte_hash); - let val_mmcs = ValMmcs::new(field_hash, compress); - let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); - let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; - let config = MyConfig::new(pcs, challenger); - - let inputs = vec![1; 32]; - let inputs_m31: Vec = inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); - - let trace = generate_trace_rows::(inputs); - - check_constraints_with_periodic_columns(&VariablesAir {}, &trace, &inputs_m31); +fn generate_inputs() -> Vec { + vec![1; 32] } + +generate_air_plonky3_test!(test_air_plonky3, VariablesAir); diff --git a/air-script/tests/variables/test_air_winterfell.rs b/air-script/tests/variables/test_air_winterfell.rs index c45e18516..3578f330f 100644 --- a/air-script/tests/variables/test_air_winterfell.rs +++ b/air-script/tests/variables/test_air_winterfell.rs @@ -3,7 +3,7 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - generate_air_test, + generate_air_winterfell_test, helpers::{AirTester, MyTraceTable}, variables::variables::PublicInputs, }; @@ -40,7 +40,7 @@ impl AirTester for VariablesAirTester { } } -generate_air_test!( +generate_air_winterfell_test!( test_variables_air, crate::variables::variables::VariablesAir, VariablesAirTester, From 7b11b116ea06faf0e63302b595decc0b8b37b832 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Tue, 14 Oct 2025 11:52:25 +0200 Subject: [PATCH 16/83] docs: Add documentation for Plonky3 backend --- docs/src/backends.md | 8 +++++--- docs/src/introduction.md | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/src/backends.md b/docs/src/backends.md index ce8b023e7..1a7f883fe 100644 --- a/docs/src/backends.md +++ b/docs/src/backends.md @@ -1,14 +1,16 @@ # Backends -AirScript currently comes bundled with two backends: +AirScript currently comes bundled with three backends: - [Winterfell backend](https://github.com/0xMiden/air-script/tree/main/codegen/winterfell) which outputs `Air` trait implementation for the [Winterfell prover](https://github.com/facebook/winterfell) (Rust). +- [Plonky3 backend](https://github.com/0xMiden/air-script/tree/main/codegen/plonky3) which outputs `Air` trait implementation for the [Plonky3 prover](https://github.com/Plonky3/Plonky3) (Rust). - [ACE backend](https://github.com/0xMiden/air-script/tree/main/codegen/ace) which outputs arithmetic circuits for Miden VM's ACE (Arithmetic Circuit Evaluation) chiplet for recursive STARK proof verification. -These backends can be used programmatically as crates. +These backends can be used programmatically as crates. -The Winterfell backend can also be used via AirScript CLI by specifying `--target` flag. For example, the following will output Winterfell `Air` trait implementation for AIR constraints described in `example.air` file: +The Winterfell and Plonky3 backends can also be used via AirScript CLI by specifying `--target` flag. For example, the following will output Winterfell and Plonky3 `Air` trait implementation for AIR constraints described in `example.air` file: ```bash # Make sure to run from the project root directory ./target/release/airc transpile examples/example.air --target winterfell +./target/release/airc transpile examples/example.air --target plonky3 ``` In both cases we assumed that the CLI has been compiled as described [here](./introduction.md#cli). diff --git a/docs/src/introduction.md b/docs/src/introduction.md index b6b565286..d39ae08d3 100644 --- a/docs/src/introduction.md +++ b/docs/src/introduction.md @@ -7,6 +7,7 @@ Currently, AirScript is on version 0.3, which includes about 95% of features needed to describe Miden VM constraints, and supports generation of constraint evaluation code for the following backends: - **Winterfell**: Generates Rust code implementing the `Air` trait for the [Winterfell prover](https://github.com/facebook/winterfell) +- **Plonky3**: Generates Rust code implementing the `Air` trait implementation for the [Plonky3 prover](https://github.com/Plonky3/Plonky3) - **ACE**: Generates arithmetic circuits for Miden VM's ACE (Arithmetic Circuit Evaluation) chiplet for recursive proof verification AirScript includes the following features: @@ -57,6 +58,8 @@ Then, run the `airc` target with the `transpile` option. For example: ``` This will output constraint evaluation code targeted for the Winterfell prover. +Using the `--target plonky3` argument will output constraint evaluation code targeted for the Plonky3 prover instead. + You can use the `help` option to see other available options. ```ignore @@ -75,6 +78,5 @@ The following changes are some of the improvements under consideration for futur - removing unnecessary nodes from the `AlgebraicGraph` of boundary and integrity constraints. - combining integrity constraints with mutually exclusive selectors to reduce the total number of constraints. - additional language targets for simplifying verifier implementations: - - Plonky3 AirBuilder. - JSON-based constraint syntax. - formal verification From 4409002ec00fc9153eacc71379028fad8b771ddc Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Wed, 15 Oct 2025 17:36:24 +0200 Subject: [PATCH 17/83] tests: Add Plonky3 E2E tests for computed indices --- air-script/tests/codegen/plonky3.rs | 22 ++++++++ .../computed_indices_complex_plonky3.rs | 43 ++++++++++++++++ .../computed_indices_simple_plonky3.rs | 50 +++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 air-script/tests/computed_indices/computed_indices_complex_plonky3.rs create mode 100644 air-script/tests/computed_indices/computed_indices_simple_plonky3.rs diff --git a/air-script/tests/codegen/plonky3.rs b/air-script/tests/codegen/plonky3.rs index bb7b3a24c..aa4de2ed4 100644 --- a/air-script/tests/codegen/plonky3.rs +++ b/air-script/tests/codegen/plonky3.rs @@ -111,6 +111,28 @@ fn bitwise() { expected.assert_eq(&generated_air); } + +#[test] +fn computed_indices_complex() { + let generated_air = + Test::new("tests/computed_indices/computed_indices_complex.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../computed_indices/computed_indices_complex_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn computed_indices_simple() { + let generated_air = Test::new("tests/computed_indices/computed_indices_simple.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["../computed_indices/computed_indices_simple_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + #[test] fn constants() { let generated_air = Test::new("tests/constants/constants.air".to_string()) diff --git a/air-script/tests/computed_indices/computed_indices_complex_plonky3.rs b/air-script/tests/computed_indices/computed_indices_complex_plonky3.rs new file mode 100644 index 000000000..1ded2a430 --- /dev/null +++ b/air-script/tests/computed_indices/computed_indices_complex_plonky3.rs @@ -0,0 +1,43 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 4; + +pub const NUM_PUBLIC_VALUES: usize = 1; + +pub struct ComputedIndicesAir; + +impl BaseAir for ComputedIndicesAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for ComputedIndicesAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for ComputedIndicesAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for ComputedIndicesAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[0].into()); + builder.assert_zero::<_>(main_current[2].into() * AB::Expr::from_u64(3) + main_current[3].into() * AB::Expr::from_u64(4)); + } +} \ No newline at end of file diff --git a/air-script/tests/computed_indices/computed_indices_simple_plonky3.rs b/air-script/tests/computed_indices/computed_indices_simple_plonky3.rs new file mode 100644 index 000000000..3ac8ed33b --- /dev/null +++ b/air-script/tests/computed_indices/computed_indices_simple_plonky3.rs @@ -0,0 +1,50 @@ +use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; +use p3_matrix::Matrix; +use p3_field::PrimeCharacteristicRing; +use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; + +pub const NUM_COLUMNS: usize = 8; + +pub const NUM_PUBLIC_VALUES: usize = 16; + +pub struct ComputedIndicesAir; + +impl BaseAir for ComputedIndicesAir { + fn width(&self) -> usize { + NUM_COLUMNS + } +} + +impl BaseAirWithPublicValues for ComputedIndicesAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl BaseAirWithPeriodicColumns for ComputedIndicesAir { + fn get_periodic_columns(&self) -> Vec> { + vec![ + ] + } +} + +impl Air for ComputedIndicesAir { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values = builder.periodic_columns(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + builder.when_first_row().assert_zero::<_>(main_current[0].into()); + builder.assert_zero::<_>(main_current[0].into()); + builder.assert_zero::<_>(main_current[1].into() - AB::Expr::from_u64(2)); + builder.assert_zero::<_>(main_current[2].into() - AB::Expr::from_u64(4)); + builder.assert_zero::<_>(main_current[3].into() - AB::Expr::from_u64(6)); + builder.when_transition().assert_zero::<_>(main_next[4].into()); + builder.when_transition().assert_zero::<_>(main_next[5].into() - main_current[5].into().double()); + builder.when_transition().assert_zero::<_>(main_next[6].into() - AB::Expr::from_u64(6) * main_current[6].into()); + builder.when_transition().assert_zero::<_>(main_next[7].into() - AB::Expr::from_u64(12) * main_current[7].into()); + } +} \ No newline at end of file From d0418fed715e9073f7bbbef52bcb1313872b6ea8 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Wed, 15 Oct 2025 17:38:13 +0200 Subject: [PATCH 18/83] chore: fix fmt lint --- air-script/tests/codegen/plonky3.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/air-script/tests/codegen/plonky3.rs b/air-script/tests/codegen/plonky3.rs index aa4de2ed4..8ccbb2d55 100644 --- a/air-script/tests/codegen/plonky3.rs +++ b/air-script/tests/codegen/plonky3.rs @@ -111,7 +111,6 @@ fn bitwise() { expected.assert_eq(&generated_air); } - #[test] fn computed_indices_complex() { let generated_air = From e6aa42181d7e872f6d15e34b0e00aebed8c5213b Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Mon, 20 Oct 2025 13:04:56 +0300 Subject: [PATCH 19/83] tests(plonky3): use Goldilocks instead of Mersenne32 --- air-script/Cargo.toml | 2 +- air-script/tests/binary/test_air_plonky3.rs | 5 +++-- air-script/tests/bitwise/test_air_plonky3.rs | 5 +++-- air-script/tests/buses/test_air_plonky3.rs | 5 +++-- .../constant_in_range/test_air_plonky3.rs | 5 +++-- air-script/tests/constants/test_air_plonky3.rs | 5 +++-- .../test_air_plonky3.rs | 5 +++-- .../tests/evaluators/test_air_plonky3.rs | 5 +++-- air-script/tests/fibonacci/test_air_plonky3.rs | 5 +++-- air-script/tests/functions/test_air_plonky3.rs | 6 ++++-- air-script/tests/helpers/macros.rs | 18 +++++++++--------- .../indexed_trace_access/test_air_plonky3.rs | 5 +++-- .../list_comprehension/test_air_plonky3.rs | 6 ++++-- .../tests/list_folding/test_air_plonky3.rs | 5 +++-- .../tests/periodic_columns/test_air_plonky3.rs | 5 +++-- .../tests/pub_inputs/test_air_plonky3.rs | 5 +++-- air-script/tests/selectors/test_air_plonky3.rs | 5 +++-- air-script/tests/system/test_air_plonky3.rs | 5 +++-- .../tests/trace_col_groups/test_air_plonky3.rs | 6 ++++-- air-script/tests/variables/test_air_plonky3.rs | 6 ++++-- 20 files changed, 68 insertions(+), 46 deletions(-) diff --git a/air-script/Cargo.toml b/air-script/Cargo.toml index 883e5b61a..50ec896f2 100644 --- a/air-script/Cargo.toml +++ b/air-script/Cargo.toml @@ -38,7 +38,7 @@ p3-field = { package = "p3-field", version = "0.3", default-features = false } p3-fri = { package = "p3-fri", version = "0.3", default-features = false } p3-matrix = { package = "p3-matrix", version = "0.3", default-features = false } p3-merkle-tree = { package = "p3-merkle-tree", version = "0.3", default-features = false } -p3-mersenne-31 = { package = "p3-mersenne-31", version = "0.3", default-features = false } +p3-goldilocks = { package = "p3-goldilocks", version = "0.3", default-features = false } p3-sha256 = { package = "p3-sha256", version = "0.3", default-features = false } p3-symmetric = { package = "p3-symmetric", version = "0.3", default-features = false } p3-uni-stark = { package = "p3-uni-stark", version = "0.3.0", default-features = false } diff --git a/air-script/tests/binary/test_air_plonky3.rs b/air-script/tests/binary/test_air_plonky3.rs index ac4e20894..9225aa013 100644 --- a/air-script/tests/binary/test_air_plonky3.rs +++ b/air-script/tests/binary/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -51,5 +51,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, BinaryAir); diff --git a/air-script/tests/bitwise/test_air_plonky3.rs b/air-script/tests/bitwise/test_air_plonky3.rs index 1bb3a9f89..da5fcb341 100644 --- a/air-script/tests/bitwise/test_air_plonky3.rs +++ b/air-script/tests/bitwise/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -89,5 +89,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, BitwiseAir); diff --git a/air-script/tests/buses/test_air_plonky3.rs b/air-script/tests/buses/test_air_plonky3.rs index a6671ba82..080db385e 100644 --- a/air-script/tests/buses/test_air_plonky3.rs +++ b/air-script/tests/buses/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -51,5 +51,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 2] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, BusesAir); diff --git a/air-script/tests/constant_in_range/test_air_plonky3.rs b/air-script/tests/constant_in_range/test_air_plonky3.rs index 2518eaf03..121a97581 100644 --- a/air-script/tests/constant_in_range/test_air_plonky3.rs +++ b/air-script/tests/constant_in_range/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -70,5 +70,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, ConstantInRangeAir); diff --git a/air-script/tests/constants/test_air_plonky3.rs b/air-script/tests/constants/test_air_plonky3.rs index 04c4b3e16..1f6974644 100644 --- a/air-script/tests/constants/test_air_plonky3.rs +++ b/air-script/tests/constants/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -66,5 +66,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 32] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, ConstantsAir); diff --git a/air-script/tests/constraint_comprehension/test_air_plonky3.rs b/air-script/tests/constraint_comprehension/test_air_plonky3.rs index 593107425..23f6e3fa5 100644 --- a/air-script/tests/constraint_comprehension/test_air_plonky3.rs +++ b/air-script/tests/constraint_comprehension/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -53,5 +53,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, ConstraintComprehensionAir); diff --git a/air-script/tests/evaluators/test_air_plonky3.rs b/air-script/tests/evaluators/test_air_plonky3.rs index 76e8ffc82..b65d85753 100644 --- a/air-script/tests/evaluators/test_air_plonky3.rs +++ b/air-script/tests/evaluators/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -66,5 +66,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, EvaluatorsAir); diff --git a/air-script/tests/fibonacci/test_air_plonky3.rs b/air-script/tests/fibonacci/test_air_plonky3.rs index 10e671df8..4ad375779 100644 --- a/air-script/tests/fibonacci/test_air_plonky3.rs +++ b/air-script/tests/fibonacci/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -53,5 +53,6 @@ fn generate_inputs() -> Vec { let last = 2178309; // 32nd Fibonacci number vec![one, one, last] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, FibonacciAir); diff --git a/air-script/tests/functions/test_air_plonky3.rs b/air-script/tests/functions/test_air_plonky3.rs index d9a590894..0388183e2 100644 --- a/air-script/tests/functions/test_air_plonky3.rs +++ b/air-script/tests/functions/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -97,4 +97,6 @@ fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; + generate_air_plonky3_test!(test_air_plonky3, FunctionsAir); diff --git a/air-script/tests/helpers/macros.rs b/air-script/tests/helpers/macros.rs index 3b4ebad6e..6f9586249 100644 --- a/air-script/tests/helpers/macros.rs +++ b/air-script/tests/helpers/macros.rs @@ -40,7 +40,7 @@ macro_rules! generate_air_plonky3_test { ($test_name:ident, $air_name:ident) => { #[test] fn $test_name() { - type Val = Mersenne31; + type Val = Goldilocks; type Challenge = BinomialExtensionField; type ByteHash = Sha256; @@ -48,7 +48,7 @@ macro_rules! generate_air_plonky3_test { type MyCompress = CompressionFunctionFromHasher; type ValMmcs = MerkleTreeMmcs; type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger32>; + type Challenger = SerializingChallenger64>; type Pcs = CirclePcs; type MyConfig = StarkConfig; @@ -67,18 +67,18 @@ macro_rules! generate_air_plonky3_test { let config = MyConfig::new(pcs, challenger); let inputs = generate_inputs(); - let inputs_m31: Vec = - inputs.iter().map(|&x| Val::new_checked(x).unwrap()).collect(); + let inputs_goldilocks: Vec = + inputs.iter().map(|&x| Val::from_u32(x)).collect(); let trace = generate_trace_rows::(inputs); - check_constraints_with_periodic_columns(&$air_name {}, &trace, &inputs_m31); + check_constraints_with_periodic_columns(&$air_name {}, &trace, &inputs_goldilocks); - /*let prove_with_periodic_columns = prove_with_periodic_columns(&config, &BitwiseAir {}, trace, &inputs_m31); - verify_with_periodic_columns(&config, &BitwiseAir {}, &prove_with_periodic_columns, &inputs_m31).expect("Verification failed");*/ + /*let prove_with_periodic_columns = prove_with_periodic_columns(&config, &BitwiseAir {}, trace, &inputs_goldilocks); + verify_with_periodic_columns(&config, &BitwiseAir {}, &prove_with_periodic_columns, &inputs_goldilocks).expect("Verification failed");*/ - /*let proof = prove(&config, &BitwiseAir {}, trace, &inputs_m31); - verify(&config, &BitwiseAir {}, &proof, &inputs_m31).expect("Verification failed");*/ + /*let proof = prove(&config, &BitwiseAir {}, trace, &inputs_goldilocks); + verify(&config, &BitwiseAir {}, &proof, &inputs_goldilocks).expect("Verification failed");*/ } }; } diff --git a/air-script/tests/indexed_trace_access/test_air_plonky3.rs b/air-script/tests/indexed_trace_access/test_air_plonky3.rs index cef3b72e8..bc1a1f736 100644 --- a/air-script/tests/indexed_trace_access/test_air_plonky3.rs +++ b/air-script/tests/indexed_trace_access/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -53,5 +53,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, TraceAccessAir); diff --git a/air-script/tests/list_comprehension/test_air_plonky3.rs b/air-script/tests/list_comprehension/test_air_plonky3.rs index 70277f2c3..3779ba640 100644 --- a/air-script/tests/list_comprehension/test_air_plonky3.rs +++ b/air-script/tests/list_comprehension/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -93,4 +93,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; + generate_air_plonky3_test!(test_air_plonky3, ListComprehensionAir); diff --git a/air-script/tests/list_folding/test_air_plonky3.rs b/air-script/tests/list_folding/test_air_plonky3.rs index 45871c969..106918f4b 100644 --- a/air-script/tests/list_folding/test_air_plonky3.rs +++ b/air-script/tests/list_folding/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -96,5 +96,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, ListFoldingAir); diff --git a/air-script/tests/periodic_columns/test_air_plonky3.rs b/air-script/tests/periodic_columns/test_air_plonky3.rs index 589fab8e8..7bbd3ed5b 100644 --- a/air-script/tests/periodic_columns/test_air_plonky3.rs +++ b/air-script/tests/periodic_columns/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -54,5 +54,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, PeriodicColumnsAir); diff --git a/air-script/tests/pub_inputs/test_air_plonky3.rs b/air-script/tests/pub_inputs/test_air_plonky3.rs index a29b06143..a68427cc2 100644 --- a/air-script/tests/pub_inputs/test_air_plonky3.rs +++ b/air-script/tests/pub_inputs/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -57,5 +57,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![0; 32] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, PubInputsAir); diff --git a/air-script/tests/selectors/test_air_plonky3.rs b/air-script/tests/selectors/test_air_plonky3.rs index ad757c2fb..1f717bac9 100644 --- a/air-script/tests/selectors/test_air_plonky3.rs +++ b/air-script/tests/selectors/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -57,5 +57,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, SelectorsAir); diff --git a/air-script/tests/system/test_air_plonky3.rs b/air-script/tests/system/test_air_plonky3.rs index 93f0d769e..63553c12f 100644 --- a/air-script/tests/system/test_air_plonky3.rs +++ b/air-script/tests/system/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -54,5 +54,6 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, SystemAir); diff --git a/air-script/tests/trace_col_groups/test_air_plonky3.rs b/air-script/tests/trace_col_groups/test_air_plonky3.rs index 225222609..16bf6d313 100644 --- a/air-script/tests/trace_col_groups/test_air_plonky3.rs +++ b/air-script/tests/trace_col_groups/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -73,4 +73,6 @@ fn generate_inputs() -> Vec { vec![1; 16] } +use p3_field::PrimeCharacteristicRing; + generate_air_plonky3_test!(test_air_plonky3, TraceColGroupAir); diff --git a/air-script/tests/variables/test_air_plonky3.rs b/air-script/tests/variables/test_air_plonky3.rs index 7c01c92d5..566d919b7 100644 --- a/air-script/tests/variables/test_air_plonky3.rs +++ b/air-script/tests/variables/test_air_plonky3.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; -use p3_challenger::{HashChallenger, SerializingChallenger32}; +use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; use p3_field::{PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; +use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; use p3_merkle_tree::MerkleTreeMmcs; -use p3_mersenne_31::Mersenne31; use p3_sha256::Sha256; use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; use p3_uni_stark::StarkConfig; @@ -58,4 +58,6 @@ fn generate_inputs() -> Vec { vec![1; 32] } +use p3_field::PrimeCharacteristicRing; + generate_air_plonky3_test!(test_air_plonky3, VariablesAir); From 1ec834bb07d99918229c964aa5bef100bf86f72b Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Mon, 20 Oct 2025 13:13:45 +0300 Subject: [PATCH 20/83] chore: cleanup import --- air-script/tests/binary/test_air_plonky3.rs | 3 +-- air-script/tests/bitwise/test_air_plonky3.rs | 3 +-- air-script/tests/buses/test_air_plonky3.rs | 3 +-- air-script/tests/constant_in_range/test_air_plonky3.rs | 3 +-- air-script/tests/constants/test_air_plonky3.rs | 3 +-- air-script/tests/constraint_comprehension/test_air_plonky3.rs | 3 +-- air-script/tests/evaluators/test_air_plonky3.rs | 3 +-- air-script/tests/fibonacci/test_air_plonky3.rs | 3 +-- air-script/tests/functions/test_air_plonky3.rs | 4 +--- air-script/tests/indexed_trace_access/test_air_plonky3.rs | 3 +-- air-script/tests/list_comprehension/test_air_plonky3.rs | 3 +-- air-script/tests/list_folding/test_air_plonky3.rs | 3 +-- air-script/tests/periodic_columns/test_air_plonky3.rs | 3 +-- air-script/tests/pub_inputs/test_air_plonky3.rs | 3 +-- air-script/tests/selectors/test_air_plonky3.rs | 3 +-- air-script/tests/system/test_air_plonky3.rs | 3 +-- air-script/tests/trace_col_groups/test_air_plonky3.rs | 4 +--- air-script/tests/variables/test_air_plonky3.rs | 4 +--- 18 files changed, 18 insertions(+), 39 deletions(-) diff --git a/air-script/tests/binary/test_air_plonky3.rs b/air-script/tests/binary/test_air_plonky3.rs index 9225aa013..cb06fb5f4 100644 --- a/air-script/tests/binary/test_air_plonky3.rs +++ b/air-script/tests/binary/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -51,6 +51,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, BinaryAir); diff --git a/air-script/tests/bitwise/test_air_plonky3.rs b/air-script/tests/bitwise/test_air_plonky3.rs index da5fcb341..d3c6efcaa 100644 --- a/air-script/tests/bitwise/test_air_plonky3.rs +++ b/air-script/tests/bitwise/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -89,6 +89,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, BitwiseAir); diff --git a/air-script/tests/buses/test_air_plonky3.rs b/air-script/tests/buses/test_air_plonky3.rs index 080db385e..1b323208b 100644 --- a/air-script/tests/buses/test_air_plonky3.rs +++ b/air-script/tests/buses/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -51,6 +51,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 2] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, BusesAir); diff --git a/air-script/tests/constant_in_range/test_air_plonky3.rs b/air-script/tests/constant_in_range/test_air_plonky3.rs index 121a97581..56e1dcb11 100644 --- a/air-script/tests/constant_in_range/test_air_plonky3.rs +++ b/air-script/tests/constant_in_range/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -70,6 +70,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, ConstantInRangeAir); diff --git a/air-script/tests/constants/test_air_plonky3.rs b/air-script/tests/constants/test_air_plonky3.rs index 1f6974644..6eecb26fa 100644 --- a/air-script/tests/constants/test_air_plonky3.rs +++ b/air-script/tests/constants/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -66,6 +66,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 32] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, ConstantsAir); diff --git a/air-script/tests/constraint_comprehension/test_air_plonky3.rs b/air-script/tests/constraint_comprehension/test_air_plonky3.rs index 23f6e3fa5..ceab24925 100644 --- a/air-script/tests/constraint_comprehension/test_air_plonky3.rs +++ b/air-script/tests/constraint_comprehension/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -53,6 +53,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, ConstraintComprehensionAir); diff --git a/air-script/tests/evaluators/test_air_plonky3.rs b/air-script/tests/evaluators/test_air_plonky3.rs index b65d85753..7fed6ebc2 100644 --- a/air-script/tests/evaluators/test_air_plonky3.rs +++ b/air-script/tests/evaluators/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -66,6 +66,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, EvaluatorsAir); diff --git a/air-script/tests/fibonacci/test_air_plonky3.rs b/air-script/tests/fibonacci/test_air_plonky3.rs index 4ad375779..6a01a91b6 100644 --- a/air-script/tests/fibonacci/test_air_plonky3.rs +++ b/air-script/tests/fibonacci/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -53,6 +53,5 @@ fn generate_inputs() -> Vec { let last = 2178309; // 32nd Fibonacci number vec![one, one, last] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, FibonacciAir); diff --git a/air-script/tests/functions/test_air_plonky3.rs b/air-script/tests/functions/test_air_plonky3.rs index 0388183e2..5ef13dbf9 100644 --- a/air-script/tests/functions/test_air_plonky3.rs +++ b/air-script/tests/functions/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -97,6 +97,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; - generate_air_plonky3_test!(test_air_plonky3, FunctionsAir); diff --git a/air-script/tests/indexed_trace_access/test_air_plonky3.rs b/air-script/tests/indexed_trace_access/test_air_plonky3.rs index bc1a1f736..b01cc0eaa 100644 --- a/air-script/tests/indexed_trace_access/test_air_plonky3.rs +++ b/air-script/tests/indexed_trace_access/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -53,6 +53,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, TraceAccessAir); diff --git a/air-script/tests/list_comprehension/test_air_plonky3.rs b/air-script/tests/list_comprehension/test_air_plonky3.rs index 3779ba640..7cec4aeed 100644 --- a/air-script/tests/list_comprehension/test_air_plonky3.rs +++ b/air-script/tests/list_comprehension/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -93,6 +93,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, ListComprehensionAir); diff --git a/air-script/tests/list_folding/test_air_plonky3.rs b/air-script/tests/list_folding/test_air_plonky3.rs index 106918f4b..83d82e10d 100644 --- a/air-script/tests/list_folding/test_air_plonky3.rs +++ b/air-script/tests/list_folding/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -96,6 +96,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, ListFoldingAir); diff --git a/air-script/tests/periodic_columns/test_air_plonky3.rs b/air-script/tests/periodic_columns/test_air_plonky3.rs index 7bbd3ed5b..6b7b9acce 100644 --- a/air-script/tests/periodic_columns/test_air_plonky3.rs +++ b/air-script/tests/periodic_columns/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -54,6 +54,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, PeriodicColumnsAir); diff --git a/air-script/tests/pub_inputs/test_air_plonky3.rs b/air-script/tests/pub_inputs/test_air_plonky3.rs index a68427cc2..71343872e 100644 --- a/air-script/tests/pub_inputs/test_air_plonky3.rs +++ b/air-script/tests/pub_inputs/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -57,6 +57,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![0; 32] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, PubInputsAir); diff --git a/air-script/tests/selectors/test_air_plonky3.rs b/air-script/tests/selectors/test_air_plonky3.rs index 1f717bac9..289939f0e 100644 --- a/air-script/tests/selectors/test_air_plonky3.rs +++ b/air-script/tests/selectors/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -57,6 +57,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, SelectorsAir); diff --git a/air-script/tests/system/test_air_plonky3.rs b/air-script/tests/system/test_air_plonky3.rs index 63553c12f..9c0eb5004 100644 --- a/air-script/tests/system/test_air_plonky3.rs +++ b/air-script/tests/system/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -54,6 +54,5 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; generate_air_plonky3_test!(test_air_plonky3, SystemAir); diff --git a/air-script/tests/trace_col_groups/test_air_plonky3.rs b/air-script/tests/trace_col_groups/test_air_plonky3.rs index 16bf6d313..b12ab3ac7 100644 --- a/air-script/tests/trace_col_groups/test_air_plonky3.rs +++ b/air-script/tests/trace_col_groups/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -73,6 +73,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -use p3_field::PrimeCharacteristicRing; - generate_air_plonky3_test!(test_air_plonky3, TraceColGroupAir); diff --git a/air-script/tests/variables/test_air_plonky3.rs b/air-script/tests/variables/test_air_plonky3.rs index 566d919b7..8296f2197 100644 --- a/air-script/tests/variables/test_air_plonky3.rs +++ b/air-script/tests/variables/test_air_plonky3.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use p3_challenger::{HashChallenger, SerializingChallenger64}; use p3_circle::CirclePcs; use p3_commit::ExtensionMmcs; -use p3_field::{PrimeField64, extension::BinomialExtensionField}; +use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; use p3_fri::create_benchmark_fri_params; use p3_goldilocks::Goldilocks; use p3_matrix::dense::RowMajorMatrix; @@ -58,6 +58,4 @@ fn generate_inputs() -> Vec { vec![1; 32] } -use p3_field::PrimeCharacteristicRing; - generate_air_plonky3_test!(test_air_plonky3, VariablesAir); From b3a441e51b56cdf1e258f2540fed177ab47c4708 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Thu, 4 Dec 2025 16:10:34 +0100 Subject: [PATCH 21/83] Target 0xMiden Plonky3 repo and use AirScriptAir and AirScriptBuilder traits (#508) --- .github/workflows/book.yml | 2 +- CHANGELOG.md | 2 + air-script/Cargo.toml | 32 +- air-script/src/lib.rs | 5 + .../test_utils/air_tester_macros.rs} | 56 +-- .../helpers.rs => src/test_utils/codegen.rs} | 3 +- .../miden_vm_aux_trace_generator.rs | 116 ++++++ air-script/src/test_utils/mod.rs | 10 + air-script/src/test_utils/plonky3_traits.rs | 189 ++++++++++ .../test_utils/winterfell_traits.rs} | 0 air-script/{ => src}/tests/binary/binary.air | 0 air-script/{ => src}/tests/binary/binary.rs | 0 air-script/src/tests/binary/binary_plonky3.rs | 44 +++ air-script/{ => src}/tests/binary/mod.rs | 0 .../tests/binary/test_air_plonky3.rs | 29 +- .../tests/binary/test_air_winterfell.rs | 6 +- .../{ => src}/tests/bitwise/bitwise.air | 0 air-script/{ => src}/tests/bitwise/bitwise.rs | 0 .../src/tests/bitwise/bitwise_plonky3.rs | 73 ++++ air-script/{ => src}/tests/bitwise/mod.rs | 1 + .../tests/bitwise/test_air_plonky3.rs | 29 +- .../tests/bitwise/test_air_winterfell.rs | 6 +- .../{ => src}/tests/buses/buses_complex.air | 8 +- .../{ => src}/tests/buses/buses_complex.rs | 2 +- .../src/tests/buses/buses_complex_plonky3.rs | 138 +++++++ .../{ => src}/tests/buses/buses_simple.air | 0 .../{ => src}/tests/buses/buses_simple.rs | 0 .../src/tests/buses/buses_simple_plonky3.rs | 133 +++++++ .../buses/buses_simple_with_evaluators.air | 0 .../buses/buses_simple_with_evaluators.rs | 100 ++++++ .../buses_simple_with_evaluators_plonky3.rs | 108 ++++++ .../buses/buses_varlen_boundary_both.air | 0 .../tests/buses/buses_varlen_boundary_both.rs | 0 .../buses_varlen_boundary_both_plonky3.rs | 134 +++++++ .../buses/buses_varlen_boundary_first.air | 0 .../buses/buses_varlen_boundary_first.rs | 0 .../buses_varlen_boundary_first_plonky3.rs | 135 +++++++ .../buses/buses_varlen_boundary_last.air | 0 .../tests/buses/buses_varlen_boundary_last.rs | 0 .../buses_varlen_boundary_last_plonky3.rs | 135 +++++++ air-script/{ => src}/tests/buses/mod.rs | 0 .../src/tests/buses/test_air_plonky3.rs | 59 +++ .../tests/buses/test_air_winterfell.rs | 12 +- .../computed_indices_complex.air | 0 .../computed_indices_complex.rs | 0 .../computed_indices_complex_plonky3.rs | 43 +++ .../computed_indices_simple.air | 0 .../computed_indices_simple.rs | 0 .../computed_indices_simple_plonky3.rs | 50 +++ air-script/src/tests/computed_indices/mod.rs | 18 + .../computed_indices/test_air_plonky3.rs | 62 ++++ .../computed_indices/test_air_winterfell.rs} | 25 +- .../constant_in_range/constant_in_range.air | 0 .../constant_in_range/constant_in_range.rs | 0 .../constant_in_range_module.air | 0 .../constant_in_range_plonky3.rs | 43 +++ .../{ => src}/tests/constant_in_range/mod.rs | 0 .../constant_in_range/test_air_plonky3.rs | 29 +- .../constant_in_range/test_air_winterfell.rs | 6 +- .../{ => src}/tests/constants/constants.air | 0 .../{ => src}/tests/constants/constants.rs | 0 .../src/tests/constants/constants_plonky3.rs | 52 +++ air-script/{ => src}/tests/constants/mod.rs | 0 .../tests/constants/test_air_plonky3.rs | 29 +- .../tests/constants/test_air_winterfell.rs | 6 +- .../cc_with_evaluators.air | 0 .../constraint_comprehension.air | 0 .../constraint_comprehension.rs | 0 .../constraint_comprehension_plonky3.rs | 46 +++ .../tests/constraint_comprehension/mod.rs | 0 .../test_air_plonky3.rs | 46 +++ .../test_air_winterfell.rs | 6 +- air-script/{ => src}/tests/docs_sync.rs | 0 .../{ => src}/tests/evaluators/evaluators.air | 0 .../{ => src}/tests/evaluators/evaluators.rs | 0 .../tests/evaluators/evaluators_plonky3.rs | 52 +++ air-script/{ => src}/tests/evaluators/mod.rs | 0 .../tests/evaluators/test_air_plonky3.rs | 29 +- .../tests/evaluators/test_air_winterfell.rs | 6 +- .../{ => src}/tests/fibonacci/fibonacci.air | 0 .../{ => src}/tests/fibonacci/fibonacci.rs | 0 .../src/tests/fibonacci/fibonacci_plonky3.rs | 46 +++ air-script/{ => src}/tests/fibonacci/mod.rs | 0 .../tests/fibonacci/test_air_plonky3.rs | 29 +- .../tests/fibonacci/test_air_winterfell.rs | 6 +- .../tests/functions/functions_complex.air | 0 .../tests/functions/functions_complex.rs | 0 .../functions/functions_complex_plonky3.rs | 44 +++ .../tests/functions/functions_simple.air | 0 .../tests/functions/functions_simple.rs | 0 .../functions/functions_simple_plonky3.rs | 50 +++ .../functions/inlined_functions_simple.air | 0 air-script/{ => src}/tests/functions/mod.rs | 0 .../tests/functions/test_air_plonky3.rs | 29 +- .../tests/functions/test_air_winterfell.rs | 6 +- .../indexed_trace_access.air | 0 .../indexed_trace_access.rs | 0 .../indexed_trace_access_plonky3.rs | 43 +++ .../tests/indexed_trace_access/mod.rs | 0 .../indexed_trace_access/test_air_plonky3.rs | 29 +- .../test_air_winterfell.rs | 6 +- .../list_comprehension/list_comprehension.air | 0 .../list_comprehension/list_comprehension.rs | 0 .../list_comprehension_nested.air | 0 .../list_comprehension_nested.rs | 0 .../list_comprehension_nested_plonky3.rs | 45 +++ .../list_comprehension_plonky3.rs | 48 +++ .../{ => src}/tests/list_comprehension/mod.rs | 7 + .../list_comprehension/test_air_plonky3.rs | 29 +- .../list_comprehension/test_air_winterfell.rs | 6 +- .../tests/list_folding/list_folding.air | 0 .../tests/list_folding/list_folding.rs | 0 .../list_folding/list_folding_plonky3.rs | 46 +++ .../{ => src}/tests/list_folding/mod.rs | 0 .../tests/list_folding/test_air_plonky3.rs | 29 +- .../tests/list_folding/test_air_winterfell.rs | 6 +- air-script/{ => src}/tests/mod.rs | 6 +- .../{ => src}/tests/periodic_columns/mod.rs | 0 .../periodic_columns/periodic_columns.air | 0 .../periodic_columns/periodic_columns.rs | 0 .../periodic_columns_plonky3.rs | 58 +++ .../periodic_columns/test_air_plonky3.rs | 29 +- .../periodic_columns/test_air_winterfell.rs | 6 +- air-script/src/tests/plonky3.rs | 336 +++++++++++++++++ air-script/{ => src}/tests/pub_inputs/mod.rs | 0 .../{ => src}/tests/pub_inputs/pub_inputs.air | 0 .../{ => src}/tests/pub_inputs/pub_inputs.rs | 0 .../tests/pub_inputs/pub_inputs_plonky3.rs | 50 +++ .../tests/pub_inputs/test_air_plonky3.rs | 29 +- .../tests/pub_inputs/test_air_winterfell.rs | 6 +- air-script/{ => src}/tests/selectors/mod.rs | 4 + .../{ => src}/tests/selectors/selectors.air | 0 .../{ => src}/tests/selectors/selectors.rs | 0 .../selectors/selectors_combine_complex.air | 0 .../selectors/selectors_combine_complex.rs | 0 .../selectors_combine_complex_plonky3.rs | 134 +++++++ .../selectors/selectors_combine_simple.air | 0 .../selectors/selectors_combine_simple.rs | 0 .../selectors_combine_simple_plonky3.rs | 44 +++ ...ctors_combine_with_list_comprehensions.air | 0 ...ectors_combine_with_list_comprehensions.rs | 0 ...ombine_with_list_comprehensions_plonky3.rs | 45 +++ .../src/tests/selectors/selectors_plonky3.rs | 44 +++ .../selectors/selectors_with_evaluators.air | 0 .../selectors/selectors_with_evaluators.rs | 0 .../selectors_with_evaluators_plonky3.rs | 44 +++ .../tests/selectors/test_air_plonky3.rs | 29 +- .../tests/selectors/test_air_winterfell.rs | 6 +- air-script/{ => src}/tests/system/mod.rs | 0 air-script/{ => src}/tests/system/system.air | 0 air-script/{ => src}/tests/system/system.rs | 0 air-script/src/tests/system/system_plonky3.rs | 43 +++ .../tests/system/test_air_plonky3.rs | 29 +- .../tests/system/test_air_winterfell.rs | 6 +- .../{ => src}/tests/trace_col_groups/mod.rs | 0 .../trace_col_groups/test_air_plonky3.rs | 29 +- .../trace_col_groups/test_air_winterfell.rs | 6 +- .../trace_col_groups/trace_col_groups.air | 0 .../trace_col_groups/trace_col_groups.rs | 0 .../trace_col_groups_plonky3.rs | 44 +++ air-script/{ => src}/tests/variables/mod.rs | 0 .../tests/variables/test_air_plonky3.rs | 29 +- .../tests/variables/test_air_winterfell.rs | 6 +- .../{ => src}/tests/variables/variables.air | 0 .../{ => src}/tests/variables/variables.rs | 0 .../src/tests/variables/variables_plonky3.rs | 60 ++++ air-script/src/tests/winterfell.rs | 337 ++++++++++++++++++ air-script/tests/binary/binary_plonky3.rs | 44 --- air-script/tests/bitwise/bitwise_plonky3.rs | 61 ---- .../tests/buses/buses_complex_plonky3.rs | 44 --- .../tests/buses/buses_simple_plonky3.rs | 41 --- .../buses_varlen_boundary_both_plonky3.rs | 41 --- .../buses_varlen_boundary_first_plonky3.rs | 41 --- .../buses_varlen_boundary_last_plonky3.rs | 41 --- air-script/tests/buses/test_air_plonky3.rs | 55 --- air-script/tests/codegen/mod.rs | 3 - air-script/tests/codegen/plonky3.rs | 323 ----------------- air-script/tests/codegen/winterfell.rs | 335 ----------------- .../computed_indices_complex_plonky3.rs | 43 --- .../computed_indices_simple_plonky3.rs | 50 --- air-script/tests/computed_indices/mod.rs | 7 - .../constant_in_range_plonky3.rs | 43 --- .../tests/constants/constants_plonky3.rs | 52 --- .../constraint_comprehension_plonky3.rs | 46 --- .../test_air_plonky3.rs | 57 --- .../tests/evaluators/evaluators_plonky3.rs | 52 --- .../tests/fibonacci/fibonacci_plonky3.rs | 46 --- .../functions/functions_complex_plonky3.rs | 44 --- .../functions/functions_simple_plonky3.rs | 50 --- air-script/tests/helpers/mod.rs | 6 - .../tests/helpers/plonky3_periodic_columns.rs | 146 -------- .../indexed_trace_access_plonky3.rs | 43 --- .../list_comprehension_nested_plonky3.rs | 45 --- .../list_comprehension_plonky3.rs | 48 --- .../list_folding/list_folding_plonky3.rs | 46 --- .../periodic_columns_plonky3.rs | 46 --- .../tests/pub_inputs/pub_inputs_plonky3.rs | 50 --- .../selectors_combine_complex_plonky3.rs | 45 --- .../selectors_combine_simple_plonky3.rs | 44 --- ...ombine_with_list_comprehensions_plonky3.rs | 45 --- .../tests/selectors/selectors_plonky3.rs | 44 --- .../selectors_with_evaluators_plonky3.rs | 44 --- air-script/tests/system/system_plonky3.rs | 43 --- .../trace_col_groups_plonky3.rs | 44 --- .../tests/variables/variables_plonky3.rs | 48 --- air/src/ir/mod.rs | 16 +- .../common_subexpression_elimination.rs | 22 ++ air/src/passes/expand_buses.rs | 47 ++- .../plonky3/src/air/boundary_constraints.rs | 30 +- codegen/plonky3/src/air/graph.rs | 176 +++++++-- .../plonky3/src/air/integrity_constraints.rs | 26 +- codegen/plonky3/src/air/mod.rs | 268 +++++++++++--- codegen/plonky3/src/imports.rs | 16 +- scripts/generate_all_e2e_tests.sh | 72 ++++ 214 files changed, 4180 insertions(+), 2877 deletions(-) rename air-script/{tests/helpers/macros.rs => src/test_utils/air_tester_macros.rs} (57%) rename air-script/{tests/codegen/helpers.rs => src/test_utils/codegen.rs} (99%) create mode 100644 air-script/src/test_utils/miden_vm_aux_trace_generator.rs create mode 100644 air-script/src/test_utils/mod.rs create mode 100644 air-script/src/test_utils/plonky3_traits.rs rename air-script/{tests/helpers/winterfell_test_helpers.rs => src/test_utils/winterfell_traits.rs} (100%) rename air-script/{ => src}/tests/binary/binary.air (100%) rename air-script/{ => src}/tests/binary/binary.rs (100%) create mode 100644 air-script/src/tests/binary/binary_plonky3.rs rename air-script/{ => src}/tests/binary/mod.rs (100%) rename air-script/{ => src}/tests/binary/test_air_plonky3.rs (51%) rename air-script/{ => src}/tests/binary/test_air_winterfell.rs (87%) rename air-script/{ => src}/tests/bitwise/bitwise.air (100%) rename air-script/{ => src}/tests/bitwise/bitwise.rs (100%) create mode 100644 air-script/src/tests/bitwise/bitwise_plonky3.rs rename air-script/{ => src}/tests/bitwise/mod.rs (86%) rename air-script/{ => src}/tests/bitwise/test_air_plonky3.rs (70%) rename air-script/{ => src}/tests/bitwise/test_air_winterfell.rs (90%) rename air-script/{ => src}/tests/buses/buses_complex.air (80%) rename air-script/{ => src}/tests/buses/buses_complex.rs (93%) create mode 100644 air-script/src/tests/buses/buses_complex_plonky3.rs rename air-script/{ => src}/tests/buses/buses_simple.air (100%) rename air-script/{ => src}/tests/buses/buses_simple.rs (100%) create mode 100644 air-script/src/tests/buses/buses_simple_plonky3.rs rename air-script/{ => src}/tests/buses/buses_simple_with_evaluators.air (100%) create mode 100644 air-script/src/tests/buses/buses_simple_with_evaluators.rs create mode 100644 air-script/src/tests/buses/buses_simple_with_evaluators_plonky3.rs rename air-script/{ => src}/tests/buses/buses_varlen_boundary_both.air (100%) rename air-script/{ => src}/tests/buses/buses_varlen_boundary_both.rs (100%) create mode 100644 air-script/src/tests/buses/buses_varlen_boundary_both_plonky3.rs rename air-script/{ => src}/tests/buses/buses_varlen_boundary_first.air (100%) rename air-script/{ => src}/tests/buses/buses_varlen_boundary_first.rs (100%) create mode 100644 air-script/src/tests/buses/buses_varlen_boundary_first_plonky3.rs rename air-script/{ => src}/tests/buses/buses_varlen_boundary_last.air (100%) rename air-script/{ => src}/tests/buses/buses_varlen_boundary_last.rs (100%) create mode 100644 air-script/src/tests/buses/buses_varlen_boundary_last_plonky3.rs rename air-script/{ => src}/tests/buses/mod.rs (100%) create mode 100644 air-script/src/tests/buses/test_air_plonky3.rs rename air-script/{ => src}/tests/buses/test_air_winterfell.rs (84%) rename air-script/{ => src}/tests/computed_indices/computed_indices_complex.air (100%) rename air-script/{ => src}/tests/computed_indices/computed_indices_complex.rs (100%) create mode 100644 air-script/src/tests/computed_indices/computed_indices_complex_plonky3.rs rename air-script/{ => src}/tests/computed_indices/computed_indices_simple.air (100%) rename air-script/{ => src}/tests/computed_indices/computed_indices_simple.rs (100%) create mode 100644 air-script/src/tests/computed_indices/computed_indices_simple_plonky3.rs create mode 100644 air-script/src/tests/computed_indices/mod.rs create mode 100644 air-script/src/tests/computed_indices/test_air_plonky3.rs rename air-script/{tests/computed_indices/test_air.rs => src/tests/computed_indices/test_air_winterfell.rs} (63%) rename air-script/{ => src}/tests/constant_in_range/constant_in_range.air (100%) rename air-script/{ => src}/tests/constant_in_range/constant_in_range.rs (100%) rename air-script/{ => src}/tests/constant_in_range/constant_in_range_module.air (100%) create mode 100644 air-script/src/tests/constant_in_range/constant_in_range_plonky3.rs rename air-script/{ => src}/tests/constant_in_range/mod.rs (100%) rename air-script/{ => src}/tests/constant_in_range/test_air_plonky3.rs (64%) rename air-script/{ => src}/tests/constant_in_range/test_air_winterfell.rs (86%) rename air-script/{ => src}/tests/constants/constants.air (100%) rename air-script/{ => src}/tests/constants/constants.rs (100%) create mode 100644 air-script/src/tests/constants/constants_plonky3.rs rename air-script/{ => src}/tests/constants/mod.rs (100%) rename air-script/{ => src}/tests/constants/test_air_plonky3.rs (61%) rename air-script/{ => src}/tests/constants/test_air_winterfell.rs (88%) rename air-script/{ => src}/tests/constraint_comprehension/cc_with_evaluators.air (100%) rename air-script/{ => src}/tests/constraint_comprehension/constraint_comprehension.air (100%) rename air-script/{ => src}/tests/constraint_comprehension/constraint_comprehension.rs (100%) create mode 100644 air-script/src/tests/constraint_comprehension/constraint_comprehension_plonky3.rs rename air-script/{ => src}/tests/constraint_comprehension/mod.rs (100%) create mode 100644 air-script/src/tests/constraint_comprehension/test_air_plonky3.rs rename air-script/{ => src}/tests/constraint_comprehension/test_air_winterfell.rs (85%) rename air-script/{ => src}/tests/docs_sync.rs (100%) rename air-script/{ => src}/tests/evaluators/evaluators.air (100%) rename air-script/{ => src}/tests/evaluators/evaluators.rs (100%) create mode 100644 air-script/src/tests/evaluators/evaluators_plonky3.rs rename air-script/{ => src}/tests/evaluators/mod.rs (100%) rename air-script/{ => src}/tests/evaluators/test_air_plonky3.rs (61%) rename air-script/{ => src}/tests/evaluators/test_air_winterfell.rs (86%) rename air-script/{ => src}/tests/fibonacci/fibonacci.air (100%) rename air-script/{ => src}/tests/fibonacci/fibonacci.rs (100%) create mode 100644 air-script/src/tests/fibonacci/fibonacci_plonky3.rs rename air-script/{ => src}/tests/fibonacci/mod.rs (100%) rename air-script/{ => src}/tests/fibonacci/test_air_plonky3.rs (53%) rename air-script/{ => src}/tests/fibonacci/test_air_winterfell.rs (88%) rename air-script/{ => src}/tests/functions/functions_complex.air (100%) rename air-script/{ => src}/tests/functions/functions_complex.rs (100%) create mode 100644 air-script/src/tests/functions/functions_complex_plonky3.rs rename air-script/{ => src}/tests/functions/functions_simple.air (100%) rename air-script/{ => src}/tests/functions/functions_simple.rs (100%) create mode 100644 air-script/src/tests/functions/functions_simple_plonky3.rs rename air-script/{ => src}/tests/functions/inlined_functions_simple.air (100%) rename air-script/{ => src}/tests/functions/mod.rs (100%) rename air-script/{ => src}/tests/functions/test_air_plonky3.rs (73%) rename air-script/{ => src}/tests/functions/test_air_winterfell.rs (92%) rename air-script/{ => src}/tests/indexed_trace_access/indexed_trace_access.air (100%) rename air-script/{ => src}/tests/indexed_trace_access/indexed_trace_access.rs (100%) create mode 100644 air-script/src/tests/indexed_trace_access/indexed_trace_access_plonky3.rs rename air-script/{ => src}/tests/indexed_trace_access/mod.rs (100%) rename air-script/{ => src}/tests/indexed_trace_access/test_air_plonky3.rs (50%) rename air-script/{ => src}/tests/indexed_trace_access/test_air_winterfell.rs (83%) rename air-script/{ => src}/tests/list_comprehension/list_comprehension.air (100%) rename air-script/{ => src}/tests/list_comprehension/list_comprehension.rs (100%) rename air-script/{ => src}/tests/list_comprehension/list_comprehension_nested.air (100%) rename air-script/{ => src}/tests/list_comprehension/list_comprehension_nested.rs (100%) create mode 100644 air-script/src/tests/list_comprehension/list_comprehension_nested_plonky3.rs create mode 100644 air-script/src/tests/list_comprehension/list_comprehension_plonky3.rs rename air-script/{ => src}/tests/list_comprehension/mod.rs (54%) rename air-script/{ => src}/tests/list_comprehension/test_air_plonky3.rs (72%) rename air-script/{ => src}/tests/list_comprehension/test_air_winterfell.rs (87%) rename air-script/{ => src}/tests/list_folding/list_folding.air (100%) rename air-script/{ => src}/tests/list_folding/list_folding.rs (100%) create mode 100644 air-script/src/tests/list_folding/list_folding_plonky3.rs rename air-script/{ => src}/tests/list_folding/mod.rs (100%) rename air-script/{ => src}/tests/list_folding/test_air_plonky3.rs (73%) rename air-script/{ => src}/tests/list_folding/test_air_winterfell.rs (89%) rename air-script/{ => src}/tests/mod.rs (97%) rename air-script/{ => src}/tests/periodic_columns/mod.rs (100%) rename air-script/{ => src}/tests/periodic_columns/periodic_columns.air (100%) rename air-script/{ => src}/tests/periodic_columns/periodic_columns.rs (100%) create mode 100644 air-script/src/tests/periodic_columns/periodic_columns_plonky3.rs rename air-script/{ => src}/tests/periodic_columns/test_air_plonky3.rs (51%) rename air-script/{ => src}/tests/periodic_columns/test_air_winterfell.rs (82%) create mode 100644 air-script/src/tests/plonky3.rs rename air-script/{ => src}/tests/pub_inputs/mod.rs (100%) rename air-script/{ => src}/tests/pub_inputs/pub_inputs.air (100%) rename air-script/{ => src}/tests/pub_inputs/pub_inputs.rs (100%) create mode 100644 air-script/src/tests/pub_inputs/pub_inputs_plonky3.rs rename air-script/{ => src}/tests/pub_inputs/test_air_plonky3.rs (55%) rename air-script/{ => src}/tests/pub_inputs/test_air_winterfell.rs (85%) rename air-script/{ => src}/tests/selectors/mod.rs (90%) rename air-script/{ => src}/tests/selectors/selectors.air (100%) rename air-script/{ => src}/tests/selectors/selectors.rs (100%) rename air-script/{ => src}/tests/selectors/selectors_combine_complex.air (100%) rename air-script/{ => src}/tests/selectors/selectors_combine_complex.rs (100%) create mode 100644 air-script/src/tests/selectors/selectors_combine_complex_plonky3.rs rename air-script/{ => src}/tests/selectors/selectors_combine_simple.air (100%) rename air-script/{ => src}/tests/selectors/selectors_combine_simple.rs (100%) create mode 100644 air-script/src/tests/selectors/selectors_combine_simple_plonky3.rs rename air-script/{ => src}/tests/selectors/selectors_combine_with_list_comprehensions.air (100%) rename air-script/{ => src}/tests/selectors/selectors_combine_with_list_comprehensions.rs (100%) create mode 100644 air-script/src/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs create mode 100644 air-script/src/tests/selectors/selectors_plonky3.rs rename air-script/{ => src}/tests/selectors/selectors_with_evaluators.air (100%) rename air-script/{ => src}/tests/selectors/selectors_with_evaluators.rs (100%) create mode 100644 air-script/src/tests/selectors/selectors_with_evaluators_plonky3.rs rename air-script/{ => src}/tests/selectors/test_air_plonky3.rs (54%) rename air-script/{ => src}/tests/selectors/test_air_winterfell.rs (84%) rename air-script/{ => src}/tests/system/mod.rs (100%) rename air-script/{ => src}/tests/system/system.air (100%) rename air-script/{ => src}/tests/system/system.rs (100%) create mode 100644 air-script/src/tests/system/system_plonky3.rs rename air-script/{ => src}/tests/system/test_air_plonky3.rs (53%) rename air-script/{ => src}/tests/system/test_air_winterfell.rs (86%) rename air-script/{ => src}/tests/trace_col_groups/mod.rs (100%) rename air-script/{ => src}/tests/trace_col_groups/test_air_plonky3.rs (64%) rename air-script/{ => src}/tests/trace_col_groups/test_air_winterfell.rs (86%) rename air-script/{ => src}/tests/trace_col_groups/trace_col_groups.air (100%) rename air-script/{ => src}/tests/trace_col_groups/trace_col_groups.rs (100%) create mode 100644 air-script/src/tests/trace_col_groups/trace_col_groups_plonky3.rs rename air-script/{ => src}/tests/variables/mod.rs (100%) rename air-script/{ => src}/tests/variables/test_air_plonky3.rs (54%) rename air-script/{ => src}/tests/variables/test_air_winterfell.rs (86%) rename air-script/{ => src}/tests/variables/variables.air (100%) rename air-script/{ => src}/tests/variables/variables.rs (100%) create mode 100644 air-script/src/tests/variables/variables_plonky3.rs create mode 100644 air-script/src/tests/winterfell.rs delete mode 100644 air-script/tests/binary/binary_plonky3.rs delete mode 100644 air-script/tests/bitwise/bitwise_plonky3.rs delete mode 100644 air-script/tests/buses/buses_complex_plonky3.rs delete mode 100644 air-script/tests/buses/buses_simple_plonky3.rs delete mode 100644 air-script/tests/buses/buses_varlen_boundary_both_plonky3.rs delete mode 100644 air-script/tests/buses/buses_varlen_boundary_first_plonky3.rs delete mode 100644 air-script/tests/buses/buses_varlen_boundary_last_plonky3.rs delete mode 100644 air-script/tests/buses/test_air_plonky3.rs delete mode 100644 air-script/tests/codegen/mod.rs delete mode 100644 air-script/tests/codegen/plonky3.rs delete mode 100644 air-script/tests/codegen/winterfell.rs delete mode 100644 air-script/tests/computed_indices/computed_indices_complex_plonky3.rs delete mode 100644 air-script/tests/computed_indices/computed_indices_simple_plonky3.rs delete mode 100644 air-script/tests/computed_indices/mod.rs delete mode 100644 air-script/tests/constant_in_range/constant_in_range_plonky3.rs delete mode 100644 air-script/tests/constants/constants_plonky3.rs delete mode 100644 air-script/tests/constraint_comprehension/constraint_comprehension_plonky3.rs delete mode 100644 air-script/tests/constraint_comprehension/test_air_plonky3.rs delete mode 100644 air-script/tests/evaluators/evaluators_plonky3.rs delete mode 100644 air-script/tests/fibonacci/fibonacci_plonky3.rs delete mode 100644 air-script/tests/functions/functions_complex_plonky3.rs delete mode 100644 air-script/tests/functions/functions_simple_plonky3.rs delete mode 100644 air-script/tests/helpers/mod.rs delete mode 100644 air-script/tests/helpers/plonky3_periodic_columns.rs delete mode 100644 air-script/tests/indexed_trace_access/indexed_trace_access_plonky3.rs delete mode 100644 air-script/tests/list_comprehension/list_comprehension_nested_plonky3.rs delete mode 100644 air-script/tests/list_comprehension/list_comprehension_plonky3.rs delete mode 100644 air-script/tests/list_folding/list_folding_plonky3.rs delete mode 100644 air-script/tests/periodic_columns/periodic_columns_plonky3.rs delete mode 100644 air-script/tests/pub_inputs/pub_inputs_plonky3.rs delete mode 100644 air-script/tests/selectors/selectors_combine_complex_plonky3.rs delete mode 100644 air-script/tests/selectors/selectors_combine_simple_plonky3.rs delete mode 100644 air-script/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs delete mode 100644 air-script/tests/selectors/selectors_plonky3.rs delete mode 100644 air-script/tests/selectors/selectors_with_evaluators_plonky3.rs delete mode 100644 air-script/tests/system/system_plonky3.rs delete mode 100644 air-script/tests/trace_col_groups/trace_col_groups_plonky3.rs delete mode 100644 air-script/tests/variables/variables_plonky3.rs create mode 100644 scripts/generate_all_e2e_tests.sh diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index 8dbe014b7..10a5fdede 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -42,7 +42,7 @@ jobs: - name: Install mdbook and plugins uses: taiki-e/install-action@v2 with: - tool: mdbook, mdbook-linkcheck, mdbook-alerts, mdbook-katex, mdbook-mermaid + tool: mdbook@0.4.48, mdbook-linkcheck, mdbook-alerts, mdbook-katex, mdbook-mermaid - name: Build book run: mdbook build docs/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f5350d29..364bc9045 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ - Fix regressions on MIR and list_comprehensions (#449). - Add Plonky3 codegen backend (#461). - Fixed a vector unrolling issue in nested match evaluations (#491). +- In Plonky3 codegen, use AirScriptAir and AirScriptBuilder traits, and generate aux constraints (#508). +- In Plonky3 codegen, use MidenAir and MidenAirBuilder from 0xMiden's Plonky3 fork instead of AirScriptAir and AirScriptBuilder (#515). ## 0.4.0 (2025-06-20) diff --git a/air-script/Cargo.toml b/air-script/Cargo.toml index 50ec896f2..65813db4e 100644 --- a/air-script/Cargo.toml +++ b/air-script/Cargo.toml @@ -30,18 +30,26 @@ mir = { package = "air-mir", path = "../mir", version = "0.5" } [dev-dependencies] expect-test = "1.4" -p3-air = { package = "p3-air", version = "0.3", default-features = false } -p3-challenger = { package = "p3-challenger", version = "0.3", default-features = false } -p3-circle = { package = "p3-circle", version = "0.3", default-features = false } -p3-commit = { package = "p3-commit", version = "0.3", default-features = false } -p3-field = { package = "p3-field", version = "0.3", default-features = false } -p3-fri = { package = "p3-fri", version = "0.3", default-features = false } -p3-matrix = { package = "p3-matrix", version = "0.3", default-features = false } -p3-merkle-tree = { package = "p3-merkle-tree", version = "0.3", default-features = false } -p3-goldilocks = { package = "p3-goldilocks", version = "0.3", default-features = false } -p3-sha256 = { package = "p3-sha256", version = "0.3", default-features = false } -p3-symmetric = { package = "p3-symmetric", version = "0.3", default-features = false } -p3-uni-stark = { package = "p3-uni-stark", version = "0.3.0", default-features = false } + +# 0xMiden Plonky3 Fork +p3-air = { package = "p3-air", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-challenger = { package = "p3-challenger", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-commit = { package = "p3-commit", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-dft = { package = "p3-dft", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-field = { package = "p3-field", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-fri = { package = "p3-fri", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-matrix = { package = "p3-matrix", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-merkle-tree = { package = "p3-merkle-tree", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-miden-air = { package = "miden-air", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-goldilocks = { package = "p3-goldilocks", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-sha256 = { package = "p3-sha256", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-symmetric = { package = "p3-symmetric", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-uni-stark = { package = "p3-uni-stark", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } + +# MassaLabs fork +miden-processor = { package = "miden-processor", git="https://github.com/massalabs/miden-vm", rev = "bc553af69a2543a0789830e8508b019694528181", default-features = false } +miden-air = { package = "miden-air", git="https://github.com/massalabs/miden-vm", rev = "bc553af69a2543a0789830e8508b019694528181", default-features = false } + winter-air = { package = "winter-air", version = "0.12", default-features = false } winter-math = { package = "winter-math", version = "0.12", default-features = false } winter-utils = { package = "winter-utils", version = "0.12", default-features = false } diff --git a/air-script/src/lib.rs b/air-script/src/lib.rs index 5d477c7d0..67c255f21 100644 --- a/air-script/src/lib.rs +++ b/air-script/src/lib.rs @@ -1,3 +1,8 @@ pub use air_codegen_winter::CodeGenerator as WinterfellCodeGenerator; pub use air_ir::{Air, CompileError, compile}; pub use air_parser::{parse, parse_file, transforms}; + +#[cfg(test)] +pub mod test_utils; +#[cfg(test)] +mod tests; diff --git a/air-script/tests/helpers/macros.rs b/air-script/src/test_utils/air_tester_macros.rs similarity index 57% rename from air-script/tests/helpers/macros.rs rename to air-script/src/test_utils/air_tester_macros.rs index 6f9586249..53a64784e 100644 --- a/air-script/tests/helpers/macros.rs +++ b/air-script/src/test_utils/air_tester_macros.rs @@ -36,49 +36,49 @@ macro_rules! generate_air_winterfell_test { /// * `test_name` - The identifier for the test function (e.g., `test_binary_air`) /// * `air_name` - The identifier for the AIR struct (e.g., `BinaryAir`) #[macro_export] -macro_rules! generate_air_plonky3_test { +macro_rules! generate_air_plonky3_test_with_airscript_traits { ($test_name:ident, $air_name:ident) => { #[test] fn $test_name() { - type Val = Goldilocks; - type Challenge = BinomialExtensionField; - - type ByteHash = Sha256; - type FieldHash = SerializingHasher; - type MyCompress = CompressionFunctionFromHasher; - type ValMmcs = MerkleTreeMmcs; - type ChallengeMmcs = ExtensionMmcs; - type Challenger = SerializingChallenger64>; - type Pcs = CirclePcs; - type MyConfig = StarkConfig; + type Val = p3_goldilocks::Goldilocks; + type Challenge = p3_field::extension::BinomialExtensionField; + type ByteHash = p3_sha256::Sha256; + type FieldHash = p3_symmetric::SerializingHasher; + type MyCompress = p3_symmetric::CompressionFunctionFromHasher; + type ValMmcs = p3_merkle_tree::MerkleTreeMmcs; + type ChallengeMmcs = p3_commit::ExtensionMmcs; + type Challenger = p3_challenger::SerializingChallenger64< + Val, + p3_challenger::HashChallenger, + >; + type Dft = p3_dft::Radix2DitParallel; + type Pcs = p3_fri::TwoAdicFriPcs; + type MyConfig = p3_uni_stark::StarkConfig; let byte_hash = ByteHash {}; - let field_hash = FieldHash::new(Sha256); + let field_hash = FieldHash::new(p3_sha256::Sha256); let compress = MyCompress::new(byte_hash); let val_mmcs = ValMmcs::new(field_hash, compress); let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); let challenger = Challenger::from_hasher(vec![], byte_hash); - let fri_params = create_benchmark_fri_params(challenge_mmcs); - let pcs = Pcs { - mmcs: val_mmcs, - fri_params, - _phantom: PhantomData, - }; + let dft = Dft::default(); + let fri_params = p3_fri::create_benchmark_fri_params(challenge_mmcs); + let pcs = Pcs::new(dft, val_mmcs, fri_params); let config = MyConfig::new(pcs, challenger); let inputs = generate_inputs(); - let inputs_goldilocks: Vec = - inputs.iter().map(|&x| Val::from_u32(x)).collect(); + let inputs_goldilocks: Vec = inputs + .iter() + .map(|&x| ::from_u32(x)) + .collect(); let trace = generate_trace_rows::(inputs); - check_constraints_with_periodic_columns(&$air_name {}, &trace, &inputs_goldilocks); - - /*let prove_with_periodic_columns = prove_with_periodic_columns(&config, &BitwiseAir {}, trace, &inputs_goldilocks); - verify_with_periodic_columns(&config, &BitwiseAir {}, &prove_with_periodic_columns, &inputs_goldilocks).expect("Verification failed");*/ - - /*let proof = prove(&config, &BitwiseAir {}, trace, &inputs_goldilocks); - verify(&config, &BitwiseAir {}, &proof, &inputs_goldilocks).expect("Verification failed");*/ + check_constraints_with_airscript_traits::( + &$air_name {}, + &trace, + &inputs_goldilocks, + ); } }; } diff --git a/air-script/tests/codegen/helpers.rs b/air-script/src/test_utils/codegen.rs similarity index 99% rename from air-script/tests/codegen/helpers.rs rename to air-script/src/test_utils/codegen.rs index 577144d42..9e68ca753 100644 --- a/air-script/tests/codegen/helpers.rs +++ b/air-script/src/test_utils/codegen.rs @@ -1,11 +1,12 @@ use std::sync::Arc; use air_ir::{CodeGenerator, CompileError}; -use air_script::compile; use miden_diagnostics::{ CodeMap, DefaultEmitter, DiagnosticsHandler, term::termcolor::ColorChoice, }; +use crate::compile; + pub enum Target { Winterfell, Plonky3, diff --git a/air-script/src/test_utils/miden_vm_aux_trace_generator.rs b/air-script/src/test_utils/miden_vm_aux_trace_generator.rs new file mode 100644 index 000000000..a6f9517c1 --- /dev/null +++ b/air-script/src/test_utils/miden_vm_aux_trace_generator.rs @@ -0,0 +1,116 @@ +use std::collections::BTreeMap; + +use miden_air::{Felt, FieldElement, trace::main_trace::MainTrace}; +use miden_processor::{ + ColMatrix, Kernel, PrecompileTranscriptState, QuadExtension, + chiplets::{AceHints, AuxTraceBuilder as ChipletsAuxTraceBuilder}, + decoder::AuxTraceBuilder as DecoderAuxTraceBuilder, + range::AuxTraceBuilder as RangeAuxTraceBuilder, + stack::AuxTraceBuilder as StackAuxTraceBuilder, +}; +use p3_field::{ExtensionField, Field, PrimeField64}; +use p3_matrix::Matrix; +use p3_miden_air::RowMajorMatrix; + +pub enum MidenModule { + Chiplets, + Decoder, + Stack, + Range, +} + +impl MidenModule { + fn build_aux_columns>( + &self, + main_trace: &MainTrace, + rand_elements: &[EF], + ) -> Vec> { + match self { + MidenModule::Chiplets => { + let kernel = Kernel::new(&[]).unwrap(); + let ace_hints = AceHints::new(0, vec![]); + let final_transcript_state = PrecompileTranscriptState::default(); + let aux_trace_builder = + ChipletsAuxTraceBuilder::new(kernel, ace_hints, final_transcript_state); + aux_trace_builder.build_aux_columns(main_trace, rand_elements).to_vec() + }, + MidenModule::Decoder => { + let aux_trace_builder = DecoderAuxTraceBuilder {}; + aux_trace_builder.build_aux_columns(main_trace, rand_elements) + }, + MidenModule::Stack => { + let aux_trace_builder = StackAuxTraceBuilder {}; + aux_trace_builder.build_aux_columns(main_trace, rand_elements) + }, + MidenModule::Range => { + let lookup_values = vec![]; + let cycle_lookups = BTreeMap::new(); + let values_start = 0; + let aux_trace_builder = + RangeAuxTraceBuilder::new(lookup_values, cycle_lookups, values_start); + aux_trace_builder.build_aux_columns(main_trace, rand_elements) + }, + } + } +} + +/// Builds the Miden VM auxiliary trace using the provided main trace and challenges. +pub fn build_aux_trace_with_miden_vm( + main: &RowMajorMatrix, + challenges: &[EF], + module: MidenModule, +) -> RowMajorMatrix +where + F: Field + PrimeField64, + EF: ExtensionField, +{ + // Convert main trace to Miden format: transposed to column-major and use `BaseElement` instead + // of `F`` + let main_transposed = main.transpose(); + let mut felt_columns_vec = Vec::new(); + for row in main_transposed.rows() { + let col_felt = row.map(|x| Felt::new(x.as_canonical_u64())).collect::>(); + felt_columns_vec.push(col_felt); + } + let col_matrix = ColMatrix::new(felt_columns_vec); + let last_program_row = main.height().into(); + let main_trace = MainTrace::new(col_matrix, last_program_row); + + // Convert challenges to Miden format + let mut rand_elements = Vec::new(); + for r in challenges { + let coeffs: Vec = r + .as_basis_coefficients_slice() + .iter() + .map(|x| Felt::new(x.as_canonical_u64())) + .collect(); + let r_fe: &[QuadExtension] = FieldElement::slice_from_base_elements(&coeffs); + rand_elements.push(r_fe[0]); + } + + // Build aux trace using Miden VM AuxTraceBuilder + let aux_trace_miden = module.build_aux_columns(&main_trace, &rand_elements); + let aux_width = aux_trace_miden.len(); + + // Convert aux trace back to RowMajorMatrix + let num_rows = main.height(); + let trace_length = num_rows * aux_width; + let long_trace = EF::zero_vec(trace_length); + let mut aux_trace = RowMajorMatrix::new(long_trace, aux_width); + + for j in 0..aux_width { + let col = aux_trace_miden.get(j).unwrap(); + for i in 0..num_rows { + let value_felt = col[i]; + let coeffs_f: Vec = value_felt + .to_base_elements() + .iter() + .map(|x| F::from_canonical_checked(x.as_int()).unwrap()) + .collect(); + let value_ef = EF::from_basis_coefficients_iter(coeffs_f.iter().cloned()).unwrap(); + aux_trace.row_mut(i)[j] = value_ef; + } + } + + aux_trace +} diff --git a/air-script/src/test_utils/mod.rs b/air-script/src/test_utils/mod.rs new file mode 100644 index 000000000..601c64017 --- /dev/null +++ b/air-script/src/test_utils/mod.rs @@ -0,0 +1,10 @@ +/// Macros to generate Air tester structs and tests for both Plonky3 and Winterfell backends. +pub mod air_tester_macros; +/// Code generation for tests/**/*.air files. +pub mod codegen; +/// Miden VM auxiliary trace generator +pub mod miden_vm_aux_trace_generator; +/// Plonky3-specific Debug constraint builder implementation +pub mod plonky3_traits; +/// Winterfell-specific traits +pub mod winterfell_traits; diff --git a/air-script/src/test_utils/plonky3_traits.rs b/air-script/src/test_utils/plonky3_traits.rs new file mode 100644 index 000000000..1985d1558 --- /dev/null +++ b/air-script/src/test_utils/plonky3_traits.rs @@ -0,0 +1,189 @@ +use p3_field::{ExtensionField, Field}; +use p3_matrix::{ + Matrix, + dense::{DenseMatrix, RowMajorMatrix, RowMajorMatrixView}, + stack::VerticalPair, +}; +use p3_miden_air::{MidenAir, MidenAirBuilder, impl_p3_air_builder_traits}; + +/// A builder that runs constraint assertions during testing. +/// +/// Used in conjunction with [`check_constraints`] to simulate an execution trace +/// and verify that the AIR logic enforces all constraints. +#[derive(Debug)] +pub struct DebugConstraintBuilderWithAirScriptTraits<'a, F: Field, EF: ExtensionField> { + /// The index of the row currently being evaluated. + row_index: usize, + /// A view of the current and next main row as a vertical pair. + main: VerticalPair, RowMajorMatrixView<'a, F>>, + /// A view of the current and next preprocessed row as a vertical pair. + preprocessed: VerticalPair, RowMajorMatrixView<'a, F>>, + /// A view of the current and next aux row as a vertical pair. + aux: VerticalPair, RowMajorMatrixView<'a, EF>>, + /// The public values provided for constraint validation (e.g. inputs or outputs). + public_values: &'a [F], + /// A flag indicating whether this is the first row. + is_first_row: F, + /// A flag indicating whether this is the last row. + is_last_row: F, + /// A flag indicating whether this is a transition row (not the last row). + is_transition: F, + /// The periodic columns provided for constraint validation. + periodic_columns: Vec, + /// The permutation randomness in the extension field. + permutation_randomness: Vec, + /// The aux bus boundary values in the extension field. + aux_bus_boundary_values: Vec, +} + +impl<'a, F, EF> MidenAirBuilder for DebugConstraintBuilderWithAirScriptTraits<'a, F, EF> +where + F: Field, + EF: ExtensionField, +{ + type F = F; + type Expr = F; + type Var = F; + type M = VerticalPair, DenseMatrix>; + type PublicVar = F; + type PeriodicVal = EF; + type EF = EF; + type ExprEF = EF; + type VarEF = EF; + type MP = VerticalPair, DenseMatrix>; + type RandomVar = EF; + + fn main(&self) -> Self::M { + self.main + } + + fn is_first_row(&self) -> Self::Expr { + self.is_first_row + } + + fn is_last_row(&self) -> Self::Expr { + self.is_last_row + } + + fn is_transition_window(&self, size: usize) -> Self::Expr { + if size == 2 { + self.is_transition + } else { + panic!("only supports a window size of 2") + } + } + + fn assert_zero>(&mut self, x: I) { + assert_eq!(x.into(), F::ZERO, "constraints had nonzero value on row {}", self.row_index); + } + + fn public_values(&self) -> &[Self::PublicVar] { + self.public_values + } + + fn periodic_evals(&self) -> &[::PeriodicVal] { + self.periodic_columns.as_slice() + } + + fn preprocessed(&self) -> Self::M { + self.preprocessed + } + + fn assert_zero_ext(&mut self, x: I) + where + I: Into, + { + assert_eq!( + x.into(), + EF::ZERO, + "constraints on ext field had nonzero value on row {}", + self.row_index + ); + } + + fn permutation(&self) -> Self::MP { + self.aux + } + + fn permutation_randomness(&self) -> &[Self::RandomVar] { + self.permutation_randomness.as_slice() + } + + fn aux_bus_boundary_values(&self) -> &[::VarEF] { + self.aux_bus_boundary_values.as_slice() + } +} + +impl_p3_air_builder_traits!(DebugConstraintBuilderWithAirScriptTraits<'a, F, EF> where F: Field, EF: ExtensionField); + +pub(crate) fn check_constraints_with_airscript_traits( + air: &A, + main: &RowMajorMatrix, + public_values: &Vec, +) where + F: Field, + EF: ExtensionField, + A: MidenAir, +{ + let height = main.height(); + + let aux_bus_boundary_values: Vec<_> = (0..air.aux_width()).map(|_| EF::GENERATOR).collect(); + let alpha = EF::from_basis_coefficients_iter( + (0..EF::DIMENSION).map(|i| F::from_u64(123456789 * (i as u64 + 1))), + ) + .unwrap(); + let beta = EF::from_u64(987654321); + let beta_powers: Vec = (0..(air.num_randomness().saturating_sub(1))) + .map(|power| beta.exp_u64(power as u64)) + .collect(); + let mut permutation_randomness = Vec::with_capacity(air.num_randomness()); + permutation_randomness.push(alpha); + permutation_randomness.extend_from_slice(beta_powers.as_slice()); + + let aux_trace = air + .build_aux_trace(main, &permutation_randomness) + .unwrap_or(DenseMatrix::default(0, height)); + + (0..height).for_each(|i| { + let i_next = (i + 1) % height; + + let main_local = main.row_slice(i).unwrap(); // i < height so unwrap should never fail. + let main_next = main.row_slice(i_next).unwrap(); // i_next < height so unwrap should never fail. + let main = VerticalPair::new( + RowMajorMatrixView::new_row(&*main_local), + RowMajorMatrixView::new_row(&*main_next), + ); + let aux_local_ref = aux_trace.row_slice(i); + let aux_next_ref = aux_trace.row_slice(i_next); + let aux_local = aux_local_ref.as_deref().unwrap_or_default(); + let aux_next = aux_next_ref.as_deref().unwrap_or_default(); + let aux = VerticalPair::new( + RowMajorMatrixView::new_row(&*aux_local), + RowMajorMatrixView::new_row(&*aux_next), + ); + let preprocessed = VerticalPair::new::( + RowMajorMatrixView::new(&[], 0), + RowMajorMatrixView::new(&[], 0), + ); + + let periodic_columns_base: Vec<_> = + air.periodic_table().iter().map(|col| col[i % col.len()]).collect(); + let periodic_columns: Vec = + periodic_columns_base.iter().map(|&v| EF::from(v)).collect(); + + let mut builder = DebugConstraintBuilderWithAirScriptTraits { + row_index: i, + main, + preprocessed, + aux, + public_values, + is_first_row: F::from_bool(i == 0), + is_last_row: F::from_bool(i == height - 1), + is_transition: F::from_bool(i != height - 1), + periodic_columns, + permutation_randomness: permutation_randomness.clone(), + aux_bus_boundary_values: aux_bus_boundary_values.clone(), + }; + air.eval(&mut builder); + }); +} diff --git a/air-script/tests/helpers/winterfell_test_helpers.rs b/air-script/src/test_utils/winterfell_traits.rs similarity index 100% rename from air-script/tests/helpers/winterfell_test_helpers.rs rename to air-script/src/test_utils/winterfell_traits.rs diff --git a/air-script/tests/binary/binary.air b/air-script/src/tests/binary/binary.air similarity index 100% rename from air-script/tests/binary/binary.air rename to air-script/src/tests/binary/binary.air diff --git a/air-script/tests/binary/binary.rs b/air-script/src/tests/binary/binary.rs similarity index 100% rename from air-script/tests/binary/binary.rs rename to air-script/src/tests/binary/binary.rs diff --git a/air-script/src/tests/binary/binary_plonky3.rs b/air-script/src/tests/binary/binary_plonky3.rs new file mode 100644 index 000000000..0c514ea56 --- /dev/null +++ b/air-script/src/tests/binary/binary_plonky3.rs @@ -0,0 +1,44 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 2; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct BinaryAir; + +impl MidenAir for BinaryAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into() - public_values[0].into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[0].clone().into() * main_current[0].clone().into() - main_current[0].clone().into()); + builder.assert_zero(main_current[1].clone().into() * main_current[1].clone().into() - main_current[1].clone().into()); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/binary/mod.rs b/air-script/src/tests/binary/mod.rs similarity index 100% rename from air-script/tests/binary/mod.rs rename to air-script/src/tests/binary/mod.rs diff --git a/air-script/tests/binary/test_air_plonky3.rs b/air-script/src/tests/binary/test_air_plonky3.rs similarity index 51% rename from air-script/tests/binary/test_air_plonky3.rs rename to air-script/src/tests/binary/test_air_plonky3.rs index cb06fb5f4..eced0b469 100644 --- a/air-script/tests/binary/test_air_plonky3.rs +++ b/air-script/src/tests/binary/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - binary::binary_plonky3::{BinaryAir, NUM_COLUMNS}, - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::binary::binary_plonky3::{BinaryAir, MAIN_WIDTH}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -52,4 +41,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -generate_air_plonky3_test!(test_air_plonky3, BinaryAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, BinaryAir); diff --git a/air-script/tests/binary/test_air_winterfell.rs b/air-script/src/tests/binary/test_air_winterfell.rs similarity index 87% rename from air-script/tests/binary/test_air_winterfell.rs rename to air-script/src/tests/binary/test_air_winterfell.rs index eca996b11..2135c11b6 100644 --- a/air-script/tests/binary/test_air_winterfell.rs +++ b/air-script/src/tests/binary/test_air_winterfell.rs @@ -3,9 +3,9 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - binary::binary::PublicInputs, generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::binary::binary::PublicInputs, }; #[derive(Clone)] @@ -41,7 +41,7 @@ impl AirTester for BinaryAirTester { generate_air_winterfell_test!( test_binary_air, - crate::binary::binary::BinaryAir, + crate::tests::binary::binary::BinaryAir, BinaryAirTester, 1024 ); diff --git a/air-script/tests/bitwise/bitwise.air b/air-script/src/tests/bitwise/bitwise.air similarity index 100% rename from air-script/tests/bitwise/bitwise.air rename to air-script/src/tests/bitwise/bitwise.air diff --git a/air-script/tests/bitwise/bitwise.rs b/air-script/src/tests/bitwise/bitwise.rs similarity index 100% rename from air-script/tests/bitwise/bitwise.rs rename to air-script/src/tests/bitwise/bitwise.rs diff --git a/air-script/src/tests/bitwise/bitwise_plonky3.rs b/air-script/src/tests/bitwise/bitwise_plonky3.rs new file mode 100644 index 000000000..bfded5b9b --- /dev/null +++ b/air-script/src/tests/bitwise/bitwise_plonky3.rs @@ -0,0 +1,73 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 14; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 2; +pub const PERIOD: usize = 8; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct BitwiseAir; + +impl MidenAir for BitwiseAir +where F: Field, + EF: ExtensionField, +{ + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } + + fn periodic_table(&self) -> Vec> { + vec![ + vec![F::from_u64(1), F::from_u64(0), F::from_u64(0), F::from_u64(0), F::from_u64(0), F::from_u64(0), F::from_u64(0), F::from_u64(0)], + vec![F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(0)], + ] + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[13].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[0].clone().into() * main_current[0].clone().into() - main_current[0].clone().into()); + builder.when_transition().assert_zero_ext(AB::ExprEF::from(periodic_values[1].clone().into()) * (AB::ExprEF::from(main_next[0].clone().into()) - AB::ExprEF::from(main_current[0].clone().into()))); + builder.assert_zero(main_current[3].clone().into() * main_current[3].clone().into() - main_current[3].clone().into()); + builder.assert_zero(main_current[4].clone().into() * main_current[4].clone().into() - main_current[4].clone().into()); + builder.assert_zero(main_current[5].clone().into() * main_current[5].clone().into() - main_current[5].clone().into()); + builder.assert_zero(main_current[6].clone().into() * main_current[6].clone().into() - main_current[6].clone().into()); + builder.assert_zero(main_current[7].clone().into() * main_current[7].clone().into() - main_current[7].clone().into()); + builder.assert_zero(main_current[8].clone().into() * main_current[8].clone().into() - main_current[8].clone().into()); + builder.assert_zero(main_current[9].clone().into() * main_current[9].clone().into() - main_current[9].clone().into()); + builder.assert_zero(main_current[10].clone().into() * main_current[10].clone().into() - main_current[10].clone().into()); + builder.assert_zero_ext(AB::ExprEF::from(periodic_values[0].clone().into()) * (AB::ExprEF::from(main_current[1].clone().into()) - (AB::ExprEF::from(main_current[3].clone().into()) + AB::ExprEF::from(main_current[4].clone().into()).double() + AB::ExprEF::from_u64(4) * AB::ExprEF::from(main_current[5].clone().into()) + AB::ExprEF::from_u64(8) * AB::ExprEF::from(main_current[6].clone().into())))); + builder.assert_zero_ext(AB::ExprEF::from(periodic_values[0].clone().into()) * (AB::ExprEF::from(main_current[2].clone().into()) - (AB::ExprEF::from(main_current[7].clone().into()) + AB::ExprEF::from(main_current[8].clone().into()).double() + AB::ExprEF::from_u64(4) * AB::ExprEF::from(main_current[9].clone().into()) + AB::ExprEF::from_u64(8) * AB::ExprEF::from(main_current[10].clone().into())))); + builder.when_transition().assert_zero_ext(AB::ExprEF::from(periodic_values[1].clone().into()) * (AB::ExprEF::from(main_next[1].clone().into()) - (AB::ExprEF::from(main_current[1].clone().into()) * AB::ExprEF::from_u64(16) + AB::ExprEF::from(main_current[3].clone().into()) + AB::ExprEF::from(main_current[4].clone().into()).double() + AB::ExprEF::from_u64(4) * AB::ExprEF::from(main_current[5].clone().into()) + AB::ExprEF::from_u64(8) * AB::ExprEF::from(main_current[6].clone().into())))); + builder.when_transition().assert_zero_ext(AB::ExprEF::from(periodic_values[1].clone().into()) * (AB::ExprEF::from(main_next[2].clone().into()) - (AB::ExprEF::from(main_current[2].clone().into()) * AB::ExprEF::from_u64(16) + AB::ExprEF::from(main_current[7].clone().into()) + AB::ExprEF::from(main_current[8].clone().into()).double() + AB::ExprEF::from_u64(4) * AB::ExprEF::from(main_current[9].clone().into()) + AB::ExprEF::from_u64(8) * AB::ExprEF::from(main_current[10].clone().into())))); + builder.assert_zero_ext(AB::ExprEF::from(periodic_values[0].clone().into()) * AB::ExprEF::from(main_current[11].clone().into())); + builder.when_transition().assert_zero_ext(AB::ExprEF::from(periodic_values[1].clone().into()) * (AB::ExprEF::from(main_current[12].clone().into()) - AB::ExprEF::from(main_next[11].clone().into()))); + builder.assert_zero((AB::Expr::ONE - main_current[0].clone().into()) * (main_current[12].clone().into() - (main_current[11].clone().into() * AB::Expr::from_u64(16) + main_current[3].clone().into() * main_current[7].clone().into() + main_current[4].clone().into().double() * main_current[8].clone().into() + AB::Expr::from_u64(4) * main_current[5].clone().into() * main_current[9].clone().into() + AB::Expr::from_u64(8) * main_current[6].clone().into() * main_current[10].clone().into())) + main_current[0].clone().into() * (main_current[12].clone().into() - (main_current[11].clone().into() * AB::Expr::from_u64(16) + main_current[3].clone().into() + main_current[7].clone().into() - main_current[3].clone().into().double() * main_current[7].clone().into() + (main_current[4].clone().into() + main_current[8].clone().into() - main_current[4].clone().into().double() * main_current[8].clone().into()).double() + AB::Expr::from_u64(4) * (main_current[5].clone().into() + main_current[9].clone().into() - main_current[5].clone().into().double() * main_current[9].clone().into()) + AB::Expr::from_u64(8) * (main_current[6].clone().into() + main_current[10].clone().into() - main_current[6].clone().into().double() * main_current[10].clone().into())))); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/bitwise/mod.rs b/air-script/src/tests/bitwise/mod.rs similarity index 86% rename from air-script/tests/bitwise/mod.rs rename to air-script/src/tests/bitwise/mod.rs index 31fe7ab14..54b732efb 100644 --- a/air-script/tests/bitwise/mod.rs +++ b/air-script/src/tests/bitwise/mod.rs @@ -3,6 +3,7 @@ mod bitwise; #[rustfmt::skip] #[allow(clippy::all)] +#[allow(unused_imports)] mod bitwise_plonky3; mod test_air_plonky3; diff --git a/air-script/tests/bitwise/test_air_plonky3.rs b/air-script/src/tests/bitwise/test_air_plonky3.rs similarity index 70% rename from air-script/tests/bitwise/test_air_plonky3.rs rename to air-script/src/tests/bitwise/test_air_plonky3.rs index d3c6efcaa..d969cb1a6 100644 --- a/air-script/tests/bitwise/test_air_plonky3.rs +++ b/air-script/src/tests/bitwise/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - bitwise::bitwise_plonky3::{BitwiseAir, NUM_COLUMNS}, - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::bitwise::bitwise_plonky3::{BitwiseAir, MAIN_WIDTH}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -90,4 +79,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -generate_air_plonky3_test!(test_air_plonky3, BitwiseAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, BitwiseAir); diff --git a/air-script/tests/bitwise/test_air_winterfell.rs b/air-script/src/tests/bitwise/test_air_winterfell.rs similarity index 90% rename from air-script/tests/bitwise/test_air_winterfell.rs rename to air-script/src/tests/bitwise/test_air_winterfell.rs index c5166f091..e563782ef 100644 --- a/air-script/tests/bitwise/test_air_winterfell.rs +++ b/air-script/src/tests/bitwise/test_air_winterfell.rs @@ -5,9 +5,9 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{AuxTraceWithMetadata, Trace, TraceTable, matrix::ColMatrix}; use crate::{ - bitwise::bitwise::PublicInputs, generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::bitwise::bitwise::PublicInputs, }; #[derive(Clone)] @@ -52,7 +52,7 @@ impl AirTester for BitwiseAirTester { generate_air_winterfell_test!( test_bitwise_air, - crate::bitwise::bitwise::BitwiseAir, + crate::tests::bitwise::bitwise::BitwiseAir, BitwiseAirTester, 1024 ); diff --git a/air-script/tests/buses/buses_complex.air b/air-script/src/tests/buses/buses_complex.air similarity index 80% rename from air-script/tests/buses/buses_complex.air rename to air-script/src/tests/buses/buses_complex.air index 401ca8b67..f27a2745c 100644 --- a/air-script/tests/buses/buses_complex.air +++ b/air-script/src/tests/buses/buses_complex.air @@ -1,7 +1,7 @@ def BusesAir trace_columns { - main: [a, b, s1, s2, d], + main: [a, b, s1, s2, s3, s4, d], } buses { @@ -34,7 +34,7 @@ integrity_constraints { p.insert(2, b) when 1 - s1; p.remove(2, a) when 1 - s2; - q.insert(3, a) when s1; - q.insert(3, a) when s1; - q.remove(4, b) with d; + q.insert(3, a) when s3; + q.insert(3, a) when s4; + q.remove(3, b) with d; } diff --git a/air-script/tests/buses/buses_complex.rs b/air-script/src/tests/buses/buses_complex.rs similarity index 93% rename from air-script/tests/buses/buses_complex.rs rename to air-script/src/tests/buses/buses_complex.rs index d15eeeb8f..33271a2ab 100644 --- a/air-script/tests/buses/buses_complex.rs +++ b/air-script/src/tests/buses/buses_complex.rs @@ -99,6 +99,6 @@ impl Air for BusesAir { let aux_current = aux_frame.current(); let aux_next = aux_frame.next(); result[0] = ((aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + (E::from(Felt::new(3_u64)) + E::from(main_current[1])) * aux_rand_elements.rand_elements()[2] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[3]) * E::from(main_current[2]) + E::ONE - E::from(main_current[2])) * ((aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[1]) * aux_rand_elements.rand_elements()[2]) * (E::ONE - E::from(main_current[2])) + E::from(main_current[2])) * aux_current[0] - ((aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + (E::from(Felt::new(3_u64)) + E::from(main_current[1])) * aux_rand_elements.rand_elements()[2] + E::from(main_current[1]) * aux_rand_elements.rand_elements()[3]) * E::from(main_current[3]) + E::ONE - E::from(main_current[3])) * ((aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (E::ONE - E::from(main_current[3])) + E::from(main_current[3])) * aux_next[0]; - result[1] = (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(4_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[1]) * aux_rand_elements.rand_elements()[2]) * aux_current[1] + (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(4_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[1]) * aux_rand_elements.rand_elements()[2]) * E::from(main_current[2]) + (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(4_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[1]) * aux_rand_elements.rand_elements()[2]) * E::from(main_current[2]) - ((aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(4_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[1]) * aux_rand_elements.rand_elements()[2]) * aux_next[1] + (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * E::from(main_current[4])); + result[1] = (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[1]) * aux_rand_elements.rand_elements()[2]) * aux_current[1] + (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[1]) * aux_rand_elements.rand_elements()[2]) * E::from(main_current[4]) + (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[1]) * aux_rand_elements.rand_elements()[2]) * E::from(main_current[5]) - ((aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[1]) * aux_rand_elements.rand_elements()[2]) * aux_next[1] + (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(3_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * E::from(main_current[6])); } } \ No newline at end of file diff --git a/air-script/src/tests/buses/buses_complex_plonky3.rs b/air-script/src/tests/buses/buses_complex_plonky3.rs new file mode 100644 index 000000000..00d266cf9 --- /dev/null +++ b/air-script/src/tests/buses/buses_complex_plonky3.rs @@ -0,0 +1,138 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 7; +pub const AUX_WIDTH: usize = 2; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 2; +pub const MAX_BETA_CHALLENGE_POWER: usize = 3; + +pub struct BusesAir; + +impl MidenAir for BusesAir +where F: Field, + EF: ExtensionField, +{ + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn num_randomness(&self) -> usize { + 1 + MAX_BETA_CHALLENGE_POWER + } + + fn aux_width(&self) -> usize { + AUX_WIDTH + } + + fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { + // Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders. + + let num_rows = _main.height(); + let trace_length = num_rows * AUX_WIDTH; + let mut long_trace = EF::zero_vec(trace_length); + let mut trace = RowMajorMatrix::new(long_trace, AUX_WIDTH); + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[EF; AUX_WIDTH]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + // Initialize first row + let initial_values = Self::buses_initial_values::(); + for j in 0..AUX_WIDTH { + rows[0][j] = initial_values[j]; + } + // Fill subsequent rows using direct access to the rows array + for i in 0..num_rows-1 { + let i_next = (i + 1) % num_rows; + let main_local = _main.row_slice(i).unwrap(); // i < height so unwrap should never fail. + let main_next = _main.row_slice(i_next).unwrap(); // i_next < height so unwrap should never fail. + let main = VerticalPair::new( + RowMajorMatrixView::new_row(&*main_local), + RowMajorMatrixView::new_row(&*main_next), + ); + let periodic_values: [_; NUM_PERIODIC_VALUES] = >::periodic_table(self).iter().map(|col| col[i % col.len()]).collect::>().try_into().expect("Wrong number of periodic values"); + let prev_row = &rows[i]; + let next_row = Self::buses_transitions::( + &main, + _challenges, + &periodic_values, + prev_row, + ); + for j in 0..AUX_WIDTH { + rows[i+1][j] = next_row[j]; + } + } + Some(trace) + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + let (&alpha, beta_challenges) = builder.permutation_randomness().split_first().expect("Wrong number of randomness"); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); + let aux_bus_boundary_values: [_; AUX_WIDTH] = builder.aux_bus_boundary_values().try_into().expect("Wrong number of aux bus boundary values"); + let aux = builder.permutation(); + let (aux_current, aux_next) = ( + aux.row_slice(0).unwrap(), + aux.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[2].clone().into() * main_current[2].clone().into() - main_current[2].clone().into()); + builder.assert_zero(main_current[3].clone().into() * main_current[3].clone().into() - main_current[3].clone().into()); + + // Aux boundary constraints + builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); + builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); + builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); + builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); + + // Aux integrity/transition constraints + builder.when_transition().assert_zero_ext(((alpha.into() + beta_challenges[0].into() + (AB::ExprEF::from_u64(3) + AB::ExprEF::from(main_current[1].clone().into())) * beta_challenges[1].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[2].into()) * AB::ExprEF::from(main_current[2].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[2].clone().into())) * ((alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[1].clone().into()) * beta_challenges[1].into()) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[2].clone().into())) + AB::ExprEF::from(main_current[2].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into() + (AB::ExprEF::from_u64(3) + AB::ExprEF::from(main_current[1].clone().into())) * beta_challenges[1].into() + AB::ExprEF::from(main_current[1].clone().into()) * beta_challenges[2].into()) * AB::ExprEF::from(main_current[3].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[3].clone().into())) * ((alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[3].clone().into())) + AB::ExprEF::from(main_current[3].clone().into())) * AB::ExprEF::from(aux_next[0].clone().into())); + builder.when_transition().assert_zero_ext((alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[1].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(aux_current[1].clone().into()) + (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[1].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(main_current[4].clone().into()) + (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[1].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(main_current[5].clone().into()) - ((alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[1].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(aux_next[1].clone().into()) + (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(main_current[6].clone().into()))); + } +} + +impl BusesAir { + fn buses_initial_values() -> Vec + where F: Field, + EF: ExtensionField, + { + vec![ + EF::ONE, + EF::ZERO, + ] + } + + fn buses_transitions(main: &VerticalPair, RowMajorMatrixView>, challenges: &[EF], periodic_evals: &[F], aux_current: &[EF]) -> Vec + where F: Field, + EF: ExtensionField, + { + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + let (&alpha, beta_challenges) = challenges.split_first().expect("Wrong number of randomness"); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = periodic_evals.try_into().expect("Wrong number of periodic values"); + vec![ + (((alpha + beta_challenges[0] + (EF::from_u64(3) + EF::from(main_current[1].clone())) * beta_challenges[1] + EF::from(main_current[0].clone()) * beta_challenges[2]) * EF::from(main_current[2].clone()) + EF::ONE - EF::from(main_current[2].clone())) * ((alpha + beta_challenges[0].double() + EF::from(main_current[1].clone()) * beta_challenges[1]) * (EF::ONE - EF::from(main_current[2].clone())) + EF::from(main_current[2].clone())) * EF::from(aux_current[0].clone())) * (((alpha + beta_challenges[0] + (EF::from_u64(3) + EF::from(main_current[1].clone())) * beta_challenges[1] + EF::from(main_current[1].clone()) * beta_challenges[2]) * EF::from(main_current[3].clone()) + EF::ONE - EF::from(main_current[3].clone())) * ((alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1]) * (EF::ONE - EF::from(main_current[3].clone())) + EF::from(main_current[3].clone()))).inverse(), + ((alpha + EF::from_u64(3) * beta_challenges[0] + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + EF::from_u64(3) * beta_challenges[0] + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + EF::from_u64(3) * beta_challenges[0] + EF::from(main_current[1].clone()) * beta_challenges[1]) * EF::from(aux_current[1].clone()) + (alpha + EF::from_u64(3) * beta_challenges[0] + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + EF::from_u64(3) * beta_challenges[0] + EF::from(main_current[1].clone()) * beta_challenges[1]) * EF::from(main_current[4].clone()) + (alpha + EF::from_u64(3) * beta_challenges[0] + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + EF::from_u64(3) * beta_challenges[0] + EF::from(main_current[1].clone()) * beta_challenges[1]) * EF::from(main_current[5].clone()) - (alpha + EF::from_u64(3) * beta_challenges[0] + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + EF::from_u64(3) * beta_challenges[0] + EF::from(main_current[0].clone()) * beta_challenges[1]) * EF::from(main_current[6].clone())) * ((alpha + EF::from_u64(3) * beta_challenges[0] + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + EF::from_u64(3) * beta_challenges[0] + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + EF::from_u64(3) * beta_challenges[0] + EF::from(main_current[1].clone()) * beta_challenges[1])).inverse(), + ] + } +} \ No newline at end of file diff --git a/air-script/tests/buses/buses_simple.air b/air-script/src/tests/buses/buses_simple.air similarity index 100% rename from air-script/tests/buses/buses_simple.air rename to air-script/src/tests/buses/buses_simple.air diff --git a/air-script/tests/buses/buses_simple.rs b/air-script/src/tests/buses/buses_simple.rs similarity index 100% rename from air-script/tests/buses/buses_simple.rs rename to air-script/src/tests/buses/buses_simple.rs diff --git a/air-script/src/tests/buses/buses_simple_plonky3.rs b/air-script/src/tests/buses/buses_simple_plonky3.rs new file mode 100644 index 000000000..1ee51d533 --- /dev/null +++ b/air-script/src/tests/buses/buses_simple_plonky3.rs @@ -0,0 +1,133 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 1; +pub const AUX_WIDTH: usize = 2; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 2; +pub const MAX_BETA_CHALLENGE_POWER: usize = 2; + +pub struct BusesAir; + +impl MidenAir for BusesAir +where F: Field, + EF: ExtensionField, +{ + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn num_randomness(&self) -> usize { + 1 + MAX_BETA_CHALLENGE_POWER + } + + fn aux_width(&self) -> usize { + AUX_WIDTH + } + + fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { + // Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders. + + let num_rows = _main.height(); + let trace_length = num_rows * AUX_WIDTH; + let mut long_trace = EF::zero_vec(trace_length); + let mut trace = RowMajorMatrix::new(long_trace, AUX_WIDTH); + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[EF; AUX_WIDTH]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + // Initialize first row + let initial_values = Self::buses_initial_values::(); + for j in 0..AUX_WIDTH { + rows[0][j] = initial_values[j]; + } + // Fill subsequent rows using direct access to the rows array + for i in 0..num_rows-1 { + let i_next = (i + 1) % num_rows; + let main_local = _main.row_slice(i).unwrap(); // i < height so unwrap should never fail. + let main_next = _main.row_slice(i_next).unwrap(); // i_next < height so unwrap should never fail. + let main = VerticalPair::new( + RowMajorMatrixView::new_row(&*main_local), + RowMajorMatrixView::new_row(&*main_next), + ); + let periodic_values: [_; NUM_PERIODIC_VALUES] = >::periodic_table(self).iter().map(|col| col[i % col.len()]).collect::>().try_into().expect("Wrong number of periodic values"); + let prev_row = &rows[i]; + let next_row = Self::buses_transitions::( + &main, + _challenges, + &periodic_values, + prev_row, + ); + for j in 0..AUX_WIDTH { + rows[i+1][j] = next_row[j]; + } + } + Some(trace) + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + let (&alpha, beta_challenges) = builder.permutation_randomness().split_first().expect("Wrong number of randomness"); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); + let aux_bus_boundary_values: [_; AUX_WIDTH] = builder.aux_bus_boundary_values().try_into().expect("Wrong number of aux bus boundary values"); + let aux = builder.permutation(); + let (aux_current, aux_next) = ( + aux.row_slice(0).unwrap(), + aux.row_slice(1).unwrap(), + ); + + // Main boundary constraints + + // Main integrity/transition constraints + + // Aux boundary constraints + builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); + builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); + builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); + + // Aux integrity/transition constraints + builder.when_transition().assert_zero_ext(((alpha.into() + beta_challenges[0].into()) * AB::ExprEF::from(main_current[0].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into()) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) + AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(aux_next[0].clone().into())); + builder.when_transition().assert_zero_ext((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_current[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_next[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()).double())); + } +} + +impl BusesAir { + fn buses_initial_values() -> Vec + where F: Field, + EF: ExtensionField, + { + vec![ + EF::ZERO, + ] + } + + fn buses_transitions(main: &VerticalPair, RowMajorMatrixView>, challenges: &[EF], periodic_evals: &[F], aux_current: &[EF]) -> Vec + where F: Field, + EF: ExtensionField, + { + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + let (&alpha, beta_challenges) = challenges.split_first().expect("Wrong number of randomness"); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = periodic_evals.try_into().expect("Wrong number of periodic values"); + vec![ + (((alpha + beta_challenges[0]) * EF::from(main_current[0].clone()) + EF::ONE - EF::from(main_current[0].clone())) * EF::from(aux_current[0].clone())) * ((alpha + beta_challenges[0]) * (EF::ONE - EF::from(main_current[0].clone())) + EF::from(main_current[0].clone())).inverse(), + ((alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(aux_current[1].clone()) + (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(main_current[0].clone()) - (alpha + beta_challenges[0] + beta_challenges[1].double()).double()) * ((alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double())).inverse(), + ] + } +} \ No newline at end of file diff --git a/air-script/tests/buses/buses_simple_with_evaluators.air b/air-script/src/tests/buses/buses_simple_with_evaluators.air similarity index 100% rename from air-script/tests/buses/buses_simple_with_evaluators.air rename to air-script/src/tests/buses/buses_simple_with_evaluators.air diff --git a/air-script/src/tests/buses/buses_simple_with_evaluators.rs b/air-script/src/tests/buses/buses_simple_with_evaluators.rs new file mode 100644 index 000000000..b0012a15a --- /dev/null +++ b/air-script/src/tests/buses/buses_simple_with_evaluators.rs @@ -0,0 +1,100 @@ +use winter_air::{Air, AirContext, Assertion, AuxRandElements, EvaluationFrame, ProofOptions as WinterProofOptions, TransitionConstraintDegree, TraceInfo}; +use winter_math::fields::f64::BaseElement as Felt; +use winter_math::{ExtensionOf, FieldElement, ToElements}; +use winter_utils::{ByteWriter, Serializable}; + +pub struct PublicInputs { + inputs: [Felt; 2], +} + +impl PublicInputs { + pub fn new(inputs: [Felt; 2]) -> Self { + Self { inputs } + } +} + +impl Serializable for PublicInputs { + fn write_into(&self, target: &mut W) { + self.inputs.write_into(target); + } +} + +impl ToElements for PublicInputs { + fn to_elements(&self) -> Vec { + let mut elements = Vec::new(); + elements.extend_from_slice(&self.inputs); + elements + } +} + +pub struct BusesAir { + context: AirContext, + inputs: [Felt; 2], +} + +impl BusesAir { + pub fn last_step(&self) -> usize { + self.trace_length() - self.context().num_transition_exemptions() + } +} + +impl Air for BusesAir { + type BaseField = Felt; + type PublicInputs = PublicInputs; + + fn context(&self) -> &AirContext { + &self.context + } + + fn new(trace_info: TraceInfo, public_inputs: PublicInputs, options: WinterProofOptions) -> Self { + let main_degrees = vec![]; + let aux_degrees = vec![TransitionConstraintDegree::new(2), TransitionConstraintDegree::new(1)]; + let num_main_assertions = 0; + let num_aux_assertions = 3; + + let context = AirContext::new_multi_segment( + trace_info, + main_degrees, + aux_degrees, + num_main_assertions, + num_aux_assertions, + options, + ) + .set_num_transition_exemptions(2); + Self { context, inputs: public_inputs.inputs } + } + + fn get_periodic_column_values(&self) -> Vec> { + vec![] + } + + fn get_assertions(&self) -> Vec> { + let mut result = Vec::new(); + result + } + + fn get_aux_assertions>(&self, aux_rand_elements: &AuxRandElements) -> Vec> { + let mut result = Vec::new(); + result.push(Assertion::single(0, self.last_step(), E::ONE)); + result.push(Assertion::single(1, 0, E::ZERO)); + result.push(Assertion::single(1, self.last_step(), E::ZERO)); + result + } + + fn evaluate_transition>(&self, frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E]) { + let main_current = frame.current(); + let main_next = frame.next(); + } + + fn evaluate_aux_transition(&self, main_frame: &EvaluationFrame, aux_frame: &EvaluationFrame, _periodic_values: &[F], aux_rand_elements: &AuxRandElements, result: &mut [E]) + where F: FieldElement, + E: FieldElement + ExtensionOf, + { + let main_current = main_frame.current(); + let main_next = main_frame.next(); + let aux_current = aux_frame.current(); + let aux_next = aux_frame.next(); + result[0] = ((aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1]) * E::from(main_current[0]) + E::ONE - E::from(main_current[0])) * aux_current[0] - ((aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1]) * (E::ONE - E::from(main_current[0])) + E::from(main_current[0])) * aux_next[0]; + result[1] = (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * aux_current[1] + (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * E::from(main_current[0]) - ((aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * aux_next[1] + (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * E::from(Felt::new(2_u64))); + } +} \ No newline at end of file diff --git a/air-script/src/tests/buses/buses_simple_with_evaluators_plonky3.rs b/air-script/src/tests/buses/buses_simple_with_evaluators_plonky3.rs new file mode 100644 index 000000000..7ad2d398f --- /dev/null +++ b/air-script/src/tests/buses/buses_simple_with_evaluators_plonky3.rs @@ -0,0 +1,108 @@ +use crate::test_utils::plonky3_traits::{AirScriptAir, AirScriptBuilder}; +use p3_air::{Air, AirBuilder, BaseAir, BaseAirWithPublicValues, ExtensionBuilder}; +use p3_field::{Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; + +pub const MAIN_WIDTH: usize = 1; +pub const AUX_WIDTH: usize = 2; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 2; +pub const MAX_BETA_CHALLENGE_POWER: usize = 2; + +pub struct BusesAir; + +impl BaseAir for BusesAir { + fn width(&self) -> usize { + MAIN_WIDTH + } +} + +impl BaseAirWithPublicValues for BusesAir { + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } +} + +impl> AirScriptAir for BusesAir { + fn aux_width(&self) -> usize { + AUX_WIDTH + } + + fn max_beta_challenge_powers(&self) -> usize { + MAX_BETA_CHALLENGE_POWER + } + + fn periodic_table(&self) -> Vec> { + vec![] + } + + fn eval(&self, builder: &mut AB) { + let public_values: [_; NUM_PUBLIC_VALUES] = + builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = + builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let main = builder.main(); + let (main_current, main_next) = (main.row_slice(0).unwrap(), main.row_slice(1).unwrap()); + let alpha = builder.alpha(); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = + builder.beta_powers().try_into().expect("Wrong number of beta challenges"); + let aux_bus_boundary_values: [_; AUX_WIDTH] = builder + .aux_bus_boundary_values() + .try_into() + .expect("Wrong number of aux bus boundary values"); + let aux = builder.permutation(); + let (aux_current, aux_next) = (aux.row_slice(0).unwrap(), aux.row_slice(1).unwrap()); + + // Main boundary constraints + + // Main integrity/transition constraints + + // Aux boundary constraints + builder + .when_last_row() + .assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); + builder + .when_first_row() + .assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); + builder + .when_last_row() + .assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); + + // Aux integrity/transition constraints + builder.when_transition().assert_zero_ext( + ((alpha.into() + beta_challenges[0].into()) + * AB::ExprEF::from(main_current[0].clone().into()) + + AB::ExprEF::ONE + - AB::ExprEF::from(main_current[0].clone().into())) + * AB::ExprEF::from(aux_current[0].clone().into()) + - ((alpha.into() + beta_challenges[0].into()) + * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) + + AB::ExprEF::from(main_current[0].clone().into())) + * AB::ExprEF::from(aux_next[0].clone().into()), + ); + builder.when_transition().assert_zero_ext( + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) + * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) + * AB::ExprEF::from(aux_current[1].clone().into()) + + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) + * AB::ExprEF::from(main_current[0].clone().into()) + - ((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) + * (alpha.into() + + beta_challenges[0].into() + + beta_challenges[1].into().double()) + * AB::ExprEF::from(aux_next[1].clone().into()) + + (alpha.into() + + beta_challenges[0].into() + + beta_challenges[1].into().double()) + .double()), + ); + } +} + +impl Air for BusesAir { + fn eval(&self, builder: &mut AB) { + >::eval(self, builder); + } +} + diff --git a/air-script/tests/buses/buses_varlen_boundary_both.air b/air-script/src/tests/buses/buses_varlen_boundary_both.air similarity index 100% rename from air-script/tests/buses/buses_varlen_boundary_both.air rename to air-script/src/tests/buses/buses_varlen_boundary_both.air diff --git a/air-script/tests/buses/buses_varlen_boundary_both.rs b/air-script/src/tests/buses/buses_varlen_boundary_both.rs similarity index 100% rename from air-script/tests/buses/buses_varlen_boundary_both.rs rename to air-script/src/tests/buses/buses_varlen_boundary_both.rs diff --git a/air-script/src/tests/buses/buses_varlen_boundary_both_plonky3.rs b/air-script/src/tests/buses/buses_varlen_boundary_both_plonky3.rs new file mode 100644 index 000000000..13ed74cc7 --- /dev/null +++ b/air-script/src/tests/buses/buses_varlen_boundary_both_plonky3.rs @@ -0,0 +1,134 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 1; +pub const AUX_WIDTH: usize = 2; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 6; +pub const MAX_BETA_CHALLENGE_POWER: usize = 2; + +pub struct BusesAir; + +impl MidenAir for BusesAir +where F: Field, + EF: ExtensionField, +{ + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn num_randomness(&self) -> usize { + 1 + MAX_BETA_CHALLENGE_POWER + } + + fn aux_width(&self) -> usize { + AUX_WIDTH + } + + fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { + // Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders. + + let num_rows = _main.height(); + let trace_length = num_rows * AUX_WIDTH; + let mut long_trace = EF::zero_vec(trace_length); + let mut trace = RowMajorMatrix::new(long_trace, AUX_WIDTH); + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[EF; AUX_WIDTH]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + // Initialize first row + let initial_values = Self::buses_initial_values::(); + for j in 0..AUX_WIDTH { + rows[0][j] = initial_values[j]; + } + // Fill subsequent rows using direct access to the rows array + for i in 0..num_rows-1 { + let i_next = (i + 1) % num_rows; + let main_local = _main.row_slice(i).unwrap(); // i < height so unwrap should never fail. + let main_next = _main.row_slice(i_next).unwrap(); // i_next < height so unwrap should never fail. + let main = VerticalPair::new( + RowMajorMatrixView::new_row(&*main_local), + RowMajorMatrixView::new_row(&*main_next), + ); + let periodic_values: [_; NUM_PERIODIC_VALUES] = >::periodic_table(self).iter().map(|col| col[i % col.len()]).collect::>().try_into().expect("Wrong number of periodic values"); + let prev_row = &rows[i]; + let next_row = Self::buses_transitions::( + &main, + _challenges, + &periodic_values, + prev_row, + ); + for j in 0..AUX_WIDTH { + rows[i+1][j] = next_row[j]; + } + } + Some(trace) + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + let (&alpha, beta_challenges) = builder.permutation_randomness().split_first().expect("Wrong number of randomness"); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); + let aux_bus_boundary_values: [_; AUX_WIDTH] = builder.aux_bus_boundary_values().try_into().expect("Wrong number of aux bus boundary values"); + let aux = builder.permutation(); + let (aux_current, aux_next) = ( + aux.row_slice(0).unwrap(), + aux.row_slice(1).unwrap(), + ); + + // Main boundary constraints + + // Main integrity/transition constraints + + // Aux boundary constraints + builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - aux_bus_boundary_values[0].into()); + builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); + builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into()) - aux_bus_boundary_values[1].into()); + + // Aux integrity/transition constraints + builder.when_transition().assert_zero_ext(((alpha.into() + beta_challenges[0].into()) * AB::ExprEF::from(main_current[0].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into()) * (AB::ExprEF::from(main_current[0].clone().into()) - AB::ExprEF::ONE) + AB::ExprEF::ONE - (AB::ExprEF::from(main_current[0].clone().into()) - AB::ExprEF::ONE)) * AB::ExprEF::from(aux_next[0].clone().into())); + builder.when_transition().assert_zero_ext((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_current[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_next[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()).double())); + } +} + +impl BusesAir { + fn buses_initial_values() -> Vec + where F: Field, + EF: ExtensionField, + { + vec![ + EF::ZERO, + EF::ZERO, + ] + } + + fn buses_transitions(main: &VerticalPair, RowMajorMatrixView>, challenges: &[EF], periodic_evals: &[F], aux_current: &[EF]) -> Vec + where F: Field, + EF: ExtensionField, + { + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + let (&alpha, beta_challenges) = challenges.split_first().expect("Wrong number of randomness"); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = periodic_evals.try_into().expect("Wrong number of periodic values"); + vec![ + (((alpha + beta_challenges[0]) * EF::from(main_current[0].clone()) + EF::ONE - EF::from(main_current[0].clone())) * EF::from(aux_current[0].clone())) * ((alpha + beta_challenges[0]) * (EF::from(main_current[0].clone()) - EF::ONE) + EF::ONE - (EF::from(main_current[0].clone()) - EF::ONE)).inverse(), + ((alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(aux_current[1].clone()) + (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(main_current[0].clone()) + (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(main_current[0].clone()) - (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()).double()) * ((alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double())).inverse(), + ] + } +} \ No newline at end of file diff --git a/air-script/tests/buses/buses_varlen_boundary_first.air b/air-script/src/tests/buses/buses_varlen_boundary_first.air similarity index 100% rename from air-script/tests/buses/buses_varlen_boundary_first.air rename to air-script/src/tests/buses/buses_varlen_boundary_first.air diff --git a/air-script/tests/buses/buses_varlen_boundary_first.rs b/air-script/src/tests/buses/buses_varlen_boundary_first.rs similarity index 100% rename from air-script/tests/buses/buses_varlen_boundary_first.rs rename to air-script/src/tests/buses/buses_varlen_boundary_first.rs diff --git a/air-script/src/tests/buses/buses_varlen_boundary_first_plonky3.rs b/air-script/src/tests/buses/buses_varlen_boundary_first_plonky3.rs new file mode 100644 index 000000000..7a8c42bda --- /dev/null +++ b/air-script/src/tests/buses/buses_varlen_boundary_first_plonky3.rs @@ -0,0 +1,135 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 1; +pub const AUX_WIDTH: usize = 2; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 2; +pub const MAX_BETA_CHALLENGE_POWER: usize = 2; + +pub struct BusesAir; + +impl MidenAir for BusesAir +where F: Field, + EF: ExtensionField, +{ + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn num_randomness(&self) -> usize { + 1 + MAX_BETA_CHALLENGE_POWER + } + + fn aux_width(&self) -> usize { + AUX_WIDTH + } + + fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { + // Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders. + + let num_rows = _main.height(); + let trace_length = num_rows * AUX_WIDTH; + let mut long_trace = EF::zero_vec(trace_length); + let mut trace = RowMajorMatrix::new(long_trace, AUX_WIDTH); + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[EF; AUX_WIDTH]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + // Initialize first row + let initial_values = Self::buses_initial_values::(); + for j in 0..AUX_WIDTH { + rows[0][j] = initial_values[j]; + } + // Fill subsequent rows using direct access to the rows array + for i in 0..num_rows-1 { + let i_next = (i + 1) % num_rows; + let main_local = _main.row_slice(i).unwrap(); // i < height so unwrap should never fail. + let main_next = _main.row_slice(i_next).unwrap(); // i_next < height so unwrap should never fail. + let main = VerticalPair::new( + RowMajorMatrixView::new_row(&*main_local), + RowMajorMatrixView::new_row(&*main_next), + ); + let periodic_values: [_; NUM_PERIODIC_VALUES] = >::periodic_table(self).iter().map(|col| col[i % col.len()]).collect::>().try_into().expect("Wrong number of periodic values"); + let prev_row = &rows[i]; + let next_row = Self::buses_transitions::( + &main, + _challenges, + &periodic_values, + prev_row, + ); + for j in 0..AUX_WIDTH { + rows[i+1][j] = next_row[j]; + } + } + Some(trace) + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + let (&alpha, beta_challenges) = builder.permutation_randomness().split_first().expect("Wrong number of randomness"); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); + let aux_bus_boundary_values: [_; AUX_WIDTH] = builder.aux_bus_boundary_values().try_into().expect("Wrong number of aux bus boundary values"); + let aux = builder.permutation(); + let (aux_current, aux_next) = ( + aux.row_slice(0).unwrap(), + aux.row_slice(1).unwrap(), + ); + + // Main boundary constraints + + // Main integrity/transition constraints + + // Aux boundary constraints + builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - aux_bus_boundary_values[0].into()); + builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); + builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into()) - aux_bus_boundary_values[1].into()); + builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); + + // Aux integrity/transition constraints + builder.when_transition().assert_zero_ext(((alpha.into() + beta_challenges[0].into()) * AB::ExprEF::from(main_current[0].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into()) * (AB::ExprEF::from(main_current[0].clone().into()) - AB::ExprEF::ONE) + AB::ExprEF::ONE - (AB::ExprEF::from(main_current[0].clone().into()) - AB::ExprEF::ONE)) * AB::ExprEF::from(aux_next[0].clone().into())); + builder.when_transition().assert_zero_ext((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_current[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_next[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()).double())); + } +} + +impl BusesAir { + fn buses_initial_values() -> Vec + where F: Field, + EF: ExtensionField, + { + vec![ + EF::ZERO, + EF::ZERO, + ] + } + + fn buses_transitions(main: &VerticalPair, RowMajorMatrixView>, challenges: &[EF], periodic_evals: &[F], aux_current: &[EF]) -> Vec + where F: Field, + EF: ExtensionField, + { + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + let (&alpha, beta_challenges) = challenges.split_first().expect("Wrong number of randomness"); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = periodic_evals.try_into().expect("Wrong number of periodic values"); + vec![ + (((alpha + beta_challenges[0]) * EF::from(main_current[0].clone()) + EF::ONE - EF::from(main_current[0].clone())) * EF::from(aux_current[0].clone())) * ((alpha + beta_challenges[0]) * (EF::from(main_current[0].clone()) - EF::ONE) + EF::ONE - (EF::from(main_current[0].clone()) - EF::ONE)).inverse(), + ((alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(aux_current[1].clone()) + (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(main_current[0].clone()) + (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(main_current[0].clone()) - (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()).double()) * ((alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double())).inverse(), + ] + } +} \ No newline at end of file diff --git a/air-script/tests/buses/buses_varlen_boundary_last.air b/air-script/src/tests/buses/buses_varlen_boundary_last.air similarity index 100% rename from air-script/tests/buses/buses_varlen_boundary_last.air rename to air-script/src/tests/buses/buses_varlen_boundary_last.air diff --git a/air-script/tests/buses/buses_varlen_boundary_last.rs b/air-script/src/tests/buses/buses_varlen_boundary_last.rs similarity index 100% rename from air-script/tests/buses/buses_varlen_boundary_last.rs rename to air-script/src/tests/buses/buses_varlen_boundary_last.rs diff --git a/air-script/src/tests/buses/buses_varlen_boundary_last_plonky3.rs b/air-script/src/tests/buses/buses_varlen_boundary_last_plonky3.rs new file mode 100644 index 000000000..fa895389e --- /dev/null +++ b/air-script/src/tests/buses/buses_varlen_boundary_last_plonky3.rs @@ -0,0 +1,135 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 1; +pub const AUX_WIDTH: usize = 2; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 2; +pub const MAX_BETA_CHALLENGE_POWER: usize = 2; + +pub struct BusesAir; + +impl MidenAir for BusesAir +where F: Field, + EF: ExtensionField, +{ + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn num_randomness(&self) -> usize { + 1 + MAX_BETA_CHALLENGE_POWER + } + + fn aux_width(&self) -> usize { + AUX_WIDTH + } + + fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { + // Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders. + + let num_rows = _main.height(); + let trace_length = num_rows * AUX_WIDTH; + let mut long_trace = EF::zero_vec(trace_length); + let mut trace = RowMajorMatrix::new(long_trace, AUX_WIDTH); + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[EF; AUX_WIDTH]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + // Initialize first row + let initial_values = Self::buses_initial_values::(); + for j in 0..AUX_WIDTH { + rows[0][j] = initial_values[j]; + } + // Fill subsequent rows using direct access to the rows array + for i in 0..num_rows-1 { + let i_next = (i + 1) % num_rows; + let main_local = _main.row_slice(i).unwrap(); // i < height so unwrap should never fail. + let main_next = _main.row_slice(i_next).unwrap(); // i_next < height so unwrap should never fail. + let main = VerticalPair::new( + RowMajorMatrixView::new_row(&*main_local), + RowMajorMatrixView::new_row(&*main_next), + ); + let periodic_values: [_; NUM_PERIODIC_VALUES] = >::periodic_table(self).iter().map(|col| col[i % col.len()]).collect::>().try_into().expect("Wrong number of periodic values"); + let prev_row = &rows[i]; + let next_row = Self::buses_transitions::( + &main, + _challenges, + &periodic_values, + prev_row, + ); + for j in 0..AUX_WIDTH { + rows[i+1][j] = next_row[j]; + } + } + Some(trace) + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + let (&alpha, beta_challenges) = builder.permutation_randomness().split_first().expect("Wrong number of randomness"); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); + let aux_bus_boundary_values: [_; AUX_WIDTH] = builder.aux_bus_boundary_values().try_into().expect("Wrong number of aux bus boundary values"); + let aux = builder.permutation(); + let (aux_current, aux_next) = ( + aux.row_slice(0).unwrap(), + aux.row_slice(1).unwrap(), + ); + + // Main boundary constraints + + // Main integrity/transition constraints + + // Aux boundary constraints + builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); + builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - aux_bus_boundary_values[0].into()); + builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); + builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into()) - aux_bus_boundary_values[1].into()); + + // Aux integrity/transition constraints + builder.when_transition().assert_zero_ext(((alpha.into() + beta_challenges[0].into()) * AB::ExprEF::from(main_current[0].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into()) * (AB::ExprEF::from(main_current[0].clone().into()) - AB::ExprEF::ONE) + AB::ExprEF::ONE - (AB::ExprEF::from(main_current[0].clone().into()) - AB::ExprEF::ONE)) * AB::ExprEF::from(aux_next[0].clone().into())); + builder.when_transition().assert_zero_ext((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_current[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_next[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()).double())); + } +} + +impl BusesAir { + fn buses_initial_values() -> Vec + where F: Field, + EF: ExtensionField, + { + vec![ + EF::ONE, + EF::ZERO, + ] + } + + fn buses_transitions(main: &VerticalPair, RowMajorMatrixView>, challenges: &[EF], periodic_evals: &[F], aux_current: &[EF]) -> Vec + where F: Field, + EF: ExtensionField, + { + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + let (&alpha, beta_challenges) = challenges.split_first().expect("Wrong number of randomness"); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = periodic_evals.try_into().expect("Wrong number of periodic values"); + vec![ + (((alpha + beta_challenges[0]) * EF::from(main_current[0].clone()) + EF::ONE - EF::from(main_current[0].clone())) * EF::from(aux_current[0].clone())) * ((alpha + beta_challenges[0]) * (EF::from(main_current[0].clone()) - EF::ONE) + EF::ONE - (EF::from(main_current[0].clone()) - EF::ONE)).inverse(), + ((alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(aux_current[1].clone()) + (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(main_current[0].clone()) + (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(main_current[0].clone()) - (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()).double()) * ((alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double())).inverse(), + ] + } +} \ No newline at end of file diff --git a/air-script/tests/buses/mod.rs b/air-script/src/tests/buses/mod.rs similarity index 100% rename from air-script/tests/buses/mod.rs rename to air-script/src/tests/buses/mod.rs diff --git a/air-script/src/tests/buses/test_air_plonky3.rs b/air-script/src/tests/buses/test_air_plonky3.rs new file mode 100644 index 000000000..20919a31e --- /dev/null +++ b/air-script/src/tests/buses/test_air_plonky3.rs @@ -0,0 +1,59 @@ +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; + +use crate::{ + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::buses::buses_complex_plonky3::{BusesAir, MAIN_WIDTH}, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * MAIN_WIDTH; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::ZERO; + rows[0][2] = F::ZERO; + rows[0][3] = F::ZERO; + rows[0][4] = F::ZERO; + rows[0][5] = F::ZERO; + rows[0][6] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let a_prev = rows[i - 1][0]; + let b_prev = rows[i - 1][1]; + let c_prev = rows[i - 1][2]; + let d_prev = rows[i - 1][3]; + let e_prev = rows[i - 1][4]; + let f_prev = rows[i - 1][5]; + let g_prev = rows[i - 1][6]; + + // Update current row based on previous values + rows[i][0] = F::ZERO; + rows[i][1] = F::ZERO; + rows[i][2] = if i > 3 && i < 8 { F::ONE } else { F::ZERO }; // s1 is true 4 times + rows[i][3] = if i > 5 && i < 10 { F::ONE } else { F::ZERO }; // s2 is true 4 times + rows[i][4] = if i > 4 && i < 10 { F::ONE } else { F::ZERO }; // s3 is true 5 times + rows[i][5] = if i > 5 && i < 13 { F::ONE } else { F::ZERO }; // s4 is true 7 times + rows[i][6] = if i > 15 && i < 20 { F::from_u64(3) } else { F::ZERO }; // d is set to 3 four times + } + + trace +} + +fn generate_inputs() -> Vec { + vec![1; 2] +} + +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, BusesAir); diff --git a/air-script/tests/buses/test_air_winterfell.rs b/air-script/src/tests/buses/test_air_winterfell.rs similarity index 84% rename from air-script/tests/buses/test_air_winterfell.rs rename to air-script/src/tests/buses/test_air_winterfell.rs index 7bf2ce5ea..150617152 100644 --- a/air-script/tests/buses/test_air_winterfell.rs +++ b/air-script/src/tests/buses/test_air_winterfell.rs @@ -3,9 +3,9 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{AuxTraceWithMetadata, Trace, TraceTable, matrix::ColMatrix}; use crate::{ - buses::buses_complex::PublicInputs, generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::buses::buses_complex::PublicInputs, }; #[derive(Clone)] @@ -15,7 +15,7 @@ impl AirTester for BusesAirTester { type PubInputs = PublicInputs; fn build_main_trace(&self, length: usize) -> MyTraceTable { - let trace_width = 5; + let trace_width = 7; let start = Felt::new(0); let mut trace = TraceTable::new(trace_width, length); @@ -26,6 +26,8 @@ impl AirTester for BusesAirTester { state[2] = start; state[3] = start; state[4] = start; + state[5] = start; + state[6] = start; }, |_, state| { state[0] = Felt::new(1) - state[0]; @@ -33,6 +35,8 @@ impl AirTester for BusesAirTester { state[2] = Felt::new(1) - state[2]; state[3] = Felt::new(1) - state[3]; state[4] = Felt::new(1) - state[4]; + state[5] = Felt::new(1) - state[4]; + state[6] = Felt::new(1) - state[4]; }, ); @@ -59,7 +63,7 @@ impl AirTester for BusesAirTester { generate_air_winterfell_test!( test_buses_air, - crate::buses::buses_complex::BusesAir, + crate::tests::buses::buses_complex::BusesAir, BusesAirTester, 1024 ); diff --git a/air-script/tests/computed_indices/computed_indices_complex.air b/air-script/src/tests/computed_indices/computed_indices_complex.air similarity index 100% rename from air-script/tests/computed_indices/computed_indices_complex.air rename to air-script/src/tests/computed_indices/computed_indices_complex.air diff --git a/air-script/tests/computed_indices/computed_indices_complex.rs b/air-script/src/tests/computed_indices/computed_indices_complex.rs similarity index 100% rename from air-script/tests/computed_indices/computed_indices_complex.rs rename to air-script/src/tests/computed_indices/computed_indices_complex.rs diff --git a/air-script/src/tests/computed_indices/computed_indices_complex_plonky3.rs b/air-script/src/tests/computed_indices/computed_indices_complex_plonky3.rs new file mode 100644 index 000000000..ae3ee2903 --- /dev/null +++ b/air-script/src/tests/computed_indices/computed_indices_complex_plonky3.rs @@ -0,0 +1,43 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 4; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 1; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct ComputedIndicesAir; + +impl MidenAir for ComputedIndicesAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[2].clone().into() * AB::Expr::from_u64(3) + main_current[3].clone().into() * AB::Expr::from_u64(4)); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/computed_indices/computed_indices_simple.air b/air-script/src/tests/computed_indices/computed_indices_simple.air similarity index 100% rename from air-script/tests/computed_indices/computed_indices_simple.air rename to air-script/src/tests/computed_indices/computed_indices_simple.air diff --git a/air-script/tests/computed_indices/computed_indices_simple.rs b/air-script/src/tests/computed_indices/computed_indices_simple.rs similarity index 100% rename from air-script/tests/computed_indices/computed_indices_simple.rs rename to air-script/src/tests/computed_indices/computed_indices_simple.rs diff --git a/air-script/src/tests/computed_indices/computed_indices_simple_plonky3.rs b/air-script/src/tests/computed_indices/computed_indices_simple_plonky3.rs new file mode 100644 index 000000000..40c374ad3 --- /dev/null +++ b/air-script/src/tests/computed_indices/computed_indices_simple_plonky3.rs @@ -0,0 +1,50 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 8; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct ComputedIndicesAir; + +impl MidenAir for ComputedIndicesAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[0].clone().into()); + builder.assert_zero(main_current[1].clone().into() - AB::Expr::from_u64(2)); + builder.assert_zero(main_current[2].clone().into() - AB::Expr::from_u64(4)); + builder.assert_zero(main_current[3].clone().into() - AB::Expr::from_u64(6)); + builder.when_transition().assert_zero(main_next[4].clone().into()); + builder.when_transition().assert_zero(main_next[5].clone().into() - main_current[5].clone().into().double()); + builder.when_transition().assert_zero(main_next[6].clone().into() - AB::Expr::from_u64(6) * main_current[6].clone().into()); + builder.when_transition().assert_zero(main_next[7].clone().into() - AB::Expr::from_u64(12) * main_current[7].clone().into()); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/src/tests/computed_indices/mod.rs b/air-script/src/tests/computed_indices/mod.rs new file mode 100644 index 000000000..948afee49 --- /dev/null +++ b/air-script/src/tests/computed_indices/mod.rs @@ -0,0 +1,18 @@ +#[rustfmt::skip] +#[allow(clippy::all)] +mod computed_indices_complex; +#[rustfmt::skip] +#[allow(clippy::all)] +mod computed_indices_simple; + +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod computed_indices_complex_plonky3; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod computed_indices_simple_plonky3; + +mod test_air_plonky3; +mod test_air_winterfell; diff --git a/air-script/src/tests/computed_indices/test_air_plonky3.rs b/air-script/src/tests/computed_indices/test_air_plonky3.rs new file mode 100644 index 000000000..5f02cc466 --- /dev/null +++ b/air-script/src/tests/computed_indices/test_air_plonky3.rs @@ -0,0 +1,62 @@ +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; + +use crate::{ + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::computed_indices::computed_indices_simple_plonky3::{ComputedIndicesAir, MAIN_WIDTH}, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * MAIN_WIDTH; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ZERO; + rows[0][1] = F::from_canonical_checked(2).unwrap(); + rows[0][2] = F::from_canonical_checked(4).unwrap(); + rows[0][3] = F::from_canonical_checked(6).unwrap(); + rows[0][4] = F::ZERO; + rows[0][5] = F::ZERO; + rows[0][6] = F::ZERO; + rows[0][7] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let col_0_prev = rows[i - 1][0]; + let col_1_prev = rows[i - 1][1]; + let col_2_prev = rows[i - 1][2]; + let col_3_prev = rows[i - 1][3]; + let col_4_prev = rows[i - 1][4]; + let col_5_prev = rows[i - 1][5]; + let col_6_prev = rows[i - 1][6]; + let col_7_prev = rows[i - 1][7]; + + // Update current row based on previous values + rows[i][0] = col_0_prev; + rows[i][1] = col_1_prev; + rows[i][2] = col_2_prev; + rows[i][3] = col_3_prev; + rows[i][4] = col_4_prev * F::ZERO; + rows[i][5] = col_5_prev * F::from_canonical_checked(2).unwrap(); + rows[i][6] = col_6_prev * F::from_canonical_checked(6).unwrap(); + rows[i][7] = col_7_prev * F::from_canonical_checked(12).unwrap(); + } + + trace +} + +fn generate_inputs() -> Vec { + vec![1; 16] +} + +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, ComputedIndicesAir); diff --git a/air-script/tests/computed_indices/test_air.rs b/air-script/src/tests/computed_indices/test_air_winterfell.rs similarity index 63% rename from air-script/tests/computed_indices/test_air.rs rename to air-script/src/tests/computed_indices/test_air_winterfell.rs index 5a953f6e5..1f7e35ed3 100644 --- a/air-script/tests/computed_indices/test_air.rs +++ b/air-script/src/tests/computed_indices/test_air_winterfell.rs @@ -3,8 +3,9 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - computed_indices::computed_indices_simple::{ComputedIndicesAir, PublicInputs}, - helpers::{AirTester, MyTraceTable}, + generate_air_winterfell_test, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::computed_indices::computed_indices_simple::PublicInputs, }; #[derive(Clone)] @@ -45,17 +46,9 @@ impl AirTester for ComputedIndicesAirTester { } } -#[test] -fn test_computed_indices_air() { - let air_tester = Box::new(ComputedIndicesAirTester {}); - let length = 1024; - - let main_trace = air_tester.build_main_trace(length); - let aux_trace = air_tester.build_aux_trace(length); - let pub_inputs = air_tester.public_inputs(); - let trace_info = air_tester.build_trace_info(length); - let options = air_tester.build_proof_options(); - - let air = ComputedIndicesAir::new(trace_info, pub_inputs, options); - main_trace.validate::(&air, aux_trace.as_ref()); -} +generate_air_winterfell_test!( + test_computed_indices_air, + crate::tests::computed_indices::computed_indices_simple::ComputedIndicesAir, + ComputedIndicesAirTester, + 1024 +); diff --git a/air-script/tests/constant_in_range/constant_in_range.air b/air-script/src/tests/constant_in_range/constant_in_range.air similarity index 100% rename from air-script/tests/constant_in_range/constant_in_range.air rename to air-script/src/tests/constant_in_range/constant_in_range.air diff --git a/air-script/tests/constant_in_range/constant_in_range.rs b/air-script/src/tests/constant_in_range/constant_in_range.rs similarity index 100% rename from air-script/tests/constant_in_range/constant_in_range.rs rename to air-script/src/tests/constant_in_range/constant_in_range.rs diff --git a/air-script/tests/constant_in_range/constant_in_range_module.air b/air-script/src/tests/constant_in_range/constant_in_range_module.air similarity index 100% rename from air-script/tests/constant_in_range/constant_in_range_module.air rename to air-script/src/tests/constant_in_range/constant_in_range_module.air diff --git a/air-script/src/tests/constant_in_range/constant_in_range_plonky3.rs b/air-script/src/tests/constant_in_range/constant_in_range_plonky3.rs new file mode 100644 index 000000000..b1238f7cc --- /dev/null +++ b/air-script/src/tests/constant_in_range/constant_in_range_plonky3.rs @@ -0,0 +1,43 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 12; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct ConstantInRangeAir; + +impl MidenAir for ConstantInRangeAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[6].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[0].clone().into() - (main_current[1].clone().into() - main_current[4].clone().into() - main_current[8].clone().into() + AB::Expr::ONE + main_current[2].clone().into() - main_current[5].clone().into() - main_current[9].clone().into() + AB::Expr::from_u64(2) + main_current[3].clone().into() - main_current[6].clone().into() - main_current[10].clone().into())); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/constant_in_range/mod.rs b/air-script/src/tests/constant_in_range/mod.rs similarity index 100% rename from air-script/tests/constant_in_range/mod.rs rename to air-script/src/tests/constant_in_range/mod.rs diff --git a/air-script/tests/constant_in_range/test_air_plonky3.rs b/air-script/src/tests/constant_in_range/test_air_plonky3.rs similarity index 64% rename from air-script/tests/constant_in_range/test_air_plonky3.rs rename to air-script/src/tests/constant_in_range/test_air_plonky3.rs index 56e1dcb11..9dd399e1c 100644 --- a/air-script/tests/constant_in_range/test_air_plonky3.rs +++ b/air-script/src/tests/constant_in_range/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - constant_in_range::constant_in_range_plonky3::{ConstantInRangeAir, NUM_COLUMNS}, - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::constant_in_range::constant_in_range_plonky3::{ConstantInRangeAir, MAIN_WIDTH}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -71,4 +60,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -generate_air_plonky3_test!(test_air_plonky3, ConstantInRangeAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, ConstantInRangeAir); diff --git a/air-script/tests/constant_in_range/test_air_winterfell.rs b/air-script/src/tests/constant_in_range/test_air_winterfell.rs similarity index 86% rename from air-script/tests/constant_in_range/test_air_winterfell.rs rename to air-script/src/tests/constant_in_range/test_air_winterfell.rs index 3672378c8..c08fa18ab 100644 --- a/air-script/tests/constant_in_range/test_air_winterfell.rs +++ b/air-script/src/tests/constant_in_range/test_air_winterfell.rs @@ -3,9 +3,9 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - constant_in_range::constant_in_range::PublicInputs, generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::constant_in_range::constant_in_range::PublicInputs, }; #[derive(Clone)] @@ -48,7 +48,7 @@ impl AirTester for ConstantInRangeAirTester { generate_air_winterfell_test!( test_constant_in_range_air, - crate::constant_in_range::constant_in_range::ConstantInRangeAir, + crate::tests::constant_in_range::constant_in_range::ConstantInRangeAir, ConstantInRangeAirTester, 1024 ); diff --git a/air-script/tests/constants/constants.air b/air-script/src/tests/constants/constants.air similarity index 100% rename from air-script/tests/constants/constants.air rename to air-script/src/tests/constants/constants.air diff --git a/air-script/tests/constants/constants.rs b/air-script/src/tests/constants/constants.rs similarity index 100% rename from air-script/tests/constants/constants.rs rename to air-script/src/tests/constants/constants.rs diff --git a/air-script/src/tests/constants/constants_plonky3.rs b/air-script/src/tests/constants/constants_plonky3.rs new file mode 100644 index 000000000..632331aab --- /dev/null +++ b/air-script/src/tests/constants/constants_plonky3.rs @@ -0,0 +1,52 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 7; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 32; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct ConstantsAir; + +impl MidenAir for ConstantsAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into() - AB::Expr::ONE); + builder.when_first_row().assert_zero(main_current[1].clone().into() - AB::Expr::ONE); + builder.when_first_row().assert_zero(main_current[2].clone().into()); + builder.when_first_row().assert_zero(main_current[3].clone().into() - AB::Expr::ONE); + builder.when_first_row().assert_zero(main_current[4].clone().into() - AB::Expr::ONE); + builder.when_last_row().assert_zero(main_current[6].clone().into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero(main_next[0].clone().into() - (main_current[0].clone().into() + AB::Expr::ONE)); + builder.when_transition().assert_zero(main_next[1].clone().into()); + builder.when_transition().assert_zero(main_next[2].clone().into() - main_current[2].clone().into()); + builder.when_transition().assert_zero(main_next[5].clone().into() - (main_current[5].clone().into() + AB::Expr::ONE)); + builder.assert_zero(main_current[4].clone().into() - AB::Expr::ONE); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/constants/mod.rs b/air-script/src/tests/constants/mod.rs similarity index 100% rename from air-script/tests/constants/mod.rs rename to air-script/src/tests/constants/mod.rs diff --git a/air-script/tests/constants/test_air_plonky3.rs b/air-script/src/tests/constants/test_air_plonky3.rs similarity index 61% rename from air-script/tests/constants/test_air_plonky3.rs rename to air-script/src/tests/constants/test_air_plonky3.rs index 6eecb26fa..cf2c54ebd 100644 --- a/air-script/tests/constants/test_air_plonky3.rs +++ b/air-script/src/tests/constants/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - constants::constants_plonky3::{ConstantsAir, NUM_COLUMNS}, - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::constants::constants_plonky3::{ConstantsAir, MAIN_WIDTH}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -67,4 +56,4 @@ fn generate_inputs() -> Vec { vec![1; 32] } -generate_air_plonky3_test!(test_air_plonky3, ConstantsAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, ConstantsAir); diff --git a/air-script/tests/constants/test_air_winterfell.rs b/air-script/src/tests/constants/test_air_winterfell.rs similarity index 88% rename from air-script/tests/constants/test_air_winterfell.rs rename to air-script/src/tests/constants/test_air_winterfell.rs index 9459000d7..926c8c77c 100644 --- a/air-script/tests/constants/test_air_winterfell.rs +++ b/air-script/src/tests/constants/test_air_winterfell.rs @@ -3,9 +3,9 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - constants::constants::PublicInputs, generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::constants::constants::PublicInputs, }; #[derive(Clone)] @@ -46,7 +46,7 @@ impl AirTester for ConstantsAirTester { generate_air_winterfell_test!( test_constants_air, - crate::constants::constants::ConstantsAir, + crate::tests::constants::constants::ConstantsAir, ConstantsAirTester, 1024 ); diff --git a/air-script/tests/constraint_comprehension/cc_with_evaluators.air b/air-script/src/tests/constraint_comprehension/cc_with_evaluators.air similarity index 100% rename from air-script/tests/constraint_comprehension/cc_with_evaluators.air rename to air-script/src/tests/constraint_comprehension/cc_with_evaluators.air diff --git a/air-script/tests/constraint_comprehension/constraint_comprehension.air b/air-script/src/tests/constraint_comprehension/constraint_comprehension.air similarity index 100% rename from air-script/tests/constraint_comprehension/constraint_comprehension.air rename to air-script/src/tests/constraint_comprehension/constraint_comprehension.air diff --git a/air-script/tests/constraint_comprehension/constraint_comprehension.rs b/air-script/src/tests/constraint_comprehension/constraint_comprehension.rs similarity index 100% rename from air-script/tests/constraint_comprehension/constraint_comprehension.rs rename to air-script/src/tests/constraint_comprehension/constraint_comprehension.rs diff --git a/air-script/src/tests/constraint_comprehension/constraint_comprehension_plonky3.rs b/air-script/src/tests/constraint_comprehension/constraint_comprehension_plonky3.rs new file mode 100644 index 000000000..e333b9626 --- /dev/null +++ b/air-script/src/tests/constraint_comprehension/constraint_comprehension_plonky3.rs @@ -0,0 +1,46 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 14; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct ConstraintComprehensionAir; + +impl MidenAir for ConstraintComprehensionAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[8].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[6].clone().into() - main_current[10].clone().into()); + builder.assert_zero(main_current[7].clone().into() - main_current[11].clone().into()); + builder.assert_zero(main_current[8].clone().into() - main_current[12].clone().into()); + builder.assert_zero(main_current[9].clone().into() - main_current[13].clone().into()); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/constraint_comprehension/mod.rs b/air-script/src/tests/constraint_comprehension/mod.rs similarity index 100% rename from air-script/tests/constraint_comprehension/mod.rs rename to air-script/src/tests/constraint_comprehension/mod.rs diff --git a/air-script/src/tests/constraint_comprehension/test_air_plonky3.rs b/air-script/src/tests/constraint_comprehension/test_air_plonky3.rs new file mode 100644 index 000000000..f9fa0d39c --- /dev/null +++ b/air-script/src/tests/constraint_comprehension/test_air_plonky3.rs @@ -0,0 +1,46 @@ +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; + +use crate::{ + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::constraint_comprehension::constraint_comprehension_plonky3::{ + ConstraintComprehensionAir, MAIN_WIDTH, + }, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 32; + let trace_length = num_rows * MAIN_WIDTH; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::from_canonical_checked(inputs[0]).unwrap(); + rows[0][1] = F::ONE; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let a_prev = rows[i - 1][0]; + let b_prev = rows[i - 1][1]; + + // Update current row based on previous values + rows[i][0] = F::ONE - a_prev; + rows[i][1] = F::ONE - b_prev; + } + + trace +} + +fn generate_inputs() -> Vec { + vec![1; 16] +} + +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, ConstraintComprehensionAir); diff --git a/air-script/tests/constraint_comprehension/test_air_winterfell.rs b/air-script/src/tests/constraint_comprehension/test_air_winterfell.rs similarity index 85% rename from air-script/tests/constraint_comprehension/test_air_winterfell.rs rename to air-script/src/tests/constraint_comprehension/test_air_winterfell.rs index 73a24bf0f..d4bec18d1 100644 --- a/air-script/tests/constraint_comprehension/test_air_winterfell.rs +++ b/air-script/src/tests/constraint_comprehension/test_air_winterfell.rs @@ -3,9 +3,9 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - constraint_comprehension::constraint_comprehension::PublicInputs, generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::constraint_comprehension::constraint_comprehension::PublicInputs, }; #[derive(Clone)] @@ -50,7 +50,7 @@ impl AirTester for ConstraintComprehensionAirTester { generate_air_winterfell_test!( test_constraint_comprehension_air, - crate::constraint_comprehension::constraint_comprehension::ConstraintComprehensionAir, + crate::tests::constraint_comprehension::constraint_comprehension::ConstraintComprehensionAir, ConstraintComprehensionAirTester, 1024 ); diff --git a/air-script/tests/docs_sync.rs b/air-script/src/tests/docs_sync.rs similarity index 100% rename from air-script/tests/docs_sync.rs rename to air-script/src/tests/docs_sync.rs diff --git a/air-script/tests/evaluators/evaluators.air b/air-script/src/tests/evaluators/evaluators.air similarity index 100% rename from air-script/tests/evaluators/evaluators.air rename to air-script/src/tests/evaluators/evaluators.air diff --git a/air-script/tests/evaluators/evaluators.rs b/air-script/src/tests/evaluators/evaluators.rs similarity index 100% rename from air-script/tests/evaluators/evaluators.rs rename to air-script/src/tests/evaluators/evaluators.rs diff --git a/air-script/src/tests/evaluators/evaluators_plonky3.rs b/air-script/src/tests/evaluators/evaluators_plonky3.rs new file mode 100644 index 000000000..07f19ea21 --- /dev/null +++ b/air-script/src/tests/evaluators/evaluators_plonky3.rs @@ -0,0 +1,52 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 7; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct EvaluatorsAir; + +impl MidenAir for EvaluatorsAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero(main_next[0].clone().into() - main_current[0].clone().into()); + builder.when_transition().assert_zero(main_next[2].clone().into() - main_current[2].clone().into()); + builder.when_transition().assert_zero(main_next[6].clone().into() - main_current[6].clone().into()); + builder.assert_zero(main_current[0].clone().into() * main_current[0].clone().into() - main_current[0].clone().into()); + builder.assert_zero(main_current[1].clone().into() * main_current[1].clone().into() - main_current[1].clone().into()); + builder.assert_zero(main_current[2].clone().into() * main_current[2].clone().into() - main_current[2].clone().into()); + builder.assert_zero(main_current[3].clone().into() * main_current[3].clone().into() - main_current[3].clone().into()); + builder.assert_zero(main_current[4].clone().into()); + builder.assert_zero(main_current[5].clone().into() - AB::Expr::ONE); + builder.assert_zero(main_current[6].clone().into() - AB::Expr::from_u64(4)); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/evaluators/mod.rs b/air-script/src/tests/evaluators/mod.rs similarity index 100% rename from air-script/tests/evaluators/mod.rs rename to air-script/src/tests/evaluators/mod.rs diff --git a/air-script/tests/evaluators/test_air_plonky3.rs b/air-script/src/tests/evaluators/test_air_plonky3.rs similarity index 61% rename from air-script/tests/evaluators/test_air_plonky3.rs rename to air-script/src/tests/evaluators/test_air_plonky3.rs index 7fed6ebc2..050ba860a 100644 --- a/air-script/tests/evaluators/test_air_plonky3.rs +++ b/air-script/src/tests/evaluators/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - evaluators::evaluators_plonky3::{EvaluatorsAir, NUM_COLUMNS}, - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::evaluators::evaluators_plonky3::{EvaluatorsAir, MAIN_WIDTH}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -67,4 +56,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -generate_air_plonky3_test!(test_air_plonky3, EvaluatorsAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, EvaluatorsAir); diff --git a/air-script/tests/evaluators/test_air_winterfell.rs b/air-script/src/tests/evaluators/test_air_winterfell.rs similarity index 86% rename from air-script/tests/evaluators/test_air_winterfell.rs rename to air-script/src/tests/evaluators/test_air_winterfell.rs index fa8f6f6d8..bbaeb5ecd 100644 --- a/air-script/tests/evaluators/test_air_winterfell.rs +++ b/air-script/src/tests/evaluators/test_air_winterfell.rs @@ -3,9 +3,9 @@ use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use winterfell::{Trace, TraceTable}; use crate::{ - evaluators::evaluators::PublicInputs, generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::evaluators::evaluators::PublicInputs, }; #[derive(Clone)] @@ -43,7 +43,7 @@ impl AirTester for EvaluatorsAirTester { generate_air_winterfell_test!( test_evaluators_air, - crate::evaluators::evaluators::EvaluatorsAir, + crate::tests::evaluators::evaluators::EvaluatorsAir, EvaluatorsAirTester, 1024 ); diff --git a/air-script/tests/fibonacci/fibonacci.air b/air-script/src/tests/fibonacci/fibonacci.air similarity index 100% rename from air-script/tests/fibonacci/fibonacci.air rename to air-script/src/tests/fibonacci/fibonacci.air diff --git a/air-script/tests/fibonacci/fibonacci.rs b/air-script/src/tests/fibonacci/fibonacci.rs similarity index 100% rename from air-script/tests/fibonacci/fibonacci.rs rename to air-script/src/tests/fibonacci/fibonacci.rs diff --git a/air-script/src/tests/fibonacci/fibonacci_plonky3.rs b/air-script/src/tests/fibonacci/fibonacci_plonky3.rs new file mode 100644 index 000000000..b5476e071 --- /dev/null +++ b/air-script/src/tests/fibonacci/fibonacci_plonky3.rs @@ -0,0 +1,46 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 2; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 3; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct FibonacciAir; + +impl MidenAir for FibonacciAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into() - public_values[0].into()); + builder.when_first_row().assert_zero(main_current[1].clone().into() - public_values[1].into()); + builder.when_last_row().assert_zero(main_current[1].clone().into() - public_values[2].into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero(main_next[1].clone().into() - (main_current[0].clone().into() + main_current[1].clone().into())); + builder.when_transition().assert_zero(main_next[0].clone().into() - main_current[1].clone().into()); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/fibonacci/mod.rs b/air-script/src/tests/fibonacci/mod.rs similarity index 100% rename from air-script/tests/fibonacci/mod.rs rename to air-script/src/tests/fibonacci/mod.rs diff --git a/air-script/tests/fibonacci/test_air_plonky3.rs b/air-script/src/tests/fibonacci/test_air_plonky3.rs similarity index 53% rename from air-script/tests/fibonacci/test_air_plonky3.rs rename to air-script/src/tests/fibonacci/test_air_plonky3.rs index 6a01a91b6..1d76e60d2 100644 --- a/air-script/tests/fibonacci/test_air_plonky3.rs +++ b/air-script/src/tests/fibonacci/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - fibonacci::fibonacci_plonky3::{FibonacciAir, NUM_COLUMNS}, - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::fibonacci::fibonacci_plonky3::{FibonacciAir, MAIN_WIDTH}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 31; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -54,4 +43,4 @@ fn generate_inputs() -> Vec { vec![one, one, last] } -generate_air_plonky3_test!(test_air_plonky3, FibonacciAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, FibonacciAir); diff --git a/air-script/tests/fibonacci/test_air_winterfell.rs b/air-script/src/tests/fibonacci/test_air_winterfell.rs similarity index 88% rename from air-script/tests/fibonacci/test_air_winterfell.rs rename to air-script/src/tests/fibonacci/test_air_winterfell.rs index 5839643d7..9d2313823 100644 --- a/air-script/tests/fibonacci/test_air_winterfell.rs +++ b/air-script/src/tests/fibonacci/test_air_winterfell.rs @@ -5,9 +5,9 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{AuxTraceWithMetadata, Trace, TraceTable, matrix::ColMatrix}; use crate::{ - fibonacci::fibonacci::PublicInputs, generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::fibonacci::fibonacci::PublicInputs, }; #[derive(Clone)] @@ -47,7 +47,7 @@ impl AirTester for FibonacciAirTester { generate_air_winterfell_test!( test_fibonacci_air, - crate::fibonacci::fibonacci::FibonacciAir, + crate::tests::fibonacci::fibonacci::FibonacciAir, FibonacciAirTester, 32 ); diff --git a/air-script/tests/functions/functions_complex.air b/air-script/src/tests/functions/functions_complex.air similarity index 100% rename from air-script/tests/functions/functions_complex.air rename to air-script/src/tests/functions/functions_complex.air diff --git a/air-script/tests/functions/functions_complex.rs b/air-script/src/tests/functions/functions_complex.rs similarity index 100% rename from air-script/tests/functions/functions_complex.rs rename to air-script/src/tests/functions/functions_complex.rs diff --git a/air-script/src/tests/functions/functions_complex_plonky3.rs b/air-script/src/tests/functions/functions_complex_plonky3.rs new file mode 100644 index 000000000..b42205283 --- /dev/null +++ b/air-script/src/tests/functions/functions_complex_plonky3.rs @@ -0,0 +1,44 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 17; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct FunctionsAir; + +impl MidenAir for FunctionsAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[3].clone().into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero(main_next[16].clone().into() - main_current[16].clone().into() * ((main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[1].clone().into() * main_current[2].clone().into() + main_current[3].clone().into() * main_current[3].clone().into() * (AB::Expr::ONE - main_current[1].clone().into()) * main_current[2].clone().into() + main_current[3].clone().into() * main_current[1].clone().into() * (AB::Expr::ONE - main_current[2].clone().into()) + (AB::Expr::ONE - main_current[1].clone().into()) * (AB::Expr::ONE - main_current[2].clone().into())) * main_current[0].clone().into() - main_current[0].clone().into() + AB::Expr::ONE)); + builder.when_transition().assert_zero(main_next[3].clone().into() - (main_current[4].clone().into() + main_current[5].clone().into() + main_current[6].clone().into() + main_current[7].clone().into() + main_current[8].clone().into() + main_current[9].clone().into() + main_current[10].clone().into() + main_current[11].clone().into() + main_current[12].clone().into() + main_current[13].clone().into() + main_current[14].clone().into() + main_current[15].clone().into() + AB::Expr::ONE).double()); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/functions/functions_simple.air b/air-script/src/tests/functions/functions_simple.air similarity index 100% rename from air-script/tests/functions/functions_simple.air rename to air-script/src/tests/functions/functions_simple.air diff --git a/air-script/tests/functions/functions_simple.rs b/air-script/src/tests/functions/functions_simple.rs similarity index 100% rename from air-script/tests/functions/functions_simple.rs rename to air-script/src/tests/functions/functions_simple.rs diff --git a/air-script/src/tests/functions/functions_simple_plonky3.rs b/air-script/src/tests/functions/functions_simple_plonky3.rs new file mode 100644 index 000000000..c5b131f49 --- /dev/null +++ b/air-script/src/tests/functions/functions_simple_plonky3.rs @@ -0,0 +1,50 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 9; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct FunctionsAir; + +impl MidenAir for FunctionsAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[3].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[0].clone().into() * main_current[3].clone().into() - AB::Expr::ONE); + builder.assert_zero(main_current[4].clone().into() * main_current[5].clone().into() * main_current[6].clone().into() * main_current[7].clone().into() * main_current[3].clone().into() - AB::Expr::ONE); + builder.assert_zero((main_current[4].clone().into() + main_current[5].clone().into() + main_current[6].clone().into() + main_current[7].clone().into()) * main_current[4].clone().into() * main_current[5].clone().into() * main_current[6].clone().into() * main_current[7].clone().into() - AB::Expr::ONE); + builder.assert_zero(main_current[4].clone().into() * main_current[5].clone().into() * main_current[6].clone().into() * main_current[7].clone().into() - AB::Expr::ONE); + builder.assert_zero(main_current[0].clone().into() * main_current[4].clone().into() * main_current[5].clone().into() * main_current[6].clone().into() * main_current[7].clone().into() - AB::Expr::ONE); + builder.assert_zero(main_current[1].clone().into() + (main_current[4].clone().into() + main_current[5].clone().into() + main_current[6].clone().into() + main_current[7].clone().into()) * main_current[4].clone().into() * main_current[5].clone().into() * main_current[6].clone().into() * main_current[7].clone().into() - AB::Expr::ONE); + builder.assert_zero(main_current[4].clone().into() + main_current[5].clone().into() + main_current[6].clone().into() + main_current[7].clone().into() - AB::Expr::ONE); + builder.assert_zero((main_current[4].clone().into() + main_current[5].clone().into() + main_current[6].clone().into() + main_current[7].clone().into()) * AB::Expr::from_u64(4) - AB::Expr::ONE); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/functions/inlined_functions_simple.air b/air-script/src/tests/functions/inlined_functions_simple.air similarity index 100% rename from air-script/tests/functions/inlined_functions_simple.air rename to air-script/src/tests/functions/inlined_functions_simple.air diff --git a/air-script/tests/functions/mod.rs b/air-script/src/tests/functions/mod.rs similarity index 100% rename from air-script/tests/functions/mod.rs rename to air-script/src/tests/functions/mod.rs diff --git a/air-script/tests/functions/test_air_plonky3.rs b/air-script/src/tests/functions/test_air_plonky3.rs similarity index 73% rename from air-script/tests/functions/test_air_plonky3.rs rename to air-script/src/tests/functions/test_air_plonky3.rs index 5ef13dbf9..f52390339 100644 --- a/air-script/tests/functions/test_air_plonky3.rs +++ b/air-script/src/tests/functions/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - functions::functions_complex_plonky3::{FunctionsAir, NUM_COLUMNS}, - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::functions::functions_complex_plonky3::{FunctionsAir, MAIN_WIDTH}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -97,4 +86,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -generate_air_plonky3_test!(test_air_plonky3, FunctionsAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, FunctionsAir); diff --git a/air-script/tests/functions/test_air_winterfell.rs b/air-script/src/tests/functions/test_air_winterfell.rs similarity index 92% rename from air-script/tests/functions/test_air_winterfell.rs rename to air-script/src/tests/functions/test_air_winterfell.rs index 0357e9039..76aa0b6f3 100644 --- a/air-script/tests/functions/test_air_winterfell.rs +++ b/air-script/src/tests/functions/test_air_winterfell.rs @@ -3,9 +3,9 @@ use winter_math::fields::f64::BaseElement as Felt; use winterfell::{Trace, TraceTable}; use crate::{ - functions::functions_complex::PublicInputs, generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::functions::functions_complex::PublicInputs, }; #[derive(Clone)] @@ -70,7 +70,7 @@ impl AirTester for FunctionsAirTester { generate_air_winterfell_test!( test_functions_complex_air, - crate::functions::functions_complex::FunctionsAir, + crate::tests::functions::functions_complex::FunctionsAir, FunctionsAirTester, 1024 ); diff --git a/air-script/tests/indexed_trace_access/indexed_trace_access.air b/air-script/src/tests/indexed_trace_access/indexed_trace_access.air similarity index 100% rename from air-script/tests/indexed_trace_access/indexed_trace_access.air rename to air-script/src/tests/indexed_trace_access/indexed_trace_access.air diff --git a/air-script/tests/indexed_trace_access/indexed_trace_access.rs b/air-script/src/tests/indexed_trace_access/indexed_trace_access.rs similarity index 100% rename from air-script/tests/indexed_trace_access/indexed_trace_access.rs rename to air-script/src/tests/indexed_trace_access/indexed_trace_access.rs diff --git a/air-script/src/tests/indexed_trace_access/indexed_trace_access_plonky3.rs b/air-script/src/tests/indexed_trace_access/indexed_trace_access_plonky3.rs new file mode 100644 index 000000000..d1f6eb420 --- /dev/null +++ b/air-script/src/tests/indexed_trace_access/indexed_trace_access_plonky3.rs @@ -0,0 +1,43 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 4; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct TraceAccessAir; + +impl MidenAir for TraceAccessAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero(main_next[0].clone().into() - (main_current[1].clone().into() + AB::Expr::ONE)); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/indexed_trace_access/mod.rs b/air-script/src/tests/indexed_trace_access/mod.rs similarity index 100% rename from air-script/tests/indexed_trace_access/mod.rs rename to air-script/src/tests/indexed_trace_access/mod.rs diff --git a/air-script/tests/indexed_trace_access/test_air_plonky3.rs b/air-script/src/tests/indexed_trace_access/test_air_plonky3.rs similarity index 50% rename from air-script/tests/indexed_trace_access/test_air_plonky3.rs rename to air-script/src/tests/indexed_trace_access/test_air_plonky3.rs index b01cc0eaa..4b0a31c39 100644 --- a/air-script/tests/indexed_trace_access/test_air_plonky3.rs +++ b/air-script/src/tests/indexed_trace_access/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, - indexed_trace_access::indexed_trace_access_plonky3::{NUM_COLUMNS, TraceAccessAir}, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::indexed_trace_access::indexed_trace_access_plonky3::{MAIN_WIDTH, TraceAccessAir}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -54,4 +43,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -generate_air_plonky3_test!(test_air_plonky3, TraceAccessAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, TraceAccessAir); diff --git a/air-script/tests/indexed_trace_access/test_air_winterfell.rs b/air-script/src/tests/indexed_trace_access/test_air_winterfell.rs similarity index 83% rename from air-script/tests/indexed_trace_access/test_air_winterfell.rs rename to air-script/src/tests/indexed_trace_access/test_air_winterfell.rs index 9d7e64bcb..44b7dfcec 100644 --- a/air-script/tests/indexed_trace_access/test_air_winterfell.rs +++ b/air-script/src/tests/indexed_trace_access/test_air_winterfell.rs @@ -4,8 +4,8 @@ use winterfell::{Trace, TraceTable}; use crate::{ generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, - indexed_trace_access::indexed_trace_access::PublicInputs, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::indexed_trace_access::indexed_trace_access::PublicInputs, }; #[derive(Clone)] @@ -42,7 +42,7 @@ impl AirTester for TraceAccessAirTester { generate_air_winterfell_test!( test_indexed_trace_access_air, - crate::indexed_trace_access::indexed_trace_access::TraceAccessAir, + crate::tests::indexed_trace_access::indexed_trace_access::TraceAccessAir, TraceAccessAirTester, 1024 ); diff --git a/air-script/tests/list_comprehension/list_comprehension.air b/air-script/src/tests/list_comprehension/list_comprehension.air similarity index 100% rename from air-script/tests/list_comprehension/list_comprehension.air rename to air-script/src/tests/list_comprehension/list_comprehension.air diff --git a/air-script/tests/list_comprehension/list_comprehension.rs b/air-script/src/tests/list_comprehension/list_comprehension.rs similarity index 100% rename from air-script/tests/list_comprehension/list_comprehension.rs rename to air-script/src/tests/list_comprehension/list_comprehension.rs diff --git a/air-script/tests/list_comprehension/list_comprehension_nested.air b/air-script/src/tests/list_comprehension/list_comprehension_nested.air similarity index 100% rename from air-script/tests/list_comprehension/list_comprehension_nested.air rename to air-script/src/tests/list_comprehension/list_comprehension_nested.air diff --git a/air-script/tests/list_comprehension/list_comprehension_nested.rs b/air-script/src/tests/list_comprehension/list_comprehension_nested.rs similarity index 100% rename from air-script/tests/list_comprehension/list_comprehension_nested.rs rename to air-script/src/tests/list_comprehension/list_comprehension_nested.rs diff --git a/air-script/src/tests/list_comprehension/list_comprehension_nested_plonky3.rs b/air-script/src/tests/list_comprehension/list_comprehension_nested_plonky3.rs new file mode 100644 index 000000000..734706746 --- /dev/null +++ b/air-script/src/tests/list_comprehension/list_comprehension_nested_plonky3.rs @@ -0,0 +1,45 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 2; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 1; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct ListComprehensionAir; + +impl MidenAir for ListComprehensionAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[0].clone().into() + main_current[1].clone().into().double() - AB::Expr::from_u64(3)); + builder.assert_zero(main_current[0].clone().into().double() + main_current[1].clone().into() * AB::Expr::from_u64(3) - AB::Expr::from_u64(5)); + builder.assert_zero(main_current[0].clone().into() * AB::Expr::from_u64(3) + main_current[1].clone().into() * AB::Expr::from_u64(4) - AB::Expr::from_u64(7)); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/src/tests/list_comprehension/list_comprehension_plonky3.rs b/air-script/src/tests/list_comprehension/list_comprehension_plonky3.rs new file mode 100644 index 000000000..727cc9cc0 --- /dev/null +++ b/air-script/src/tests/list_comprehension/list_comprehension_plonky3.rs @@ -0,0 +1,48 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 16; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct ListComprehensionAir; + +impl MidenAir for ListComprehensionAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[10].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[0].clone().into() - main_current[2].clone().into()); + builder.assert_zero(main_current[4].clone().into() - main_current[0].clone().into() * AB::Expr::from_u64(8) * main_current[11].clone().into()); + builder.when_transition().assert_zero(main_current[4].clone().into() - main_current[0].clone().into() * (main_next[8].clone().into() - main_next[12].clone().into())); + builder.assert_zero(main_current[6].clone().into() - main_current[0].clone().into() * (main_current[9].clone().into() - main_current[14].clone().into())); + builder.assert_zero(main_current[1].clone().into() - (main_current[5].clone().into() - main_current[8].clone().into() - main_current[12].clone().into() + AB::Expr::from_u64(10) + main_current[6].clone().into() - main_current[9].clone().into() - main_current[13].clone().into() + AB::Expr::from_u64(20) + main_current[7].clone().into() - main_current[10].clone().into() - main_current[14].clone().into())); + builder.assert_zero(main_current[14].clone().into() - AB::Expr::from_u64(10)); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/list_comprehension/mod.rs b/air-script/src/tests/list_comprehension/mod.rs similarity index 54% rename from air-script/tests/list_comprehension/mod.rs rename to air-script/src/tests/list_comprehension/mod.rs index eb8e94340..e33f216df 100644 --- a/air-script/tests/list_comprehension/mod.rs +++ b/air-script/src/tests/list_comprehension/mod.rs @@ -1,5 +1,12 @@ #[rustfmt::skip] #[allow(clippy::all)] +mod list_comprehension_nested; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +mod list_comprehension_nested_plonky3; +#[rustfmt::skip] +#[allow(clippy::all)] mod list_comprehension; #[rustfmt::skip] #[allow(clippy::all)] diff --git a/air-script/tests/list_comprehension/test_air_plonky3.rs b/air-script/src/tests/list_comprehension/test_air_plonky3.rs similarity index 72% rename from air-script/tests/list_comprehension/test_air_plonky3.rs rename to air-script/src/tests/list_comprehension/test_air_plonky3.rs index 7cec4aeed..a99c57065 100644 --- a/air-script/tests/list_comprehension/test_air_plonky3.rs +++ b/air-script/src/tests/list_comprehension/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, - list_comprehension::list_comprehension_plonky3::{ListComprehensionAir, NUM_COLUMNS}, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::list_comprehension::list_comprehension_plonky3::{ListComprehensionAir, MAIN_WIDTH}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -94,4 +83,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -generate_air_plonky3_test!(test_air_plonky3, ListComprehensionAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, ListComprehensionAir); diff --git a/air-script/tests/list_comprehension/test_air_winterfell.rs b/air-script/src/tests/list_comprehension/test_air_winterfell.rs similarity index 87% rename from air-script/tests/list_comprehension/test_air_winterfell.rs rename to air-script/src/tests/list_comprehension/test_air_winterfell.rs index 8fef13b07..749541299 100644 --- a/air-script/tests/list_comprehension/test_air_winterfell.rs +++ b/air-script/src/tests/list_comprehension/test_air_winterfell.rs @@ -4,8 +4,8 @@ use winterfell::{Trace, TraceTable}; use crate::{ generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, - list_comprehension::list_comprehension::PublicInputs, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::list_comprehension::list_comprehension::PublicInputs, }; #[derive(Clone)] @@ -54,7 +54,7 @@ impl AirTester for ListComprehensionAirTester { generate_air_winterfell_test!( test_list_comprehension_air, - crate::list_comprehension::list_comprehension::ListComprehensionAir, + crate::tests::list_comprehension::list_comprehension::ListComprehensionAir, ListComprehensionAirTester, 1024 ); diff --git a/air-script/tests/list_folding/list_folding.air b/air-script/src/tests/list_folding/list_folding.air similarity index 100% rename from air-script/tests/list_folding/list_folding.air rename to air-script/src/tests/list_folding/list_folding.air diff --git a/air-script/tests/list_folding/list_folding.rs b/air-script/src/tests/list_folding/list_folding.rs similarity index 100% rename from air-script/tests/list_folding/list_folding.rs rename to air-script/src/tests/list_folding/list_folding.rs diff --git a/air-script/src/tests/list_folding/list_folding_plonky3.rs b/air-script/src/tests/list_folding/list_folding_plonky3.rs new file mode 100644 index 000000000..1a455fdd9 --- /dev/null +++ b/air-script/src/tests/list_folding/list_folding_plonky3.rs @@ -0,0 +1,46 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 17; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct ListFoldingAir; + +impl MidenAir for ListFoldingAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[11].clone().into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero(main_next[5].clone().into() - (main_current[9].clone().into() + main_current[10].clone().into() + main_current[11].clone().into() + main_current[12].clone().into() + main_current[13].clone().into() * main_current[14].clone().into() * main_current[15].clone().into() * main_current[16].clone().into())); + builder.when_transition().assert_zero(main_next[6].clone().into() - (main_current[9].clone().into() + main_current[10].clone().into() + main_current[11].clone().into() + main_current[12].clone().into() + main_current[13].clone().into() * main_current[14].clone().into() * main_current[15].clone().into() * main_current[16].clone().into())); + builder.when_transition().assert_zero(main_next[7].clone().into() - (main_current[9].clone().into() * main_current[13].clone().into() + main_current[10].clone().into() * main_current[14].clone().into() + main_current[11].clone().into() * main_current[15].clone().into() + main_current[12].clone().into() * main_current[16].clone().into() + (main_current[9].clone().into() + main_current[13].clone().into()) * (main_current[10].clone().into() + main_current[14].clone().into()) * (main_current[11].clone().into() + main_current[15].clone().into()) * (main_current[12].clone().into() + main_current[16].clone().into()))); + builder.when_transition().assert_zero(main_next[8].clone().into() - (main_current[1].clone().into() + main_current[9].clone().into() * main_current[13].clone().into() + main_current[10].clone().into() * main_current[14].clone().into() + main_current[11].clone().into() * main_current[15].clone().into() + main_current[12].clone().into() * main_current[16].clone().into() + main_current[9].clone().into() * main_current[13].clone().into() + main_current[10].clone().into() * main_current[14].clone().into() + main_current[11].clone().into() * main_current[15].clone().into() + main_current[12].clone().into() * main_current[16].clone().into())); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/list_folding/mod.rs b/air-script/src/tests/list_folding/mod.rs similarity index 100% rename from air-script/tests/list_folding/mod.rs rename to air-script/src/tests/list_folding/mod.rs diff --git a/air-script/tests/list_folding/test_air_plonky3.rs b/air-script/src/tests/list_folding/test_air_plonky3.rs similarity index 73% rename from air-script/tests/list_folding/test_air_plonky3.rs rename to air-script/src/tests/list_folding/test_air_plonky3.rs index 83d82e10d..a87dae25b 100644 --- a/air-script/tests/list_folding/test_air_plonky3.rs +++ b/air-script/src/tests/list_folding/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, - list_folding::list_folding_plonky3::{ListFoldingAir, NUM_COLUMNS}, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::list_folding::list_folding_plonky3::{ListFoldingAir, MAIN_WIDTH}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -97,4 +86,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -generate_air_plonky3_test!(test_air_plonky3, ListFoldingAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, ListFoldingAir); diff --git a/air-script/tests/list_folding/test_air_winterfell.rs b/air-script/src/tests/list_folding/test_air_winterfell.rs similarity index 89% rename from air-script/tests/list_folding/test_air_winterfell.rs rename to air-script/src/tests/list_folding/test_air_winterfell.rs index f8376848d..a957f18f9 100644 --- a/air-script/tests/list_folding/test_air_winterfell.rs +++ b/air-script/src/tests/list_folding/test_air_winterfell.rs @@ -4,8 +4,8 @@ use winterfell::{Trace, TraceTable}; use crate::{ generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, - list_folding::list_folding::PublicInputs, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::list_folding::list_folding::PublicInputs, }; #[derive(Clone)] @@ -55,7 +55,7 @@ impl AirTester for ListFoldingAirTester { generate_air_winterfell_test!( test_list_folding_air, - crate::list_folding::list_folding::ListFoldingAir, + crate::tests::list_folding::list_folding::ListFoldingAir, ListFoldingAirTester, 1024 ); diff --git a/air-script/tests/mod.rs b/air-script/src/tests/mod.rs similarity index 97% rename from air-script/tests/mod.rs rename to air-script/src/tests/mod.rs index 1f8c0fc09..c3d85e7a0 100644 --- a/air-script/tests/mod.rs +++ b/air-script/src/tests/mod.rs @@ -1,7 +1,3 @@ -mod codegen; - -pub mod helpers; - #[allow(unused_variables, dead_code, unused_mut)] mod binary; #[allow(unused_variables, dead_code, unused_mut)] @@ -42,3 +38,5 @@ mod trace_col_groups; mod variables; mod docs_sync; +mod plonky3; +mod winterfell; diff --git a/air-script/tests/periodic_columns/mod.rs b/air-script/src/tests/periodic_columns/mod.rs similarity index 100% rename from air-script/tests/periodic_columns/mod.rs rename to air-script/src/tests/periodic_columns/mod.rs diff --git a/air-script/tests/periodic_columns/periodic_columns.air b/air-script/src/tests/periodic_columns/periodic_columns.air similarity index 100% rename from air-script/tests/periodic_columns/periodic_columns.air rename to air-script/src/tests/periodic_columns/periodic_columns.air diff --git a/air-script/tests/periodic_columns/periodic_columns.rs b/air-script/src/tests/periodic_columns/periodic_columns.rs similarity index 100% rename from air-script/tests/periodic_columns/periodic_columns.rs rename to air-script/src/tests/periodic_columns/periodic_columns.rs diff --git a/air-script/src/tests/periodic_columns/periodic_columns_plonky3.rs b/air-script/src/tests/periodic_columns/periodic_columns_plonky3.rs new file mode 100644 index 000000000..22acad484 --- /dev/null +++ b/air-script/src/tests/periodic_columns/periodic_columns_plonky3.rs @@ -0,0 +1,58 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 3; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 2; +pub const PERIOD: usize = 8; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct PeriodicColumnsAir; + +impl MidenAir for PeriodicColumnsAir +where F: Field, + EF: ExtensionField, +{ + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } + + fn periodic_table(&self) -> Vec> { + vec![ + vec![F::from_u64(1), F::from_u64(0), F::from_u64(0), F::from_u64(0)], + vec![F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(0)], + ] + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero_ext(AB::ExprEF::from(periodic_values[0].clone().into()) * (AB::ExprEF::from(main_current[1].clone().into()) + AB::ExprEF::from(main_current[2].clone().into()))); + builder.when_transition().assert_zero_ext(AB::ExprEF::from(periodic_values[1].clone().into()) * (AB::ExprEF::from(main_next[0].clone().into()) - AB::ExprEF::from(main_current[0].clone().into()))); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/periodic_columns/test_air_plonky3.rs b/air-script/src/tests/periodic_columns/test_air_plonky3.rs similarity index 51% rename from air-script/tests/periodic_columns/test_air_plonky3.rs rename to air-script/src/tests/periodic_columns/test_air_plonky3.rs index 6b7b9acce..42091fa46 100644 --- a/air-script/tests/periodic_columns/test_air_plonky3.rs +++ b/air-script/src/tests/periodic_columns/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, - periodic_columns::periodic_columns_plonky3::{NUM_COLUMNS, PeriodicColumnsAir}, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::periodic_columns::periodic_columns_plonky3::{MAIN_WIDTH, PeriodicColumnsAir}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -55,4 +44,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -generate_air_plonky3_test!(test_air_plonky3, PeriodicColumnsAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, PeriodicColumnsAir); diff --git a/air-script/tests/periodic_columns/test_air_winterfell.rs b/air-script/src/tests/periodic_columns/test_air_winterfell.rs similarity index 82% rename from air-script/tests/periodic_columns/test_air_winterfell.rs rename to air-script/src/tests/periodic_columns/test_air_winterfell.rs index 0adc8364f..cf92e1101 100644 --- a/air-script/tests/periodic_columns/test_air_winterfell.rs +++ b/air-script/src/tests/periodic_columns/test_air_winterfell.rs @@ -4,8 +4,8 @@ use winterfell::{Trace, TraceTable}; use crate::{ generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, - periodic_columns::periodic_columns::PublicInputs, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::periodic_columns::periodic_columns::PublicInputs, }; #[derive(Clone)] @@ -39,7 +39,7 @@ impl AirTester for PeriodicColumnsAirTester { generate_air_winterfell_test!( test_periodic_columns_air, - crate::periodic_columns::periodic_columns::PeriodicColumnsAir, + crate::tests::periodic_columns::periodic_columns::PeriodicColumnsAir, PeriodicColumnsAirTester, 1024 ); diff --git a/air-script/src/tests/plonky3.rs b/air-script/src/tests/plonky3.rs new file mode 100644 index 000000000..e599d2189 --- /dev/null +++ b/air-script/src/tests/plonky3.rs @@ -0,0 +1,336 @@ +use expect_test::expect_file; + +use crate::test_utils::codegen::{Target, Test}; + +#[test] +fn binary() { + let generated_air = Test::new("src/tests/binary/binary.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["binary/binary_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn bitwise() { + let generated_air = Test::new("src/tests/bitwise/bitwise.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["bitwise/bitwise_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_complex() { + let generated_air = Test::new("src/tests/buses/buses_complex.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["buses/buses_complex_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_simple() { + let generated_air = Test::new("src/tests/buses/buses_simple.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["buses/buses_simple_plonky3.rs"]; + expected.assert_eq(&generated_air); +} +#[test] +fn buses_simple_with_evaluators() { + let generated_air = Test::new("src/tests/buses/buses_simple_with_evaluators.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["buses/buses_simple_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_varlen_boundary_both() { + let generated_air = Test::new("src/tests/buses/buses_varlen_boundary_both.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["buses/buses_varlen_boundary_both_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_varlen_boundary_first() { + let generated_air = Test::new("src/tests/buses/buses_varlen_boundary_first.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["buses/buses_varlen_boundary_first_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_varlen_boundary_last() { + let generated_air = Test::new("src/tests/buses/buses_varlen_boundary_last.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["buses/buses_varlen_boundary_last_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn computed_indices_complex() { + let generated_air = + Test::new("src/tests/computed_indices/computed_indices_complex.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["computed_indices/computed_indices_complex_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn computed_indices_simple() { + let generated_air = + Test::new("src/tests/computed_indices/computed_indices_simple.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["computed_indices/computed_indices_simple_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn constant_in_range() { + let generated_air = Test::new("src/tests/constant_in_range/constant_in_range.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["constant_in_range/constant_in_range_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn constants() { + let generated_air = Test::new("src/tests/constants/constants.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["constants/constants_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn evaluators() { + let generated_air = Test::new("src/tests/evaluators/evaluators.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["evaluators/evaluators_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn fibonacci() { + let generated_air = Test::new("src/tests/fibonacci/fibonacci.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["fibonacci/fibonacci_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn functions_complex() { + let generated_air = Test::new("src/tests/functions/functions_complex.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["functions/functions_complex_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn functions_simple() { + let generated_air = Test::new("src/tests/functions/functions_simple.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["functions/functions_simple_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn functions_simple_inlined() { + // make sure that the constraints generated using inlined functions are the same as the ones + // generated using regular functions + let generated_air = Test::new("src/tests/functions/inlined_functions_simple.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["functions/functions_simple_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn indexed_trace_access() { + let generated_air = + Test::new("src/tests/indexed_trace_access/indexed_trace_access.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["indexed_trace_access/indexed_trace_access_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn list_comprehension_nested() { + let generated_air = + Test::new("src/tests/list_comprehension/list_comprehension_nested.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["list_comprehension/list_comprehension_nested_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn list_comprehension() { + let generated_air = + Test::new("src/tests/list_comprehension/list_comprehension.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["list_comprehension/list_comprehension_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn list_folding() { + let generated_air = Test::new("src/tests/list_folding/list_folding.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["list_folding/list_folding_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn periodic_columns() { + let generated_air = Test::new("src/tests/periodic_columns/periodic_columns.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["periodic_columns/periodic_columns_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn pub_inputs() { + let generated_air = Test::new("src/tests/pub_inputs/pub_inputs.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["pub_inputs/pub_inputs_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors_combine_complex() { + let generated_air = Test::new("src/tests/selectors/selectors_combine_complex.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["selectors/selectors_combine_complex_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors_combine_simple() { + let generated_air = Test::new("src/tests/selectors/selectors_combine_simple.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["selectors/selectors_combine_simple_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors_combine_with_list_comprehensions() { + let generated_air = + Test::new("src/tests/selectors/selectors_combine_with_list_comprehensions.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["selectors/selectors_combine_with_list_comprehensions_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors() { + let generated_air = Test::new("src/tests/selectors/selectors.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["selectors/selectors_plonky3.rs"]; + expected.assert_eq(&generated_air); + + let generated_air = Test::new("src/tests/selectors/selectors_with_evaluators.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["selectors/selectors_with_evaluators_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn system() { + let generated_air = Test::new("src/tests/system/system.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["system/system_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn trace_col_groups() { + let generated_air = Test::new("src/tests/trace_col_groups/trace_col_groups.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["trace_col_groups/trace_col_groups_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn variables() { + let generated_air = Test::new("src/tests/variables/variables.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["variables/variables_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn constraint_comprehension() { + let generated_air = + Test::new("src/tests/constraint_comprehension/constraint_comprehension.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["constraint_comprehension/constraint_comprehension_plonky3.rs"]; + expected.assert_eq(&generated_air); + + let generated_air = + Test::new("src/tests/constraint_comprehension/cc_with_evaluators.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["constraint_comprehension/constraint_comprehension_plonky3.rs"]; + expected.assert_eq(&generated_air); +} diff --git a/air-script/tests/pub_inputs/mod.rs b/air-script/src/tests/pub_inputs/mod.rs similarity index 100% rename from air-script/tests/pub_inputs/mod.rs rename to air-script/src/tests/pub_inputs/mod.rs diff --git a/air-script/tests/pub_inputs/pub_inputs.air b/air-script/src/tests/pub_inputs/pub_inputs.air similarity index 100% rename from air-script/tests/pub_inputs/pub_inputs.air rename to air-script/src/tests/pub_inputs/pub_inputs.air diff --git a/air-script/tests/pub_inputs/pub_inputs.rs b/air-script/src/tests/pub_inputs/pub_inputs.rs similarity index 100% rename from air-script/tests/pub_inputs/pub_inputs.rs rename to air-script/src/tests/pub_inputs/pub_inputs.rs diff --git a/air-script/src/tests/pub_inputs/pub_inputs_plonky3.rs b/air-script/src/tests/pub_inputs/pub_inputs_plonky3.rs new file mode 100644 index 000000000..bbdef9bf9 --- /dev/null +++ b/air-script/src/tests/pub_inputs/pub_inputs_plonky3.rs @@ -0,0 +1,50 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 4; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 32; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct PubInputsAir; + +impl MidenAir for PubInputsAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into() - public_values[8].into()); + builder.when_first_row().assert_zero(main_current[1].clone().into() - public_values[9].into()); + builder.when_first_row().assert_zero(main_current[2].clone().into() - public_values[10].into()); + builder.when_first_row().assert_zero(main_current[3].clone().into() - public_values[11].into()); + builder.when_last_row().assert_zero(main_current[0].clone().into() - public_values[12].into()); + builder.when_last_row().assert_zero(main_current[1].clone().into() - public_values[13].into()); + builder.when_last_row().assert_zero(main_current[2].clone().into() - public_values[14].into()); + builder.when_last_row().assert_zero(main_current[3].clone().into() - public_values[15].into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero(main_next[0].clone().into() - (main_current[1].clone().into() + main_current[2].clone().into())); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/pub_inputs/test_air_plonky3.rs b/air-script/src/tests/pub_inputs/test_air_plonky3.rs similarity index 55% rename from air-script/tests/pub_inputs/test_air_plonky3.rs rename to air-script/src/tests/pub_inputs/test_air_plonky3.rs index 71343872e..8595358b4 100644 --- a/air-script/tests/pub_inputs/test_air_plonky3.rs +++ b/air-script/src/tests/pub_inputs/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, - pub_inputs::pub_inputs_plonky3::{NUM_COLUMNS, PubInputsAir}, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::pub_inputs::pub_inputs_plonky3::{MAIN_WIDTH, PubInputsAir}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -58,4 +47,4 @@ fn generate_inputs() -> Vec { vec![0; 32] } -generate_air_plonky3_test!(test_air_plonky3, PubInputsAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, PubInputsAir); diff --git a/air-script/tests/pub_inputs/test_air_winterfell.rs b/air-script/src/tests/pub_inputs/test_air_winterfell.rs similarity index 85% rename from air-script/tests/pub_inputs/test_air_winterfell.rs rename to air-script/src/tests/pub_inputs/test_air_winterfell.rs index 947f4bbac..62b8be849 100644 --- a/air-script/tests/pub_inputs/test_air_winterfell.rs +++ b/air-script/src/tests/pub_inputs/test_air_winterfell.rs @@ -4,8 +4,8 @@ use winterfell::{Trace, TraceTable}; use crate::{ generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, - pub_inputs::pub_inputs::PublicInputs, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::pub_inputs::pub_inputs::PublicInputs, }; #[derive(Clone)] @@ -40,7 +40,7 @@ impl AirTester for PubInputsAirTester { generate_air_winterfell_test!( test_pub_inputs_air, - crate::pub_inputs::pub_inputs::PubInputsAir, + crate::tests::pub_inputs::pub_inputs::PubInputsAir, PubInputsAirTester, 1024 ); diff --git a/air-script/tests/selectors/mod.rs b/air-script/src/tests/selectors/mod.rs similarity index 90% rename from air-script/tests/selectors/mod.rs rename to air-script/src/tests/selectors/mod.rs index 39924b2bc..a8007ac23 100644 --- a/air-script/tests/selectors/mod.rs +++ b/air-script/src/tests/selectors/mod.rs @@ -16,6 +16,10 @@ mod selectors_with_evaluators; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] +mod selectors_plonky3; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] mod selectors_combine_simple_plonky3; #[rustfmt::skip] #[allow(clippy::all)] diff --git a/air-script/tests/selectors/selectors.air b/air-script/src/tests/selectors/selectors.air similarity index 100% rename from air-script/tests/selectors/selectors.air rename to air-script/src/tests/selectors/selectors.air diff --git a/air-script/tests/selectors/selectors.rs b/air-script/src/tests/selectors/selectors.rs similarity index 100% rename from air-script/tests/selectors/selectors.rs rename to air-script/src/tests/selectors/selectors.rs diff --git a/air-script/tests/selectors/selectors_combine_complex.air b/air-script/src/tests/selectors/selectors_combine_complex.air similarity index 100% rename from air-script/tests/selectors/selectors_combine_complex.air rename to air-script/src/tests/selectors/selectors_combine_complex.air diff --git a/air-script/tests/selectors/selectors_combine_complex.rs b/air-script/src/tests/selectors/selectors_combine_complex.rs similarity index 100% rename from air-script/tests/selectors/selectors_combine_complex.rs rename to air-script/src/tests/selectors/selectors_combine_complex.rs diff --git a/air-script/src/tests/selectors/selectors_combine_complex_plonky3.rs b/air-script/src/tests/selectors/selectors_combine_complex_plonky3.rs new file mode 100644 index 000000000..7714372f2 --- /dev/null +++ b/air-script/src/tests/selectors/selectors_combine_complex_plonky3.rs @@ -0,0 +1,134 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 6; +pub const AUX_WIDTH: usize = 1; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 1; +pub const MAX_BETA_CHALLENGE_POWER: usize = 2; + +pub struct SelectorsAir; + +impl MidenAir for SelectorsAir +where F: Field, + EF: ExtensionField, +{ + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn num_randomness(&self) -> usize { + 1 + MAX_BETA_CHALLENGE_POWER + } + + fn aux_width(&self) -> usize { + AUX_WIDTH + } + + fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { + // Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders. + + let num_rows = _main.height(); + let trace_length = num_rows * AUX_WIDTH; + let mut long_trace = EF::zero_vec(trace_length); + let mut trace = RowMajorMatrix::new(long_trace, AUX_WIDTH); + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[EF; AUX_WIDTH]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + // Initialize first row + let initial_values = Self::buses_initial_values::(); + for j in 0..AUX_WIDTH { + rows[0][j] = initial_values[j]; + } + // Fill subsequent rows using direct access to the rows array + for i in 0..num_rows-1 { + let i_next = (i + 1) % num_rows; + let main_local = _main.row_slice(i).unwrap(); // i < height so unwrap should never fail. + let main_next = _main.row_slice(i_next).unwrap(); // i_next < height so unwrap should never fail. + let main = VerticalPair::new( + RowMajorMatrixView::new_row(&*main_local), + RowMajorMatrixView::new_row(&*main_next), + ); + let periodic_values: [_; NUM_PERIODIC_VALUES] = >::periodic_table(self).iter().map(|col| col[i % col.len()]).collect::>().try_into().expect("Wrong number of periodic values"); + let prev_row = &rows[i]; + let next_row = Self::buses_transitions::( + &main, + _challenges, + &periodic_values, + prev_row, + ); + for j in 0..AUX_WIDTH { + rows[i+1][j] = next_row[j]; + } + } + Some(trace) + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + let (&alpha, beta_challenges) = builder.permutation_randomness().split_first().expect("Wrong number of randomness"); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); + let aux_bus_boundary_values: [_; AUX_WIDTH] = builder.aux_bus_boundary_values().try_into().expect("Wrong number of aux bus boundary values"); + let aux = builder.permutation(); + let (aux_current, aux_next) = ( + aux.row_slice(0).unwrap(), + aux.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[5].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero((main_current[0].clone().into() + (AB::Expr::ONE - main_current[0].clone().into()) * main_current[1].clone().into()) * (main_current[3].clone().into() - AB::Expr::from_u64(16)) + (AB::Expr::ONE - main_current[0].clone().into()) * (AB::Expr::ONE - main_current[1].clone().into()) * (main_current[4].clone().into() - AB::Expr::from_u64(5))); + builder.assert_zero((AB::Expr::ONE - main_current[0].clone().into()) * (main_current[5].clone().into() - AB::Expr::from_u64(5)) + main_current[0].clone().into() * (main_current[4].clone().into() - AB::Expr::from_u64(4))); + builder.assert_zero(main_current[0].clone().into() * (main_current[5].clone().into() - AB::Expr::from_u64(20)) + (AB::Expr::ONE - main_current[0].clone().into()) * main_current[1].clone().into() * (main_current[4].clone().into() - AB::Expr::from_u64(31))); + + // Aux boundary constraints + builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); + builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); + + // Aux integrity/transition constraints + builder.when_transition().assert_zero_ext(((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) * AB::ExprEF::from(main_current[5].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into()) * AB::ExprEF::from(main_current[5].clone().into())) * ((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(main_current[1].clone().into()) * AB::ExprEF::from(main_current[5].clone().into()) + AB::ExprEF::ONE - (AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(main_current[1].clone().into()) * AB::ExprEF::from(main_current[5].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from_u64(4) * beta_challenges[1].into()) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[1].clone().into())) * AB::ExprEF::from(main_current[4].clone().into()) + AB::ExprEF::ONE - (AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[1].clone().into())) * AB::ExprEF::from(main_current[4].clone().into())) * AB::ExprEF::from(aux_next[0].clone().into())); + } +} + +impl SelectorsAir { + fn buses_initial_values() -> Vec + where F: Field, + EF: ExtensionField, + { + vec![ + EF::ONE, + ] + } + + fn buses_transitions(main: &VerticalPair, RowMajorMatrixView>, challenges: &[EF], periodic_evals: &[F], aux_current: &[EF]) -> Vec + where F: Field, + EF: ExtensionField, + { + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + let (&alpha, beta_challenges) = challenges.split_first().expect("Wrong number of randomness"); + let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = periodic_evals.try_into().expect("Wrong number of periodic values"); + vec![ + (((alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(main_current[0].clone()) * EF::from(main_current[5].clone()) + EF::ONE - EF::from(main_current[0].clone()) * EF::from(main_current[5].clone())) * ((alpha + beta_challenges[0] + beta_challenges[1].double()) * (EF::ONE - EF::from(main_current[0].clone())) * EF::from(main_current[1].clone()) * EF::from(main_current[5].clone()) + EF::ONE - (EF::ONE - EF::from(main_current[0].clone())) * EF::from(main_current[1].clone()) * EF::from(main_current[5].clone())) * EF::from(aux_current[0].clone())) * ((alpha + EF::from_u64(3) * beta_challenges[0] + EF::from_u64(4) * beta_challenges[1]) * (EF::ONE - EF::from(main_current[0].clone())) * (EF::ONE - EF::from(main_current[1].clone())) * EF::from(main_current[4].clone()) + EF::ONE - (EF::ONE - EF::from(main_current[0].clone())) * (EF::ONE - EF::from(main_current[1].clone())) * EF::from(main_current[4].clone())).inverse(), + ] + } +} \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_combine_simple.air b/air-script/src/tests/selectors/selectors_combine_simple.air similarity index 100% rename from air-script/tests/selectors/selectors_combine_simple.air rename to air-script/src/tests/selectors/selectors_combine_simple.air diff --git a/air-script/tests/selectors/selectors_combine_simple.rs b/air-script/src/tests/selectors/selectors_combine_simple.rs similarity index 100% rename from air-script/tests/selectors/selectors_combine_simple.rs rename to air-script/src/tests/selectors/selectors_combine_simple.rs diff --git a/air-script/src/tests/selectors/selectors_combine_simple_plonky3.rs b/air-script/src/tests/selectors/selectors_combine_simple_plonky3.rs new file mode 100644 index 000000000..df958a51e --- /dev/null +++ b/air-script/src/tests/selectors/selectors_combine_simple_plonky3.rs @@ -0,0 +1,44 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 4; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 1; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct SelectorsAir; + +impl MidenAir for SelectorsAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[3].clone().into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero(main_next[1].clone().into() - main_current[2].clone().into()); + builder.when_transition().assert_zero(main_current[3].clone().into() * (main_next[0].clone().into() - (main_current[0].clone().into() + main_current[1].clone().into())) + (AB::Expr::ONE - main_current[3].clone().into()) * (main_next[0].clone().into() - main_current[0].clone().into() * main_current[1].clone().into())); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_combine_with_list_comprehensions.air b/air-script/src/tests/selectors/selectors_combine_with_list_comprehensions.air similarity index 100% rename from air-script/tests/selectors/selectors_combine_with_list_comprehensions.air rename to air-script/src/tests/selectors/selectors_combine_with_list_comprehensions.air diff --git a/air-script/tests/selectors/selectors_combine_with_list_comprehensions.rs b/air-script/src/tests/selectors/selectors_combine_with_list_comprehensions.rs similarity index 100% rename from air-script/tests/selectors/selectors_combine_with_list_comprehensions.rs rename to air-script/src/tests/selectors/selectors_combine_with_list_comprehensions.rs diff --git a/air-script/src/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs b/air-script/src/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs new file mode 100644 index 000000000..c7c4e3940 --- /dev/null +++ b/air-script/src/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs @@ -0,0 +1,45 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 6; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 1; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct SelectorsAir; + +impl MidenAir for SelectorsAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[5].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero((main_current[0].clone().into() + (AB::Expr::ONE - main_current[0].clone().into()) * main_current[1].clone().into()) * main_current[3].clone().into() + (AB::Expr::ONE - main_current[0].clone().into()) * (AB::Expr::ONE - main_current[1].clone().into()) * (main_current[4].clone().into() - AB::Expr::from_u64(8))); + builder.assert_zero((AB::Expr::ONE - main_current[0].clone().into()) * (main_current[5].clone().into() - AB::Expr::from_u64(8)) + main_current[0].clone().into() * (main_current[4].clone().into() - AB::Expr::from_u64(2))); + builder.assert_zero(main_current[0].clone().into() * (main_current[5].clone().into() - AB::Expr::from_u64(4)) + (AB::Expr::ONE - main_current[0].clone().into()) * main_current[1].clone().into() * (main_current[4].clone().into() - AB::Expr::from_u64(6))); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/src/tests/selectors/selectors_plonky3.rs b/air-script/src/tests/selectors/selectors_plonky3.rs new file mode 100644 index 000000000..51f70004b --- /dev/null +++ b/air-script/src/tests/selectors/selectors_plonky3.rs @@ -0,0 +1,44 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 4; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct SelectorsAir; + +impl MidenAir for SelectorsAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[3].clone().into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero(main_current[0].clone().into() * (AB::Expr::ONE - main_current[1].clone().into()) * main_next[3].clone().into()); + builder.when_transition().assert_zero(main_current[0].clone().into() * main_current[1].clone().into() * main_current[2].clone().into() * (main_next[3].clone().into() - main_current[3].clone().into()) + (AB::Expr::ONE - main_current[1].clone().into()) * (AB::Expr::ONE - main_current[2].clone().into()) * (main_next[3].clone().into() - AB::Expr::ONE)); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_with_evaluators.air b/air-script/src/tests/selectors/selectors_with_evaluators.air similarity index 100% rename from air-script/tests/selectors/selectors_with_evaluators.air rename to air-script/src/tests/selectors/selectors_with_evaluators.air diff --git a/air-script/tests/selectors/selectors_with_evaluators.rs b/air-script/src/tests/selectors/selectors_with_evaluators.rs similarity index 100% rename from air-script/tests/selectors/selectors_with_evaluators.rs rename to air-script/src/tests/selectors/selectors_with_evaluators.rs diff --git a/air-script/src/tests/selectors/selectors_with_evaluators_plonky3.rs b/air-script/src/tests/selectors/selectors_with_evaluators_plonky3.rs new file mode 100644 index 000000000..1dde4fecf --- /dev/null +++ b/air-script/src/tests/selectors/selectors_with_evaluators_plonky3.rs @@ -0,0 +1,44 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 4; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct SelectorsAir; + +impl MidenAir for SelectorsAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[3].clone().into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero(main_current[0].clone().into() * (AB::Expr::ONE - main_current[1].clone().into()) * main_next[3].clone().into()); + builder.when_transition().assert_zero(main_current[1].clone().into() * main_current[2].clone().into() * main_current[0].clone().into() * (main_next[3].clone().into() - main_current[3].clone().into()) + (AB::Expr::ONE - main_current[1].clone().into()) * (AB::Expr::ONE - main_current[2].clone().into()) * (main_next[3].clone().into() - AB::Expr::ONE)); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/selectors/test_air_plonky3.rs b/air-script/src/tests/selectors/test_air_plonky3.rs similarity index 54% rename from air-script/tests/selectors/test_air_plonky3.rs rename to air-script/src/tests/selectors/test_air_plonky3.rs index 289939f0e..ef3c01f81 100644 --- a/air-script/tests/selectors/test_air_plonky3.rs +++ b/air-script/src/tests/selectors/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, - selectors::selectors_with_evaluators_plonky3::{NUM_COLUMNS, SelectorsAir}, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::selectors::selectors_with_evaluators_plonky3::{MAIN_WIDTH, SelectorsAir}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -58,4 +47,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -generate_air_plonky3_test!(test_air_plonky3, SelectorsAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, SelectorsAir); diff --git a/air-script/tests/selectors/test_air_winterfell.rs b/air-script/src/tests/selectors/test_air_winterfell.rs similarity index 84% rename from air-script/tests/selectors/test_air_winterfell.rs rename to air-script/src/tests/selectors/test_air_winterfell.rs index 19f683ef6..3bb3ac337 100644 --- a/air-script/tests/selectors/test_air_winterfell.rs +++ b/air-script/src/tests/selectors/test_air_winterfell.rs @@ -4,8 +4,8 @@ use winterfell::{Trace, TraceTable}; use crate::{ generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, - selectors::selectors_with_evaluators::PublicInputs, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::selectors::selectors_with_evaluators::PublicInputs, }; #[derive(Clone)] @@ -42,7 +42,7 @@ impl AirTester for SelectorsAirTester { generate_air_winterfell_test!( test_selectors_with_evaluators_air, - crate::selectors::selectors_with_evaluators::SelectorsAir, + crate::tests::selectors::selectors_with_evaluators::SelectorsAir, SelectorsAirTester, 1024 ); diff --git a/air-script/tests/system/mod.rs b/air-script/src/tests/system/mod.rs similarity index 100% rename from air-script/tests/system/mod.rs rename to air-script/src/tests/system/mod.rs diff --git a/air-script/tests/system/system.air b/air-script/src/tests/system/system.air similarity index 100% rename from air-script/tests/system/system.air rename to air-script/src/tests/system/system.air diff --git a/air-script/tests/system/system.rs b/air-script/src/tests/system/system.rs similarity index 100% rename from air-script/tests/system/system.rs rename to air-script/src/tests/system/system.rs diff --git a/air-script/src/tests/system/system_plonky3.rs b/air-script/src/tests/system/system_plonky3.rs new file mode 100644 index 000000000..574bccd9b --- /dev/null +++ b/air-script/src/tests/system/system_plonky3.rs @@ -0,0 +1,43 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 3; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct SystemAir; + +impl MidenAir for SystemAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero(main_next[0].clone().into() - (main_current[0].clone().into() + AB::Expr::ONE)); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/system/test_air_plonky3.rs b/air-script/src/tests/system/test_air_plonky3.rs similarity index 53% rename from air-script/tests/system/test_air_plonky3.rs rename to air-script/src/tests/system/test_air_plonky3.rs index 9c0eb5004..404ea03d7 100644 --- a/air-script/tests/system/test_air_plonky3.rs +++ b/air-script/src/tests/system/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, - system::system_plonky3::{NUM_COLUMNS, SystemAir}, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::system::system_plonky3::{MAIN_WIDTH, SystemAir}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -55,4 +44,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -generate_air_plonky3_test!(test_air_plonky3, SystemAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, SystemAir); diff --git a/air-script/tests/system/test_air_winterfell.rs b/air-script/src/tests/system/test_air_winterfell.rs similarity index 86% rename from air-script/tests/system/test_air_winterfell.rs rename to air-script/src/tests/system/test_air_winterfell.rs index 9327f6e98..99a717f78 100644 --- a/air-script/tests/system/test_air_winterfell.rs +++ b/air-script/src/tests/system/test_air_winterfell.rs @@ -4,8 +4,8 @@ use winterfell::{Trace, TraceTable}; use crate::{ generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, - system::system::PublicInputs, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::system::system::PublicInputs, }; #[derive(Clone)] @@ -41,7 +41,7 @@ impl AirTester for SystemAirTester { generate_air_winterfell_test!( test_system_air, - crate::system::system::SystemAir, + crate::tests::system::system::SystemAir, SystemAirTester, 1024 ); diff --git a/air-script/tests/trace_col_groups/mod.rs b/air-script/src/tests/trace_col_groups/mod.rs similarity index 100% rename from air-script/tests/trace_col_groups/mod.rs rename to air-script/src/tests/trace_col_groups/mod.rs diff --git a/air-script/tests/trace_col_groups/test_air_plonky3.rs b/air-script/src/tests/trace_col_groups/test_air_plonky3.rs similarity index 64% rename from air-script/tests/trace_col_groups/test_air_plonky3.rs rename to air-script/src/tests/trace_col_groups/test_air_plonky3.rs index b12ab3ac7..1b4522ecf 100644 --- a/air-script/tests/trace_col_groups/test_air_plonky3.rs +++ b/air-script/src/tests/trace_col_groups/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, - trace_col_groups::trace_col_groups_plonky3::{NUM_COLUMNS, TraceColGroupAir}, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::trace_col_groups::trace_col_groups_plonky3::{MAIN_WIDTH, TraceColGroupAir}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -73,4 +62,4 @@ fn generate_inputs() -> Vec { vec![1; 16] } -generate_air_plonky3_test!(test_air_plonky3, TraceColGroupAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, TraceColGroupAir); diff --git a/air-script/tests/trace_col_groups/test_air_winterfell.rs b/air-script/src/tests/trace_col_groups/test_air_winterfell.rs similarity index 86% rename from air-script/tests/trace_col_groups/test_air_winterfell.rs rename to air-script/src/tests/trace_col_groups/test_air_winterfell.rs index 92400a4f2..53a5af721 100644 --- a/air-script/tests/trace_col_groups/test_air_winterfell.rs +++ b/air-script/src/tests/trace_col_groups/test_air_winterfell.rs @@ -4,8 +4,8 @@ use winterfell::{Trace, TraceTable}; use crate::{ generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, - trace_col_groups::trace_col_groups::PublicInputs, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::trace_col_groups::trace_col_groups::PublicInputs, }; #[derive(Clone)] @@ -48,7 +48,7 @@ impl AirTester for TraceColGroupAirTester { generate_air_winterfell_test!( test_trace_col_groups_air, - crate::trace_col_groups::trace_col_groups::TraceColGroupAir, + crate::tests::trace_col_groups::trace_col_groups::TraceColGroupAir, TraceColGroupAirTester, 1024 ); diff --git a/air-script/tests/trace_col_groups/trace_col_groups.air b/air-script/src/tests/trace_col_groups/trace_col_groups.air similarity index 100% rename from air-script/tests/trace_col_groups/trace_col_groups.air rename to air-script/src/tests/trace_col_groups/trace_col_groups.air diff --git a/air-script/tests/trace_col_groups/trace_col_groups.rs b/air-script/src/tests/trace_col_groups/trace_col_groups.rs similarity index 100% rename from air-script/tests/trace_col_groups/trace_col_groups.rs rename to air-script/src/tests/trace_col_groups/trace_col_groups.rs diff --git a/air-script/src/tests/trace_col_groups/trace_col_groups_plonky3.rs b/air-script/src/tests/trace_col_groups/trace_col_groups_plonky3.rs new file mode 100644 index 000000000..026304e2d --- /dev/null +++ b/air-script/src/tests/trace_col_groups/trace_col_groups_plonky3.rs @@ -0,0 +1,44 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 9; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct TraceColGroupAir; + +impl MidenAir for TraceColGroupAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[8].clone().into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero(main_next[2].clone().into() - (main_current[2].clone().into() + AB::Expr::ONE)); + builder.when_transition().assert_zero(main_next[1].clone().into() - (main_current[1].clone().into() - AB::Expr::ONE)); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/variables/mod.rs b/air-script/src/tests/variables/mod.rs similarity index 100% rename from air-script/tests/variables/mod.rs rename to air-script/src/tests/variables/mod.rs diff --git a/air-script/tests/variables/test_air_plonky3.rs b/air-script/src/tests/variables/test_air_plonky3.rs similarity index 54% rename from air-script/tests/variables/test_air_plonky3.rs rename to air-script/src/tests/variables/test_air_plonky3.rs index 8296f2197..b8d2d88ef 100644 --- a/air-script/tests/variables/test_air_plonky3.rs +++ b/air-script/src/tests/variables/test_air_plonky3.rs @@ -1,32 +1,21 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; use crate::{ - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, - variables::variables_plonky3::{NUM_COLUMNS, VariablesAir}, + generate_air_plonky3_test_with_airscript_traits, + test_utils::plonky3_traits::check_constraints_with_airscript_traits, + tests::variables::variables_plonky3::{MAIN_WIDTH, VariablesAir}, }; pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; + let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), num_rows); @@ -58,4 +47,4 @@ fn generate_inputs() -> Vec { vec![1; 32] } -generate_air_plonky3_test!(test_air_plonky3, VariablesAir); +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, VariablesAir); diff --git a/air-script/tests/variables/test_air_winterfell.rs b/air-script/src/tests/variables/test_air_winterfell.rs similarity index 86% rename from air-script/tests/variables/test_air_winterfell.rs rename to air-script/src/tests/variables/test_air_winterfell.rs index 3578f330f..d384975b3 100644 --- a/air-script/tests/variables/test_air_winterfell.rs +++ b/air-script/src/tests/variables/test_air_winterfell.rs @@ -4,8 +4,8 @@ use winterfell::{Trace, TraceTable}; use crate::{ generate_air_winterfell_test, - helpers::{AirTester, MyTraceTable}, - variables::variables::PublicInputs, + test_utils::winterfell_traits::{AirTester, MyTraceTable}, + tests::variables::variables::PublicInputs, }; #[derive(Clone)] @@ -42,7 +42,7 @@ impl AirTester for VariablesAirTester { generate_air_winterfell_test!( test_variables_air, - crate::variables::variables::VariablesAir, + crate::tests::variables::variables::VariablesAir, VariablesAirTester, 1024 ); diff --git a/air-script/tests/variables/variables.air b/air-script/src/tests/variables/variables.air similarity index 100% rename from air-script/tests/variables/variables.air rename to air-script/src/tests/variables/variables.air diff --git a/air-script/tests/variables/variables.rs b/air-script/src/tests/variables/variables.rs similarity index 100% rename from air-script/tests/variables/variables.rs rename to air-script/src/tests/variables/variables.rs diff --git a/air-script/src/tests/variables/variables_plonky3.rs b/air-script/src/tests/variables/variables_plonky3.rs new file mode 100644 index 000000000..bfe0da908 --- /dev/null +++ b/air-script/src/tests/variables/variables_plonky3.rs @@ -0,0 +1,60 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 4; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 1; +pub const PERIOD: usize = 8; +pub const NUM_PUBLIC_VALUES: usize = 32; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct VariablesAir; + +impl MidenAir for VariablesAir +where F: Field, + EF: ExtensionField, +{ + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } + + fn periodic_table(&self) -> Vec> { + vec![ + vec![F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(0)], + ] + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[1].clone().into()); + builder.when_last_row().assert_zero(main_current[1].clone().into() - AB::Expr::ONE); + + // Main integrity/transition constraints + builder.assert_zero(main_current[0].clone().into() * main_current[0].clone().into() - main_current[0].clone().into()); + builder.when_transition().assert_zero_ext(AB::ExprEF::from(periodic_values[0].clone().into()) * (AB::ExprEF::from(main_next[0].clone().into()) - AB::ExprEF::from(main_current[0].clone().into()))); + builder.assert_zero((AB::Expr::ONE - main_current[0].clone().into()) * (main_current[3].clone().into() - main_current[1].clone().into() - main_current[2].clone().into()) - (AB::Expr::from_u64(6) - (AB::Expr::from_u64(7) - main_current[0].clone().into()))); + builder.when_transition().assert_zero(main_current[0].clone().into() * (main_current[3].clone().into() - main_current[1].clone().into() * main_current[2].clone().into()) - (AB::Expr::ONE - main_next[0].clone().into())); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/src/tests/winterfell.rs b/air-script/src/tests/winterfell.rs new file mode 100644 index 000000000..2b2683cb6 --- /dev/null +++ b/air-script/src/tests/winterfell.rs @@ -0,0 +1,337 @@ +use expect_test::expect_file; + +use crate::test_utils::codegen::{Target, Test}; + +#[test] +fn binary() { + let generated_air = Test::new("src/tests/binary/binary.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["binary/binary.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn bitwise() { + let generated_air = Test::new("src/tests/bitwise/bitwise.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["bitwise/bitwise.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_complex() { + let generated_air = Test::new("src/tests/buses/buses_complex.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["buses/buses_complex.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_simple() { + let generated_air = Test::new("src/tests/buses/buses_simple.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["buses/buses_simple.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_simple_with_evaluators() { + let generated_air = Test::new("src/tests/buses/buses_simple_with_evaluators.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["buses/buses_simple.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_varlen_boundary_both() { + let generated_air = Test::new("src/tests/buses/buses_varlen_boundary_both.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["buses/buses_varlen_boundary_both.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_varlen_boundary_first() { + let generated_air = Test::new("src/tests/buses/buses_varlen_boundary_first.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["buses/buses_varlen_boundary_first.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_varlen_boundary_last() { + let generated_air = Test::new("src/tests/buses/buses_varlen_boundary_last.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["buses/buses_varlen_boundary_last.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn computed_indices_complex() { + let generated_air = + Test::new("src/tests/computed_indices/computed_indices_complex.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["computed_indices/computed_indices_complex.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn computed_indices_simple() { + let generated_air = + Test::new("src/tests/computed_indices/computed_indices_simple.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["computed_indices/computed_indices_simple.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn constant_in_range() { + let generated_air = Test::new("src/tests/constant_in_range/constant_in_range.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["constant_in_range/constant_in_range.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn constants() { + let generated_air = Test::new("src/tests/constants/constants.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["constants/constants.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn constraint_comprehension() { + let generated_air = + Test::new("src/tests/constraint_comprehension/constraint_comprehension.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["constraint_comprehension/constraint_comprehension.rs"]; + expected.assert_eq(&generated_air); + + let generated_air = + Test::new("src/tests/constraint_comprehension/cc_with_evaluators.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["constraint_comprehension/constraint_comprehension.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn evaluators() { + let generated_air = Test::new("src/tests/evaluators/evaluators.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["evaluators/evaluators.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn fibonacci() { + let generated_air = Test::new("src/tests/fibonacci/fibonacci.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["fibonacci/fibonacci.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn functions_complex() { + let generated_air = Test::new("src/tests/functions/functions_complex.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["functions/functions_complex.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn functions_simple() { + let generated_air = Test::new("src/tests/functions/functions_simple.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["functions/functions_simple.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn functions_simple_inlined() { + // make sure that the constraints generated using inlined functions are the same as the ones + // generated using regular functions + let generated_air = Test::new("src/tests/functions/inlined_functions_simple.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["functions/functions_simple.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn indexed_trace_access() { + let generated_air = + Test::new("src/tests/indexed_trace_access/indexed_trace_access.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["indexed_trace_access/indexed_trace_access.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn list_comprehension() { + let generated_air = + Test::new("src/tests/list_comprehension/list_comprehension.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["list_comprehension/list_comprehension.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn list_comprehension_nested() { + let generated_air = + Test::new("src/tests/list_comprehension/list_comprehension_nested.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["list_comprehension/list_comprehension_nested.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn list_folding() { + let generated_air = Test::new("src/tests/list_folding/list_folding.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["list_folding/list_folding.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn periodic_columns() { + let generated_air = Test::new("src/tests/periodic_columns/periodic_columns.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["periodic_columns/periodic_columns.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn pub_inputs() { + let generated_air = Test::new("src/tests/pub_inputs/pub_inputs.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["pub_inputs/pub_inputs.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors() { + let generated_air = Test::new("src/tests/selectors/selectors.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["selectors/selectors.rs"]; + expected.assert_eq(&generated_air); + + let generated_air = Test::new("src/tests/selectors/selectors_with_evaluators.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["selectors/selectors_with_evaluators.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors_combine_simple() { + let generated_air = Test::new("src/tests/selectors/selectors_combine_simple.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["selectors/selectors_combine_simple.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors_combine_complex() { + let generated_air = Test::new("src/tests/selectors/selectors_combine_complex.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["selectors/selectors_combine_complex.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors_combine_with_list_comprehensions() { + let generated_air = + Test::new("src/tests/selectors/selectors_combine_with_list_comprehensions.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["selectors/selectors_combine_with_list_comprehensions.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn system() { + let generated_air = Test::new("src/tests/system/system.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["system/system.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn trace_col_groups() { + let generated_air = Test::new("src/tests/trace_col_groups/trace_col_groups.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["trace_col_groups/trace_col_groups.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn variables() { + let generated_air = Test::new("src/tests/variables/variables.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["variables/variables.rs"]; + expected.assert_eq(&generated_air); +} diff --git a/air-script/tests/binary/binary_plonky3.rs b/air-script/tests/binary/binary_plonky3.rs deleted file mode 100644 index df44bcb6d..000000000 --- a/air-script/tests/binary/binary_plonky3.rs +++ /dev/null @@ -1,44 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 2; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct BinaryAir; - -impl BaseAir for BinaryAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for BinaryAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for BinaryAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for BinaryAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[0].into() - public_values[0].into()); - builder.assert_zero::<_>(main_current[0].into() * main_current[0].into() - main_current[0].into()); - builder.assert_zero::<_>(main_current[1].into() * main_current[1].into() - main_current[1].into()); - } -} \ No newline at end of file diff --git a/air-script/tests/bitwise/bitwise_plonky3.rs b/air-script/tests/bitwise/bitwise_plonky3.rs deleted file mode 100644 index ef5d5d992..000000000 --- a/air-script/tests/bitwise/bitwise_plonky3.rs +++ /dev/null @@ -1,61 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 14; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct BitwiseAir; - -impl BaseAir for BitwiseAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for BitwiseAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for BitwiseAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - vec![F::from_u64(1), F::from_u64(0), F::from_u64(0), F::from_u64(0), F::from_u64(0), F::from_u64(0), F::from_u64(0), F::from_u64(0)], - vec![F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(0)], - ] - } -} - -impl Air for BitwiseAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[13].into()); - builder.assert_zero::<_>(main_current[0].into() * main_current[0].into() - main_current[0].into()); - builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[0].into() - main_current[0].into())); - builder.assert_zero::<_>(main_current[3].into() * main_current[3].into() - main_current[3].into()); - builder.assert_zero::<_>(main_current[4].into() * main_current[4].into() - main_current[4].into()); - builder.assert_zero::<_>(main_current[5].into() * main_current[5].into() - main_current[5].into()); - builder.assert_zero::<_>(main_current[6].into() * main_current[6].into() - main_current[6].into()); - builder.assert_zero::<_>(main_current[7].into() * main_current[7].into() - main_current[7].into()); - builder.assert_zero::<_>(main_current[8].into() * main_current[8].into() - main_current[8].into()); - builder.assert_zero::<_>(main_current[9].into() * main_current[9].into() - main_current[9].into()); - builder.assert_zero::<_>(main_current[10].into() * main_current[10].into() - main_current[10].into()); - builder.assert_zero::<_>(periodic_values[0].into() * (main_current[1].into() - (main_current[3].into() + main_current[4].into().double() + AB::Expr::from_u64(4) * main_current[5].into() + AB::Expr::from_u64(8) * main_current[6].into()))); - builder.assert_zero::<_>(periodic_values[0].into() * (main_current[2].into() - (main_current[7].into() + main_current[8].into().double() + AB::Expr::from_u64(4) * main_current[9].into() + AB::Expr::from_u64(8) * main_current[10].into()))); - builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[1].into() - (main_current[1].into() * AB::Expr::from_u64(16) + main_current[3].into() + main_current[4].into().double() + AB::Expr::from_u64(4) * main_current[5].into() + AB::Expr::from_u64(8) * main_current[6].into()))); - builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[2].into() - (main_current[2].into() * AB::Expr::from_u64(16) + main_current[7].into() + main_current[8].into().double() + AB::Expr::from_u64(4) * main_current[9].into() + AB::Expr::from_u64(8) * main_current[10].into()))); - builder.assert_zero::<_>(periodic_values[0].into() * main_current[11].into()); - builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_current[12].into() - main_next[11].into())); - builder.assert_zero::<_>((AB::Expr::ONE - main_current[0].into()) * (main_current[12].into() - (main_current[11].into() * AB::Expr::from_u64(16) + main_current[3].into() * main_current[7].into() + main_current[4].into().double() * main_current[8].into() + AB::Expr::from_u64(4) * main_current[5].into() * main_current[9].into() + AB::Expr::from_u64(8) * main_current[6].into() * main_current[10].into())) + main_current[0].into() * (main_current[12].into() - (main_current[11].into() * AB::Expr::from_u64(16) + main_current[3].into() + main_current[7].into() - main_current[3].into().double() * main_current[7].into() + (main_current[4].into() + main_current[8].into() - main_current[4].into().double() * main_current[8].into()).double() + AB::Expr::from_u64(4) * (main_current[5].into() + main_current[9].into() - main_current[5].into().double() * main_current[9].into()) + AB::Expr::from_u64(8) * (main_current[6].into() + main_current[10].into() - main_current[6].into().double() * main_current[10].into())))); - } -} \ No newline at end of file diff --git a/air-script/tests/buses/buses_complex_plonky3.rs b/air-script/tests/buses/buses_complex_plonky3.rs deleted file mode 100644 index 462ef499a..000000000 --- a/air-script/tests/buses/buses_complex_plonky3.rs +++ /dev/null @@ -1,44 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 5; - -pub const NUM_PUBLIC_VALUES: usize = 2; - -pub struct BusesAir; - -impl BaseAir for BusesAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for BusesAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for BusesAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for BusesAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[0].into()); - builder.assert_zero::<_>(main_current[2].into() * main_current[2].into() - main_current[2].into()); - builder.assert_zero::<_>(main_current[3].into() * main_current[3].into() - main_current[3].into()); - } -} \ No newline at end of file diff --git a/air-script/tests/buses/buses_simple_plonky3.rs b/air-script/tests/buses/buses_simple_plonky3.rs deleted file mode 100644 index 6ea46ea4b..000000000 --- a/air-script/tests/buses/buses_simple_plonky3.rs +++ /dev/null @@ -1,41 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 1; - -pub const NUM_PUBLIC_VALUES: usize = 2; - -pub struct BusesAir; - -impl BaseAir for BusesAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for BusesAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for BusesAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for BusesAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - } -} \ No newline at end of file diff --git a/air-script/tests/buses/buses_varlen_boundary_both_plonky3.rs b/air-script/tests/buses/buses_varlen_boundary_both_plonky3.rs deleted file mode 100644 index e8f0c1253..000000000 --- a/air-script/tests/buses/buses_varlen_boundary_both_plonky3.rs +++ /dev/null @@ -1,41 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 1; - -pub const NUM_PUBLIC_VALUES: usize = 6; - -pub struct BusesAir; - -impl BaseAir for BusesAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for BusesAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for BusesAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for BusesAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - } -} \ No newline at end of file diff --git a/air-script/tests/buses/buses_varlen_boundary_first_plonky3.rs b/air-script/tests/buses/buses_varlen_boundary_first_plonky3.rs deleted file mode 100644 index 6ea46ea4b..000000000 --- a/air-script/tests/buses/buses_varlen_boundary_first_plonky3.rs +++ /dev/null @@ -1,41 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 1; - -pub const NUM_PUBLIC_VALUES: usize = 2; - -pub struct BusesAir; - -impl BaseAir for BusesAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for BusesAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for BusesAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for BusesAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - } -} \ No newline at end of file diff --git a/air-script/tests/buses/buses_varlen_boundary_last_plonky3.rs b/air-script/tests/buses/buses_varlen_boundary_last_plonky3.rs deleted file mode 100644 index 6ea46ea4b..000000000 --- a/air-script/tests/buses/buses_varlen_boundary_last_plonky3.rs +++ /dev/null @@ -1,41 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 1; - -pub const NUM_PUBLIC_VALUES: usize = 2; - -pub struct BusesAir; - -impl BaseAir for BusesAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for BusesAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for BusesAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for BusesAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - } -} \ No newline at end of file diff --git a/air-script/tests/buses/test_air_plonky3.rs b/air-script/tests/buses/test_air_plonky3.rs deleted file mode 100644 index 1b323208b..000000000 --- a/air-script/tests/buses/test_air_plonky3.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; - -use crate::{ - buses::buses_complex_plonky3::{BusesAir, NUM_COLUMNS}, - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, -}; - -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; - - let mut long_trace = F::zero_vec(trace_length); - - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); - - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; - assert!(prefix.is_empty(), "Alignment should match"); - assert!(suffix.is_empty(), "Alignment should match"); - assert_eq!(rows.len(), num_rows); - - // Initialize first row - rows[0][0] = F::ZERO; - rows[0][1] = F::ZERO; - - // Fill subsequent rows using direct access to the rows array - for i in 1..num_rows { - let a_prev = rows[i - 1][0]; - let b_prev = rows[i - 1][1]; - - // Update current row based on previous values - rows[i][0] = F::ONE - a_prev; - rows[i][1] = F::ONE - b_prev; - } - - trace -} - -fn generate_inputs() -> Vec { - vec![1; 2] -} - -generate_air_plonky3_test!(test_air_plonky3, BusesAir); diff --git a/air-script/tests/codegen/mod.rs b/air-script/tests/codegen/mod.rs deleted file mode 100644 index a23676b61..000000000 --- a/air-script/tests/codegen/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod helpers; -mod plonky3; -mod winterfell; diff --git a/air-script/tests/codegen/plonky3.rs b/air-script/tests/codegen/plonky3.rs deleted file mode 100644 index 8ccbb2d55..000000000 --- a/air-script/tests/codegen/plonky3.rs +++ /dev/null @@ -1,323 +0,0 @@ -use expect_test::expect_file; - -use super::helpers::{Target, Test}; - -#[test] -fn binary() { - let generated_air = Test::new("tests/binary/binary.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../binary/binary_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_simple() { - let generated_air = Test::new("tests/buses/buses_simple.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../buses/buses_simple_plonky3.rs"]; - expected.assert_eq(&generated_air); -} -#[test] -fn buses_simple_with_evaluators() { - let generated_air = Test::new("tests/buses/buses_simple_with_evaluators.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../buses/buses_simple_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_complex() { - let generated_air = Test::new("tests/buses/buses_complex.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../buses/buses_complex_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_varlen_boundary_first() { - let generated_air = Test::new("tests/buses/buses_varlen_boundary_first.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../buses/buses_varlen_boundary_first_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_varlen_boundary_last() { - let generated_air = Test::new("tests/buses/buses_varlen_boundary_last.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../buses/buses_varlen_boundary_last_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_varlen_boundary_both() { - let generated_air = Test::new("tests/buses/buses_varlen_boundary_both.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../buses/buses_varlen_boundary_both_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn periodic_columns() { - let generated_air = Test::new("tests/periodic_columns/periodic_columns.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../periodic_columns/periodic_columns_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn pub_inputs() { - let generated_air = Test::new("tests/pub_inputs/pub_inputs.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../pub_inputs/pub_inputs_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn system() { - let generated_air = Test::new("tests/system/system.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../system/system_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn bitwise() { - let generated_air = Test::new("tests/bitwise/bitwise.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../bitwise/bitwise_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn computed_indices_complex() { - let generated_air = - Test::new("tests/computed_indices/computed_indices_complex.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../computed_indices/computed_indices_complex_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn computed_indices_simple() { - let generated_air = Test::new("tests/computed_indices/computed_indices_simple.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../computed_indices/computed_indices_simple_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn constants() { - let generated_air = Test::new("tests/constants/constants.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../constants/constants_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn constant_in_range() { - let generated_air = Test::new("tests/constant_in_range/constant_in_range.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../constant_in_range/constant_in_range_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn evaluators() { - let generated_air = Test::new("tests/evaluators/evaluators.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../evaluators/evaluators_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn fibonacci() { - let generated_air = Test::new("tests/fibonacci/fibonacci.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../fibonacci/fibonacci_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn functions_simple() { - let generated_air = Test::new("tests/functions/functions_simple.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../functions/functions_simple_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn functions_simple_inlined() { - // make sure that the constraints generated using inlined functions are the same as the ones - // generated using regular functions - let generated_air = Test::new("tests/functions/inlined_functions_simple.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../functions/functions_simple_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn functions_complex() { - let generated_air = Test::new("tests/functions/functions_complex.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../functions/functions_complex_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn variables() { - let generated_air = Test::new("tests/variables/variables.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../variables/variables_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn trace_col_groups() { - let generated_air = Test::new("tests/trace_col_groups/trace_col_groups.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../trace_col_groups/trace_col_groups_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn indexed_trace_access() { - let generated_air = - Test::new("tests/indexed_trace_access/indexed_trace_access.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../indexed_trace_access/indexed_trace_access_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn list_comprehension() { - let generated_air = Test::new("tests/list_comprehension/list_comprehension.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../list_comprehension/list_comprehension_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn list_comprehension_nested() { - let generated_air = - Test::new("tests/list_comprehension/list_comprehension_nested.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../list_comprehension/list_comprehension_nested_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn list_folding() { - let generated_air = Test::new("tests/list_folding/list_folding.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../list_folding/list_folding_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn selectors() { - let generated_air = Test::new("tests/selectors/selectors.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../selectors/selectors_plonky3.rs"]; - expected.assert_eq(&generated_air); - - let generated_air = Test::new("tests/selectors/selectors_with_evaluators.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../selectors/selectors_with_evaluators_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn selectors_combine_simple() { - let generated_air = Test::new("tests/selectors/selectors_combine_simple.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../selectors/selectors_combine_simple_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn selectors_combine_complex() { - let generated_air = Test::new("tests/selectors/selectors_combine_complex.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../selectors/selectors_combine_complex_plonky3.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn constraint_comprehension() { - let generated_air = - Test::new("tests/constraint_comprehension/constraint_comprehension.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../constraint_comprehension/constraint_comprehension_plonky3.rs"]; - expected.assert_eq(&generated_air); - - let generated_air = - Test::new("tests/constraint_comprehension/cc_with_evaluators.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); - - let expected = expect_file!["../constraint_comprehension/constraint_comprehension_plonky3.rs"]; - expected.assert_eq(&generated_air); -} diff --git a/air-script/tests/codegen/winterfell.rs b/air-script/tests/codegen/winterfell.rs deleted file mode 100644 index 28a993cd6..000000000 --- a/air-script/tests/codegen/winterfell.rs +++ /dev/null @@ -1,335 +0,0 @@ -use expect_test::expect_file; - -use super::helpers::{Target, Test}; - -#[test] -fn binary() { - let generated_air = Test::new("tests/binary/binary.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../binary/binary.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn bitwise() { - let generated_air = Test::new("tests/bitwise/bitwise.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../bitwise/bitwise.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_complex() { - let generated_air = Test::new("tests/buses/buses_complex.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../buses/buses_complex.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_simple() { - let generated_air = Test::new("tests/buses/buses_simple.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../buses/buses_simple.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_simple_with_evaluators() { - let generated_air = Test::new("tests/buses/buses_simple_with_evaluators.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../buses/buses_simple.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_varlen_boundary_both() { - let generated_air = Test::new("tests/buses/buses_varlen_boundary_both.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../buses/buses_varlen_boundary_both.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_varlen_boundary_first() { - let generated_air = Test::new("tests/buses/buses_varlen_boundary_first.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../buses/buses_varlen_boundary_first.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_varlen_boundary_last() { - let generated_air = Test::new("tests/buses/buses_varlen_boundary_last.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../buses/buses_varlen_boundary_last.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn computed_indices_complex() { - let generated_air = - Test::new("tests/computed_indices/computed_indices_complex.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../computed_indices/computed_indices_complex.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn computed_indices_simple() { - let generated_air = Test::new("tests/computed_indices/computed_indices_simple.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../computed_indices/computed_indices_simple.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn constant_in_range() { - let generated_air = Test::new("tests/constant_in_range/constant_in_range.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../constant_in_range/constant_in_range.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn constants() { - let generated_air = Test::new("tests/constants/constants.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../constants/constants.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn constraint_comprehension() { - let generated_air = - Test::new("tests/constraint_comprehension/constraint_comprehension.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../constraint_comprehension/constraint_comprehension.rs"]; - expected.assert_eq(&generated_air); - - let generated_air = - Test::new("tests/constraint_comprehension/cc_with_evaluators.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../constraint_comprehension/constraint_comprehension.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn evaluators() { - let generated_air = Test::new("tests/evaluators/evaluators.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../evaluators/evaluators.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn fibonacci() { - let generated_air = Test::new("tests/fibonacci/fibonacci.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../fibonacci/fibonacci.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn functions_complex() { - let generated_air = Test::new("tests/functions/functions_complex.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../functions/functions_complex.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn functions_simple() { - let generated_air = Test::new("tests/functions/functions_simple.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../functions/functions_simple.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn functions_simple_inlined() { - // make sure that the constraints generated using inlined functions are the same as the ones - // generated using regular functions - let generated_air = Test::new("tests/functions/inlined_functions_simple.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../functions/functions_simple.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn indexed_trace_access() { - let generated_air = - Test::new("tests/indexed_trace_access/indexed_trace_access.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../indexed_trace_access/indexed_trace_access.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn list_comprehension() { - let generated_air = Test::new("tests/list_comprehension/list_comprehension.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../list_comprehension/list_comprehension.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn list_comprehension_nested() { - let generated_air = - Test::new("tests/list_comprehension/list_comprehension_nested.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../list_comprehension/list_comprehension_nested.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn list_folding() { - let generated_air = Test::new("tests/list_folding/list_folding.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../list_folding/list_folding.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn periodic_columns() { - let generated_air = Test::new("tests/periodic_columns/periodic_columns.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../periodic_columns/periodic_columns.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn pub_inputs() { - let generated_air = Test::new("tests/pub_inputs/pub_inputs.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../pub_inputs/pub_inputs.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn selectors() { - let generated_air = Test::new("tests/selectors/selectors.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../selectors/selectors.rs"]; - expected.assert_eq(&generated_air); - - let generated_air = Test::new("tests/selectors/selectors_with_evaluators.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../selectors/selectors_with_evaluators.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn selectors_combine_simple() { - let generated_air = Test::new("tests/selectors/selectors_combine_simple.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../selectors/selectors_combine_simple.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn selectors_combine_complex() { - let generated_air = Test::new("tests/selectors/selectors_combine_complex.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../selectors/selectors_combine_complex.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn selectors_combine_with_list_comprehensions() { - let generated_air = - Test::new("tests/selectors/selectors_combine_with_list_comprehensions.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../selectors/selectors_combine_with_list_comprehensions.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn system() { - let generated_air = Test::new("tests/system/system.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../system/system.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn trace_col_groups() { - let generated_air = Test::new("tests/trace_col_groups/trace_col_groups.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../trace_col_groups/trace_col_groups.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn variables() { - let generated_air = Test::new("tests/variables/variables.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../variables/variables.rs"]; - expected.assert_eq(&generated_air); -} diff --git a/air-script/tests/computed_indices/computed_indices_complex_plonky3.rs b/air-script/tests/computed_indices/computed_indices_complex_plonky3.rs deleted file mode 100644 index 1ded2a430..000000000 --- a/air-script/tests/computed_indices/computed_indices_complex_plonky3.rs +++ /dev/null @@ -1,43 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 4; - -pub const NUM_PUBLIC_VALUES: usize = 1; - -pub struct ComputedIndicesAir; - -impl BaseAir for ComputedIndicesAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for ComputedIndicesAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for ComputedIndicesAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for ComputedIndicesAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[0].into()); - builder.assert_zero::<_>(main_current[2].into() * AB::Expr::from_u64(3) + main_current[3].into() * AB::Expr::from_u64(4)); - } -} \ No newline at end of file diff --git a/air-script/tests/computed_indices/computed_indices_simple_plonky3.rs b/air-script/tests/computed_indices/computed_indices_simple_plonky3.rs deleted file mode 100644 index 3ac8ed33b..000000000 --- a/air-script/tests/computed_indices/computed_indices_simple_plonky3.rs +++ /dev/null @@ -1,50 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 8; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct ComputedIndicesAir; - -impl BaseAir for ComputedIndicesAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for ComputedIndicesAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for ComputedIndicesAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for ComputedIndicesAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[0].into()); - builder.assert_zero::<_>(main_current[0].into()); - builder.assert_zero::<_>(main_current[1].into() - AB::Expr::from_u64(2)); - builder.assert_zero::<_>(main_current[2].into() - AB::Expr::from_u64(4)); - builder.assert_zero::<_>(main_current[3].into() - AB::Expr::from_u64(6)); - builder.when_transition().assert_zero::<_>(main_next[4].into()); - builder.when_transition().assert_zero::<_>(main_next[5].into() - main_current[5].into().double()); - builder.when_transition().assert_zero::<_>(main_next[6].into() - AB::Expr::from_u64(6) * main_current[6].into()); - builder.when_transition().assert_zero::<_>(main_next[7].into() - AB::Expr::from_u64(12) * main_current[7].into()); - } -} \ No newline at end of file diff --git a/air-script/tests/computed_indices/mod.rs b/air-script/tests/computed_indices/mod.rs deleted file mode 100644 index 5f582b136..000000000 --- a/air-script/tests/computed_indices/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[rustfmt::skip] -#[allow(clippy::all)] -mod computed_indices_complex; -#[rustfmt::skip] -#[allow(clippy::all)] -mod computed_indices_simple; -mod test_air; diff --git a/air-script/tests/constant_in_range/constant_in_range_plonky3.rs b/air-script/tests/constant_in_range/constant_in_range_plonky3.rs deleted file mode 100644 index 5f657e987..000000000 --- a/air-script/tests/constant_in_range/constant_in_range_plonky3.rs +++ /dev/null @@ -1,43 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 12; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct ConstantInRangeAir; - -impl BaseAir for ConstantInRangeAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for ConstantInRangeAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for ConstantInRangeAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for ConstantInRangeAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[6].into()); - builder.assert_zero::<_>(main_current[0].into() - (main_current[1].into() - main_current[4].into() - main_current[8].into() + AB::Expr::ONE + main_current[2].into() - main_current[5].into() - main_current[9].into() + AB::Expr::from_u64(2) + main_current[3].into() - main_current[6].into() - main_current[10].into())); - } -} \ No newline at end of file diff --git a/air-script/tests/constants/constants_plonky3.rs b/air-script/tests/constants/constants_plonky3.rs deleted file mode 100644 index fa2ffccac..000000000 --- a/air-script/tests/constants/constants_plonky3.rs +++ /dev/null @@ -1,52 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 7; - -pub const NUM_PUBLIC_VALUES: usize = 32; - -pub struct ConstantsAir; - -impl BaseAir for ConstantsAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for ConstantsAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for ConstantsAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for ConstantsAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[0].into() - AB::Expr::ONE); - builder.when_first_row().assert_zero::<_>(main_current[1].into() - AB::Expr::ONE); - builder.when_first_row().assert_zero::<_>(main_current[2].into()); - builder.when_first_row().assert_zero::<_>(main_current[3].into() - AB::Expr::ONE); - builder.when_first_row().assert_zero::<_>(main_current[4].into() - AB::Expr::ONE); - builder.when_last_row().assert_zero::<_>(main_current[6].into()); - builder.when_transition().assert_zero::<_>(main_next[0].into() - (main_current[0].into() + AB::Expr::ONE)); - builder.when_transition().assert_zero::<_>(main_next[1].into()); - builder.when_transition().assert_zero::<_>(main_next[2].into() - main_current[2].into()); - builder.when_transition().assert_zero::<_>(main_next[5].into() - (main_current[5].into() + AB::Expr::ONE)); - builder.assert_zero::<_>(main_current[4].into() - AB::Expr::ONE); - } -} \ No newline at end of file diff --git a/air-script/tests/constraint_comprehension/constraint_comprehension_plonky3.rs b/air-script/tests/constraint_comprehension/constraint_comprehension_plonky3.rs deleted file mode 100644 index b501fe9d3..000000000 --- a/air-script/tests/constraint_comprehension/constraint_comprehension_plonky3.rs +++ /dev/null @@ -1,46 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 14; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct ConstraintComprehensionAir; - -impl BaseAir for ConstraintComprehensionAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for ConstraintComprehensionAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for ConstraintComprehensionAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for ConstraintComprehensionAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[8].into()); - builder.assert_zero::<_>(main_current[6].into() - main_current[10].into()); - builder.assert_zero::<_>(main_current[7].into() - main_current[11].into()); - builder.assert_zero::<_>(main_current[8].into() - main_current[12].into()); - builder.assert_zero::<_>(main_current[9].into() - main_current[13].into()); - } -} \ No newline at end of file diff --git a/air-script/tests/constraint_comprehension/test_air_plonky3.rs b/air-script/tests/constraint_comprehension/test_air_plonky3.rs deleted file mode 100644 index ceab24925..000000000 --- a/air-script/tests/constraint_comprehension/test_air_plonky3.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::marker::PhantomData; - -use p3_challenger::{HashChallenger, SerializingChallenger64}; -use p3_circle::CirclePcs; -use p3_commit::ExtensionMmcs; -use p3_field::{PrimeCharacteristicRing, PrimeField64, extension::BinomialExtensionField}; -use p3_fri::create_benchmark_fri_params; -use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_merkle_tree::MerkleTreeMmcs; -use p3_sha256::Sha256; -use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher}; -use p3_uni_stark::StarkConfig; - -use crate::{ - constraint_comprehension::constraint_comprehension_plonky3::{ - ConstraintComprehensionAir, NUM_COLUMNS, - }, - generate_air_plonky3_test, - helpers::check_constraints_with_periodic_columns, -}; - -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; - let trace_length = num_rows * NUM_COLUMNS; - - let mut long_trace = F::zero_vec(trace_length); - - let mut trace = RowMajorMatrix::new(long_trace, NUM_COLUMNS); - - let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; NUM_COLUMNS]>() }; - assert!(prefix.is_empty(), "Alignment should match"); - assert!(suffix.is_empty(), "Alignment should match"); - assert_eq!(rows.len(), num_rows); - - // Initialize first row - rows[0][0] = F::from_canonical_checked(inputs[0]).unwrap(); - rows[0][1] = F::ONE; - - // Fill subsequent rows using direct access to the rows array - for i in 1..num_rows { - let a_prev = rows[i - 1][0]; - let b_prev = rows[i - 1][1]; - - // Update current row based on previous values - rows[i][0] = F::ONE - a_prev; - rows[i][1] = F::ONE - b_prev; - } - - trace -} - -fn generate_inputs() -> Vec { - vec![1; 16] -} - -generate_air_plonky3_test!(test_air_plonky3, ConstraintComprehensionAir); diff --git a/air-script/tests/evaluators/evaluators_plonky3.rs b/air-script/tests/evaluators/evaluators_plonky3.rs deleted file mode 100644 index becb57b5a..000000000 --- a/air-script/tests/evaluators/evaluators_plonky3.rs +++ /dev/null @@ -1,52 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 7; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct EvaluatorsAir; - -impl BaseAir for EvaluatorsAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for EvaluatorsAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for EvaluatorsAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for EvaluatorsAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[0].into()); - builder.when_transition().assert_zero::<_>(main_next[0].into() - main_current[0].into()); - builder.when_transition().assert_zero::<_>(main_next[2].into() - main_current[2].into()); - builder.when_transition().assert_zero::<_>(main_next[6].into() - main_current[6].into()); - builder.assert_zero::<_>(main_current[0].into() * main_current[0].into() - main_current[0].into()); - builder.assert_zero::<_>(main_current[1].into() * main_current[1].into() - main_current[1].into()); - builder.assert_zero::<_>(main_current[2].into() * main_current[2].into() - main_current[2].into()); - builder.assert_zero::<_>(main_current[3].into() * main_current[3].into() - main_current[3].into()); - builder.assert_zero::<_>(main_current[4].into()); - builder.assert_zero::<_>(main_current[5].into() - AB::Expr::ONE); - builder.assert_zero::<_>(main_current[6].into() - AB::Expr::from_u64(4)); - } -} \ No newline at end of file diff --git a/air-script/tests/fibonacci/fibonacci_plonky3.rs b/air-script/tests/fibonacci/fibonacci_plonky3.rs deleted file mode 100644 index 262dbb610..000000000 --- a/air-script/tests/fibonacci/fibonacci_plonky3.rs +++ /dev/null @@ -1,46 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 2; - -pub const NUM_PUBLIC_VALUES: usize = 3; - -pub struct FibonacciAir; - -impl BaseAir for FibonacciAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for FibonacciAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for FibonacciAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for FibonacciAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[0].into() - public_values[0].into()); - builder.when_first_row().assert_zero::<_>(main_current[1].into() - public_values[1].into()); - builder.when_last_row().assert_zero::<_>(main_current[1].into() - public_values[2].into()); - builder.when_transition().assert_zero::<_>(main_next[1].into() - (main_current[0].into() + main_current[1].into())); - builder.when_transition().assert_zero::<_>(main_next[0].into() - main_current[1].into()); - } -} \ No newline at end of file diff --git a/air-script/tests/functions/functions_complex_plonky3.rs b/air-script/tests/functions/functions_complex_plonky3.rs deleted file mode 100644 index cca15366b..000000000 --- a/air-script/tests/functions/functions_complex_plonky3.rs +++ /dev/null @@ -1,44 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 17; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct FunctionsAir; - -impl BaseAir for FunctionsAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for FunctionsAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for FunctionsAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for FunctionsAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[3].into()); - builder.when_transition().assert_zero::<_>(main_next[16].into() - main_current[16].into() * ((main_current[3].into() * main_current[3].into() * main_current[3].into() * main_current[3].into() * main_current[3].into() * main_current[3].into() * main_current[3].into() * main_current[1].into() * main_current[2].into() + main_current[3].into() * main_current[3].into() * (AB::Expr::ONE - main_current[1].into()) * main_current[2].into() + main_current[3].into() * main_current[1].into() * (AB::Expr::ONE - main_current[2].into()) + (AB::Expr::ONE - main_current[1].into()) * (AB::Expr::ONE - main_current[2].into())) * main_current[0].into() - main_current[0].into() + AB::Expr::ONE)); - builder.when_transition().assert_zero::<_>(main_next[3].into() - (main_current[4].into() + main_current[5].into() + main_current[6].into() + main_current[7].into() + main_current[8].into() + main_current[9].into() + main_current[10].into() + main_current[11].into() + main_current[12].into() + main_current[13].into() + main_current[14].into() + main_current[15].into() + AB::Expr::ONE).double()); - } -} \ No newline at end of file diff --git a/air-script/tests/functions/functions_simple_plonky3.rs b/air-script/tests/functions/functions_simple_plonky3.rs deleted file mode 100644 index 7d19e8a4b..000000000 --- a/air-script/tests/functions/functions_simple_plonky3.rs +++ /dev/null @@ -1,50 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 9; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct FunctionsAir; - -impl BaseAir for FunctionsAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for FunctionsAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for FunctionsAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for FunctionsAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[3].into()); - builder.assert_zero::<_>(main_current[0].into() * main_current[3].into() - AB::Expr::ONE); - builder.assert_zero::<_>(main_current[4].into() * main_current[5].into() * main_current[6].into() * main_current[7].into() * main_current[3].into() - AB::Expr::ONE); - builder.assert_zero::<_>((main_current[4].into() + main_current[5].into() + main_current[6].into() + main_current[7].into()) * main_current[4].into() * main_current[5].into() * main_current[6].into() * main_current[7].into() - AB::Expr::ONE); - builder.assert_zero::<_>(main_current[4].into() * main_current[5].into() * main_current[6].into() * main_current[7].into() - AB::Expr::ONE); - builder.assert_zero::<_>(main_current[0].into() * main_current[4].into() * main_current[5].into() * main_current[6].into() * main_current[7].into() - AB::Expr::ONE); - builder.assert_zero::<_>(main_current[1].into() + (main_current[4].into() + main_current[5].into() + main_current[6].into() + main_current[7].into()) * main_current[4].into() * main_current[5].into() * main_current[6].into() * main_current[7].into() - AB::Expr::ONE); - builder.assert_zero::<_>(main_current[4].into() + main_current[5].into() + main_current[6].into() + main_current[7].into() - AB::Expr::ONE); - builder.assert_zero::<_>((main_current[4].into() + main_current[5].into() + main_current[6].into() + main_current[7].into()) * AB::Expr::from_u64(4) - AB::Expr::ONE); - } -} \ No newline at end of file diff --git a/air-script/tests/helpers/mod.rs b/air-script/tests/helpers/mod.rs deleted file mode 100644 index 846ff0fd2..000000000 --- a/air-script/tests/helpers/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod plonky3_periodic_columns; -mod winterfell_test_helpers; - -pub use plonky3_periodic_columns::*; -pub use winterfell_test_helpers::*; -pub mod macros; diff --git a/air-script/tests/helpers/plonky3_periodic_columns.rs b/air-script/tests/helpers/plonky3_periodic_columns.rs deleted file mode 100644 index 245a8d037..000000000 --- a/air-script/tests/helpers/plonky3_periodic_columns.rs +++ /dev/null @@ -1,146 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir}; -use p3_field::Field; -use p3_matrix::{ - Matrix, - dense::{RowMajorMatrix, RowMajorMatrixView}, - stack::VerticalPair, -}; - -pub trait BaseAirWithPeriodicColumns: BaseAir { - fn get_periodic_columns(&self) -> Vec> { - vec![] - } -} - -pub trait AirBuilderWithPeriodicColumns: AirBuilder { - type PeriodicColumnsVar: Field + Into; - - fn periodic_columns(&self) -> Vec { - vec![] - } -} - -pub(crate) fn check_constraints_with_periodic_columns( - air: &A, - main: &RowMajorMatrix, - public_values: &Vec, -) where - F: Field, - A: for<'a> Air> - + BaseAirWithPeriodicColumns, -{ - let height = main.height(); - - (0..height).for_each(|i| { - let i_next = (i + 1) % height; - - let local = main.row_slice(i).unwrap(); // i < height so unwrap should never fail. - let next = main.row_slice(i_next).unwrap(); // i_next < height so unwrap should never fail. - let main = VerticalPair::new( - RowMajorMatrixView::new_row(&*local), - RowMajorMatrixView::new_row(&*next), - ); - let periodic_columns = air.get_periodic_columns(); - - let mut builder = DebugConstraintBuilderWithPeriodicColumns { - row_index: i, - main, - public_values, - is_first_row: F::from_bool(i == 0), - is_last_row: F::from_bool(i == height - 1), - is_transition: F::from_bool(i != height - 1), - periodic_columns, - }; - - air.eval(&mut builder); - }); -} - -/// A builder that runs constraint assertions during testing. -/// -/// Used in conjunction with [`check_constraints`] to simulate -/// an execution trace and verify that the AIR logic enforces all constraints. -#[derive(Debug)] -pub struct DebugConstraintBuilderWithPeriodicColumns<'a, F: Field> { - /// The index of the row currently being evaluated. - row_index: usize, - /// A view of the current and next row as a vertical pair. - main: VerticalPair, RowMajorMatrixView<'a, F>>, - /// The public values provided for constraint validation (e.g. inputs or outputs). - public_values: &'a [F], - /// A flag indicating whether this is the first row. - is_first_row: F, - /// A flag indicating whether this is the last row. - is_last_row: F, - /// A flag indicating whether this is a transition row (not the last row). - is_transition: F, - /// The periodic columns provided for constraint validation. - periodic_columns: Vec>, -} - -impl<'a, F> AirBuilderWithPeriodicColumns for DebugConstraintBuilderWithPeriodicColumns<'a, F> -where - F: Field + Into, -{ - type PeriodicColumnsVar = F; - - fn periodic_columns(&self) -> Vec { - self.periodic_columns - .iter() - .map(|col| col[self.row_index % col.len()]) - .collect::>() - } -} - -impl<'a, F> AirBuilder for DebugConstraintBuilderWithPeriodicColumns<'a, F> -where - F: Field, -{ - type F = F; - type Expr = F; - type Var = F; - type M = VerticalPair, RowMajorMatrixView<'a, F>>; - - fn main(&self) -> Self::M { - self.main - } - - fn is_first_row(&self) -> Self::Expr { - self.is_first_row - } - - fn is_last_row(&self) -> Self::Expr { - self.is_last_row - } - - /// # Panics - /// This function panics if `size` is not `2`. - fn is_transition_window(&self, size: usize) -> Self::Expr { - if size == 2 { - self.is_transition - } else { - panic!("only supports a window size of 2") - } - } - - fn assert_zero>(&mut self, x: I) { - assert_eq!(x.into(), F::ZERO, "constraints had nonzero value on row {}", self.row_index); - } - - fn assert_eq, I2: Into>(&mut self, x: I1, y: I2) { - let x = x.into(); - let y = y.into(); - assert_eq!(x, y, "values didn't match on row {}: {} != {}", self.row_index, x, y); - } -} - -impl<'a, F> AirBuilderWithPublicValues for DebugConstraintBuilderWithPeriodicColumns<'a, F> -where - F: Field, -{ - type PublicVar = Self::F; - - fn public_values(&self) -> &[Self::F] { - self.public_values - } -} diff --git a/air-script/tests/indexed_trace_access/indexed_trace_access_plonky3.rs b/air-script/tests/indexed_trace_access/indexed_trace_access_plonky3.rs deleted file mode 100644 index 0bd768900..000000000 --- a/air-script/tests/indexed_trace_access/indexed_trace_access_plonky3.rs +++ /dev/null @@ -1,43 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 4; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct TraceAccessAir; - -impl BaseAir for TraceAccessAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for TraceAccessAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for TraceAccessAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for TraceAccessAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[0].into()); - builder.when_transition().assert_zero::<_>(main_next[0].into() - (main_current[1].into() + AB::Expr::ONE)); - } -} \ No newline at end of file diff --git a/air-script/tests/list_comprehension/list_comprehension_nested_plonky3.rs b/air-script/tests/list_comprehension/list_comprehension_nested_plonky3.rs deleted file mode 100644 index 2a8b9181f..000000000 --- a/air-script/tests/list_comprehension/list_comprehension_nested_plonky3.rs +++ /dev/null @@ -1,45 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 2; - -pub const NUM_PUBLIC_VALUES: usize = 1; - -pub struct ListComprehensionAir; - -impl BaseAir for ListComprehensionAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for ListComprehensionAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for ListComprehensionAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for ListComprehensionAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[0].into()); - builder.assert_zero::<_>(main_current[0].into() + main_current[1].into().double() - AB::Expr::from_u64(3)); - builder.assert_zero::<_>(main_current[0].into().double() + main_current[1].into() * AB::Expr::from_u64(3) - AB::Expr::from_u64(5)); - builder.assert_zero::<_>(main_current[0].into() * AB::Expr::from_u64(3) + main_current[1].into() * AB::Expr::from_u64(4) - AB::Expr::from_u64(7)); - } -} \ No newline at end of file diff --git a/air-script/tests/list_comprehension/list_comprehension_plonky3.rs b/air-script/tests/list_comprehension/list_comprehension_plonky3.rs deleted file mode 100644 index b2133e873..000000000 --- a/air-script/tests/list_comprehension/list_comprehension_plonky3.rs +++ /dev/null @@ -1,48 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 16; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct ListComprehensionAir; - -impl BaseAir for ListComprehensionAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for ListComprehensionAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for ListComprehensionAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for ListComprehensionAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[10].into()); - builder.assert_zero::<_>(main_current[0].into() - main_current[2].into()); - builder.assert_zero::<_>(main_current[4].into() - main_current[0].into() * AB::Expr::from_u64(8) * main_current[11].into()); - builder.when_transition().assert_zero::<_>(main_current[4].into() - main_current[0].into() * (main_next[8].into() - main_next[12].into())); - builder.assert_zero::<_>(main_current[6].into() - main_current[0].into() * (main_current[9].into() - main_current[14].into())); - builder.assert_zero::<_>(main_current[1].into() - (main_current[5].into() - main_current[8].into() - main_current[12].into() + AB::Expr::from_u64(10) + main_current[6].into() - main_current[9].into() - main_current[13].into() + AB::Expr::from_u64(20) + main_current[7].into() - main_current[10].into() - main_current[14].into())); - builder.assert_zero::<_>(main_current[14].into() - AB::Expr::from_u64(10)); - } -} \ No newline at end of file diff --git a/air-script/tests/list_folding/list_folding_plonky3.rs b/air-script/tests/list_folding/list_folding_plonky3.rs deleted file mode 100644 index c1cd5e912..000000000 --- a/air-script/tests/list_folding/list_folding_plonky3.rs +++ /dev/null @@ -1,46 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 17; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct ListFoldingAir; - -impl BaseAir for ListFoldingAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for ListFoldingAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for ListFoldingAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for ListFoldingAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[11].into()); - builder.when_transition().assert_zero::<_>(main_next[5].into() - (main_current[9].into() + main_current[10].into() + main_current[11].into() + main_current[12].into() + main_current[13].into() * main_current[14].into() * main_current[15].into() * main_current[16].into())); - builder.when_transition().assert_zero::<_>(main_next[6].into() - (main_current[9].into() + main_current[10].into() + main_current[11].into() + main_current[12].into() + main_current[13].into() * main_current[14].into() * main_current[15].into() * main_current[16].into())); - builder.when_transition().assert_zero::<_>(main_next[7].into() - (main_current[9].into() * main_current[13].into() + main_current[10].into() * main_current[14].into() + main_current[11].into() * main_current[15].into() + main_current[12].into() * main_current[16].into() + (main_current[9].into() + main_current[13].into()) * (main_current[10].into() + main_current[14].into()) * (main_current[11].into() + main_current[15].into()) * (main_current[12].into() + main_current[16].into()))); - builder.when_transition().assert_zero::<_>(main_next[8].into() - (main_current[1].into() + main_current[9].into() * main_current[13].into() + main_current[10].into() * main_current[14].into() + main_current[11].into() * main_current[15].into() + main_current[12].into() * main_current[16].into() + main_current[9].into() * main_current[13].into() + main_current[10].into() * main_current[14].into() + main_current[11].into() * main_current[15].into() + main_current[12].into() * main_current[16].into())); - } -} \ No newline at end of file diff --git a/air-script/tests/periodic_columns/periodic_columns_plonky3.rs b/air-script/tests/periodic_columns/periodic_columns_plonky3.rs deleted file mode 100644 index fb42592e1..000000000 --- a/air-script/tests/periodic_columns/periodic_columns_plonky3.rs +++ /dev/null @@ -1,46 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 3; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct PeriodicColumnsAir; - -impl BaseAir for PeriodicColumnsAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for PeriodicColumnsAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for PeriodicColumnsAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - vec![F::from_u64(1), F::from_u64(0), F::from_u64(0), F::from_u64(0)], - vec![F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(0)], - ] - } -} - -impl Air for PeriodicColumnsAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[0].into()); - builder.assert_zero::<_>(periodic_values[0].into() * (main_current[1].into() + main_current[2].into())); - builder.when_transition().assert_zero::<_>(periodic_values[1].into() * (main_next[0].into() - main_current[0].into())); - } -} \ No newline at end of file diff --git a/air-script/tests/pub_inputs/pub_inputs_plonky3.rs b/air-script/tests/pub_inputs/pub_inputs_plonky3.rs deleted file mode 100644 index cb2ac03b7..000000000 --- a/air-script/tests/pub_inputs/pub_inputs_plonky3.rs +++ /dev/null @@ -1,50 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 4; - -pub const NUM_PUBLIC_VALUES: usize = 32; - -pub struct PubInputsAir; - -impl BaseAir for PubInputsAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for PubInputsAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for PubInputsAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for PubInputsAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[0].into() - public_values[8].into()); - builder.when_first_row().assert_zero::<_>(main_current[1].into() - public_values[9].into()); - builder.when_first_row().assert_zero::<_>(main_current[2].into() - public_values[10].into()); - builder.when_first_row().assert_zero::<_>(main_current[3].into() - public_values[11].into()); - builder.when_last_row().assert_zero::<_>(main_current[0].into() - public_values[12].into()); - builder.when_last_row().assert_zero::<_>(main_current[1].into() - public_values[13].into()); - builder.when_last_row().assert_zero::<_>(main_current[2].into() - public_values[14].into()); - builder.when_last_row().assert_zero::<_>(main_current[3].into() - public_values[15].into()); - builder.when_transition().assert_zero::<_>(main_next[0].into() - (main_current[1].into() + main_current[2].into())); - } -} \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_combine_complex_plonky3.rs b/air-script/tests/selectors/selectors_combine_complex_plonky3.rs deleted file mode 100644 index c8115936b..000000000 --- a/air-script/tests/selectors/selectors_combine_complex_plonky3.rs +++ /dev/null @@ -1,45 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 6; - -pub const NUM_PUBLIC_VALUES: usize = 1; - -pub struct SelectorsAir; - -impl BaseAir for SelectorsAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for SelectorsAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for SelectorsAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for SelectorsAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[5].into()); - builder.assert_zero::<_>((main_current[0].into() + (AB::Expr::ONE - main_current[0].into()) * main_current[1].into()) * (main_current[3].into() - AB::Expr::from_u64(16)) + (AB::Expr::ONE - main_current[0].into()) * (AB::Expr::ONE - main_current[1].into()) * (main_current[4].into() - AB::Expr::from_u64(5))); - builder.assert_zero::<_>((AB::Expr::ONE - main_current[0].into()) * (main_current[5].into() - AB::Expr::from_u64(5)) + main_current[0].into() * (main_current[4].into() - AB::Expr::from_u64(4))); - builder.assert_zero::<_>(main_current[0].into() * (main_current[5].into() - AB::Expr::from_u64(20)) + (AB::Expr::ONE - main_current[0].into()) * main_current[1].into() * (main_current[4].into() - AB::Expr::from_u64(31))); - } -} \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_combine_simple_plonky3.rs b/air-script/tests/selectors/selectors_combine_simple_plonky3.rs deleted file mode 100644 index c981e950b..000000000 --- a/air-script/tests/selectors/selectors_combine_simple_plonky3.rs +++ /dev/null @@ -1,44 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 4; - -pub const NUM_PUBLIC_VALUES: usize = 1; - -pub struct SelectorsAir; - -impl BaseAir for SelectorsAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for SelectorsAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for SelectorsAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for SelectorsAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[3].into()); - builder.when_transition().assert_zero::<_>(main_next[1].into() - main_current[2].into()); - builder.when_transition().assert_zero::<_>(main_current[3].into() * (main_next[0].into() - (main_current[0].into() + main_current[1].into())) + (AB::Expr::ONE - main_current[3].into()) * (main_next[0].into() - main_current[0].into() * main_current[1].into())); - } -} \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs b/air-script/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs deleted file mode 100644 index 1a937a640..000000000 --- a/air-script/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs +++ /dev/null @@ -1,45 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 6; - -pub const NUM_PUBLIC_VALUES: usize = 1; - -pub struct SelectorsAir; - -impl BaseAir for SelectorsAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for SelectorsAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for SelectorsAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for SelectorsAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[5].into()); - builder.assert_zero::<_>((main_current[0].into() + (AB::Expr::ONE - main_current[0].into()) * main_current[1].into()) * main_current[3].into() + (AB::Expr::ONE - main_current[0].into()) * (AB::Expr::ONE - main_current[1].into()) * (main_current[4].into() - AB::Expr::from_u64(8))); - builder.assert_zero::<_>((AB::Expr::ONE - main_current[0].into()) * (main_current[5].into() - AB::Expr::from_u64(8)) + main_current[0].into() * (main_current[4].into() - AB::Expr::from_u64(2))); - builder.assert_zero::<_>(main_current[0].into() * (main_current[5].into() - AB::Expr::from_u64(4)) + (AB::Expr::ONE - main_current[0].into()) * main_current[1].into() * (main_current[4].into() - AB::Expr::from_u64(6))); - } -} \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_plonky3.rs b/air-script/tests/selectors/selectors_plonky3.rs deleted file mode 100644 index 7b0b2f210..000000000 --- a/air-script/tests/selectors/selectors_plonky3.rs +++ /dev/null @@ -1,44 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 4; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct SelectorsAir; - -impl BaseAir for SelectorsAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for SelectorsAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for SelectorsAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for SelectorsAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[3].into()); - builder.when_transition().assert_zero::<_>(main_current[0].into() * (AB::Expr::ONE - main_current[1].into()) * main_next[3].into()); - builder.when_transition().assert_zero::<_>(main_current[0].into() * main_current[1].into() * main_current[2].into() * (main_next[3].into() - main_current[3].into()) + (AB::Expr::ONE - main_current[1].into()) * (AB::Expr::ONE - main_current[2].into()) * (main_next[3].into() - AB::Expr::ONE)); - } -} \ No newline at end of file diff --git a/air-script/tests/selectors/selectors_with_evaluators_plonky3.rs b/air-script/tests/selectors/selectors_with_evaluators_plonky3.rs deleted file mode 100644 index 10fd5fb98..000000000 --- a/air-script/tests/selectors/selectors_with_evaluators_plonky3.rs +++ /dev/null @@ -1,44 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 4; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct SelectorsAir; - -impl BaseAir for SelectorsAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for SelectorsAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for SelectorsAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for SelectorsAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[3].into()); - builder.when_transition().assert_zero::<_>(main_current[0].into() * (AB::Expr::ONE - main_current[1].into()) * main_next[3].into()); - builder.when_transition().assert_zero::<_>(main_current[1].into() * main_current[2].into() * main_current[0].into() * (main_next[3].into() - main_current[3].into()) + (AB::Expr::ONE - main_current[1].into()) * (AB::Expr::ONE - main_current[2].into()) * (main_next[3].into() - AB::Expr::ONE)); - } -} \ No newline at end of file diff --git a/air-script/tests/system/system_plonky3.rs b/air-script/tests/system/system_plonky3.rs deleted file mode 100644 index ca2204da0..000000000 --- a/air-script/tests/system/system_plonky3.rs +++ /dev/null @@ -1,43 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 3; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct SystemAir; - -impl BaseAir for SystemAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for SystemAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for SystemAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for SystemAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[0].into()); - builder.when_transition().assert_zero::<_>(main_next[0].into() - (main_current[0].into() + AB::Expr::ONE)); - } -} \ No newline at end of file diff --git a/air-script/tests/trace_col_groups/trace_col_groups_plonky3.rs b/air-script/tests/trace_col_groups/trace_col_groups_plonky3.rs deleted file mode 100644 index 08bf27975..000000000 --- a/air-script/tests/trace_col_groups/trace_col_groups_plonky3.rs +++ /dev/null @@ -1,44 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 9; - -pub const NUM_PUBLIC_VALUES: usize = 16; - -pub struct TraceColGroupAir; - -impl BaseAir for TraceColGroupAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for TraceColGroupAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for TraceColGroupAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - ] - } -} - -impl Air for TraceColGroupAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[8].into()); - builder.when_transition().assert_zero::<_>(main_next[2].into() - (main_current[2].into() + AB::Expr::ONE)); - builder.when_transition().assert_zero::<_>(main_next[1].into() - (main_current[1].into() - AB::Expr::ONE)); - } -} \ No newline at end of file diff --git a/air-script/tests/variables/variables_plonky3.rs b/air-script/tests/variables/variables_plonky3.rs deleted file mode 100644 index 48ce365c8..000000000 --- a/air-script/tests/variables/variables_plonky3.rs +++ /dev/null @@ -1,48 +0,0 @@ -use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, BaseAirWithPublicValues}; -use p3_matrix::Matrix; -use p3_field::PrimeCharacteristicRing; -use crate::helpers::{AirBuilderWithPeriodicColumns, BaseAirWithPeriodicColumns}; - -pub const NUM_COLUMNS: usize = 4; - -pub const NUM_PUBLIC_VALUES: usize = 32; - -pub struct VariablesAir; - -impl BaseAir for VariablesAir { - fn width(&self) -> usize { - NUM_COLUMNS - } -} - -impl BaseAirWithPublicValues for VariablesAir { - fn num_public_values(&self) -> usize { - NUM_PUBLIC_VALUES - } -} - -impl BaseAirWithPeriodicColumns for VariablesAir { - fn get_periodic_columns(&self) -> Vec> { - vec![ - vec![F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(1), F::from_u64(0)], - ] - } -} - -impl Air for VariablesAir { - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); - let periodic_values = builder.periodic_columns(); - let (main_current, main_next) = ( - main.row_slice(0).unwrap(), - main.row_slice(1).unwrap(), - ); - builder.when_first_row().assert_zero::<_>(main_current[1].into()); - builder.when_last_row().assert_zero::<_>(main_current[1].into() - AB::Expr::ONE); - builder.assert_zero::<_>(main_current[0].into() * main_current[0].into() - main_current[0].into()); - builder.when_transition().assert_zero::<_>(periodic_values[0].into() * (main_next[0].into() - main_current[0].into())); - builder.assert_zero::<_>((AB::Expr::ONE - main_current[0].into()) * (main_current[3].into() - main_current[1].into() - main_current[2].into()) - (AB::Expr::from_u64(6) - (AB::Expr::from_u64(7) - main_current[0].into()))); - builder.when_transition().assert_zero::<_>(main_current[0].into() * (main_current[3].into() - main_current[1].into() * main_current[2].into()) - (AB::Expr::ONE - main_next[0].into())); - } -} \ No newline at end of file diff --git a/air/src/ir/mod.rs b/air/src/ir/mod.rs index 0393558ba..06758392c 100644 --- a/air/src/ir/mod.rs +++ b/air/src/ir/mod.rs @@ -162,7 +162,7 @@ use alloc::collections::BTreeMap; use miden_diagnostics::{SourceSpan, Spanned}; -use crate::graph::AlgebraicGraph; +use crate::{NodeIndex, graph::AlgebraicGraph}; /// The intermediate representation of a complete AirScript program /// @@ -195,6 +195,18 @@ pub struct Air { /// /// Only their name, type, and the first and last boundary constraints are stored here. pub buses: BTreeMap, + /// Buses initial values used for auxiliary trace generation (indexed by bus index) + pub buses_initial_values: BTreeMap, + /// Buses transition expressions used for auxiliary trace generation (indexed by bus index) + /// The tuple contains the numerator and an optional denominator operation (p_prime = numerator + /// if None or numerator / denominator), computed in the `ExpandBuses` Air pass depending on + /// the bus type + /// - for multiset buses: p' = p * columns_inserted_in_bus / columns_removed_from_bus + /// - for logup buses, if u corresponds to the columns inserted when s1 and v to the columns + /// removed when s2 + /// q' = q + s1 / u - s2 / v + /// = (q * u * v + s1 * v - s2 * u) / (u * v) + pub buses_transitions: BTreeMap)>, } impl Default for Air { fn default() -> Self { @@ -217,6 +229,8 @@ impl Air { num_random_values: 0, constraints: Default::default(), buses: Default::default(), + buses_initial_values: Default::default(), + buses_transitions: Default::default(), } } diff --git a/air/src/passes/common_subexpression_elimination.rs b/air/src/passes/common_subexpression_elimination.rs index bc162b0da..38711c81c 100644 --- a/air/src/passes/common_subexpression_elimination.rs +++ b/air/src/passes/common_subexpression_elimination.rs @@ -32,6 +32,28 @@ impl Pass for CommonSubexpressionElimination<'_> { // Update constraints with the new node indices ir.constraints.renumber_and_deduplicate_constraints(&renumbering_map); + // Iterate over all bus transition expression and renumber their node indices + for (_, value) in ir.buses_initial_values.iter_mut() { + let new_value_index = *renumbering_map + .get(value) + .expect("Error: cannot find value index in renumbering map"); + *value = new_value_index; + } + + // Iterate over all bus transition expression and renumber their node indices + for (_, (numerator, denominator)) in ir.buses_transitions.iter_mut() { + let new_numerator_index = *renumbering_map + .get(numerator) + .expect("Error: cannot find numerator index in renumbering map"); + *numerator = new_numerator_index; + if let Some(denominator) = denominator { + let new_denominator_index = *renumbering_map + .get(denominator) + .expect("Error: cannot find denominator index in renumbering map"); + *denominator = new_denominator_index; + } + } + Ok(ir) } } diff --git a/air/src/passes/expand_buses.rs b/air/src/passes/expand_buses.rs index ff02487dd..fb5a33513 100644 --- a/air/src/passes/expand_buses.rs +++ b/air/src/passes/expand_buses.rs @@ -63,6 +63,7 @@ impl Pass for BusOpExpand<'_> { bus_ops, bus_access, bus_access_with_offset, + bus_index, ); }, BusType::Logup => { @@ -71,6 +72,7 @@ impl Pass for BusOpExpand<'_> { bus_ops, bus_access, bus_access_with_offset, + bus_index, ); }, } @@ -139,6 +141,19 @@ impl<'a> BusOpExpand<'a> { }; // Store the generated constraint ir.constraints.insert_constraint(TraceSegmentId::Aux, root, domain); + + // Also store the initial value for auxiliary trace generation + if boundary == Boundary::First { + // TODO: May be invalid? For now, we put its value to zero + if let BusBoundary::PublicInputTable(_) = bus_boundary { + let value = ir + .constraint_graph_mut() + .insert_node(Operation::Value(crate::Value::Constant(0))); + ir.buses_initial_values.insert(bus_index, value); + } else { + ir.buses_initial_values.insert(bus_index, value); + } + } } /// Helper function to expand the integrity constraint of a multiset bus @@ -148,6 +163,7 @@ impl<'a> BusOpExpand<'a> { bus_ops: Vec, bus_access: NodeIndex, bus_access_with_offset: NodeIndex, + bus_index: usize, ) { let graph = ir.constraint_graph_mut(); @@ -234,8 +250,14 @@ impl<'a> BusOpExpand<'a> { // 6. Create the resulting constraint and insert it into the graph let root = graph.insert_node(Operation::Sub(p_prod, p_prime_prod)); - ir.constraints - .insert_constraint(TraceSegmentId::Aux, root, ConstraintDomain::EveryRow); + ir.constraints.insert_constraint( + TraceSegmentId::Aux, + root, + ConstraintDomain::EveryFrame(2), + ); + + // Also store the expression to computed p_prime for auxiliary trace generation + ir.buses_transitions.insert(bus_index, (p_prod, p_prime_factor)); } /// Helper function to expand the integrity constraint of a logup bus @@ -245,6 +267,7 @@ impl<'a> BusOpExpand<'a> { bus_ops: Vec, bus_access: NodeIndex, bus_access_with_offset: NodeIndex, + bus_index: usize, ) { let graph = ir.constraint_graph_mut(); // Example: @@ -372,7 +395,23 @@ impl<'a> BusOpExpand<'a> { // 5. Create the resulting constraint let root = graph.insert_node(Operation::Sub(q_term, q_prime_term)); - ir.constraints - .insert_constraint(TraceSegmentId::Aux, root, ConstraintDomain::EveryRow); + + // Also store the expression to computed q_prime for auxiliary trace generation + // Note: TODO: Potentially adapt CSE to handle this properly, otherwise indices might + // change... + let numerator = match terms_removed_from_bus { + Some(terms_removed_from_bus) => { + graph.insert_node(Operation::Sub(q_term, terms_removed_from_bus)) + }, + None => q_term, + }; + + ir.constraints.insert_constraint( + TraceSegmentId::Aux, + root, + ConstraintDomain::EveryFrame(2), + ); + + ir.buses_transitions.insert(bus_index, (numerator, total_factors)); } } diff --git a/codegen/plonky3/src/air/boundary_constraints.rs b/codegen/plonky3/src/air/boundary_constraints.rs index 4c8fd0047..aba2206b0 100644 --- a/codegen/plonky3/src/air/boundary_constraints.rs +++ b/codegen/plonky3/src/air/boundary_constraints.rs @@ -1,24 +1,28 @@ use air_ir::{Air, TraceSegmentId}; use codegen::Function; -use super::Codegen; +use crate::air::graph::constraint_to_string; /// Adds the main boundary constraints to the generated code. pub(super) fn add_main_boundary_constraints(eval_func: &mut Function, ir: &Air) { + eval_func.line(""); + eval_func.line("// Main boundary constraints"); for constraint in ir.boundary_constraints(TraceSegmentId::Main) { - let expr_root = constraint.node_index(); - - let expr_root_string = expr_root.to_string(ir); + let assertion = constraint_to_string(ir, constraint, true); + eval_func.line(assertion); + } +} - let assertion = match constraint.domain() { - air_ir::ConstraintDomain::FirstRow => { - format!("builder.when_first_row().assert_zero::<_>({expr_root_string});") - }, - air_ir::ConstraintDomain::LastRow => { - format!("builder.when_last_row().assert_zero::<_>({expr_root_string});") - }, - _ => unreachable!("Boundary constraints can only be applied to the first or last row"), - }; +/// Adds the aux boundary constraints to the generated code. +pub(super) fn add_aux_boundary_constraints(eval_func: &mut Function, ir: &Air) { + eval_func.line(""); + eval_func.line("// Aux boundary constraints"); + for constraint in ir.boundary_constraints(TraceSegmentId::Aux) { + let assertion = constraint_to_string(ir, constraint, true); eval_func.line(assertion); + + // TODO: better check assumptions on aux boundary constraints: + // - start only with empty buses, + // - end with values derived from builder.aux_bus_boundary_value() } } diff --git a/codegen/plonky3/src/air/graph.rs b/codegen/plonky3/src/air/graph.rs index fb9e816b6..f9c27ed7c 100644 --- a/codegen/plonky3/src/air/graph.rs +++ b/codegen/plonky3/src/air/graph.rs @@ -1,4 +1,8 @@ -use air_ir::{Air, NodeIndex, Operation, TraceAccess, Value}; +use air_ir::{ + Air, ConstraintDomain, ConstraintRoot, NodeIndex, Operation, TraceAccess, TraceSegmentId, Value, +}; + +use crate::air::ElemType; // RUST STRING GENERATION FOR THE CONSTRAINT GRAPH // ================================================================================================ @@ -6,50 +10,66 @@ use air_ir::{Air, NodeIndex, Operation, TraceAccess, Value}; /// Code generation trait for generating Rust code strings from IR types related to constraints and /// the [AlgebraicGraph]. pub trait Codegen { - fn to_string(&self, ir: &Air) -> String; + fn to_string(&self, ir: &Air, elem_type: ElemType) -> String; } impl Codegen for TraceAccess { - fn to_string(&self, _ir: &Air) -> String { + fn to_string(&self, _ir: &Air, elem_type: ElemType) -> String { let frame = self.segment.to_string(); let row_offset = match self.row_offset { 0 => { - format!("current[{}].into()", self.column) + format!("current[{}]", self.column) }, 1 => { - format!("next[{}].into()", self.column) + format!("next[{}]", self.column) }, _ => panic!("Plonky3 doesn't support row offsets greater than 1."), }; - format!("{frame}_{row_offset}") + match elem_type { + ElemType::Base => format!("{frame}_{row_offset}.clone().into()"), + ElemType::Ext => format!("AB::ExprEF::from({frame}_{row_offset}.clone().into())"), + ElemType::ExtFieldElem => format!("EF::from({frame}_{row_offset}.clone())"), + } } } impl Codegen for NodeIndex { - fn to_string(&self, ir: &Air) -> String { + fn to_string(&self, ir: &Air, elem_type: ElemType) -> String { let op = ir.constraint_graph().node(self).op(); - op.to_string(ir) + op.to_string(ir, elem_type) } } impl Codegen for Operation { - fn to_string(&self, ir: &Air) -> String { + fn to_string(&self, ir: &Air, elem_type: ElemType) -> String { match self { - Operation::Value(value) => value.to_string(ir), - Operation::Add(..) => binary_op_to_string(ir, self), - Operation::Sub(..) => binary_op_to_string(ir, self), - Operation::Mul(..) => binary_op_to_string(ir, self), + Operation::Value(value) => value.to_string(ir, elem_type), + Operation::Add(..) => binary_op_to_string(ir, elem_type, self), + Operation::Sub(..) => binary_op_to_string(ir, elem_type, self), + Operation::Mul(..) => binary_op_to_string(ir, elem_type, self), } } } impl Codegen for Value { - fn to_string(&self, ir: &Air) -> String { + fn to_string(&self, ir: &Air, elem_type: ElemType) -> String { match self { - Value::Constant(0) => format!("AB::Expr::ZERO"), - Value::Constant(1) => format!("AB::Expr::ONE"), - Value::Constant(value) => format!("AB::Expr::from_u64({value})"), - Value::TraceAccess(trace_access) => trace_access.to_string(ir), + Value::Constant(0) => match elem_type { + ElemType::Base => format!("AB::Expr::ZERO"), + ElemType::Ext => format!("AB::ExprEF::ZERO"), + ElemType::ExtFieldElem => format!("EF::ZERO"), + }, + Value::Constant(1) => match elem_type { + ElemType::Base => format!("AB::Expr::ONE"), + ElemType::Ext => format!("AB::ExprEF::ONE"), + ElemType::ExtFieldElem => format!("EF::ONE"), + }, + Value::Constant(value) => match elem_type { + ElemType::Base => format!("AB::Expr::from_u64({value})"), + ElemType::Ext => format!("AB::ExprEF::from_u64({value})"), + ElemType::ExtFieldElem => format!("EF::from_u64({value})"), + }, + Value::TraceAccess(trace_access) => trace_access.to_string(ir, elem_type), Value::PublicInput(air_ir::PublicInputAccess { name, index }) => { let get_public_input_offset = |name: &str| { ir.public_inputs() @@ -62,37 +82,57 @@ impl Codegen for Value { Value::PeriodicColumn(pc) => { let index = ir.periodic_columns.iter().position(|(qid, _)| qid == &pc.name).unwrap(); - format!("periodic_values[{index}].into()") + match elem_type { + ElemType::Base => format!("AB::Expr::from(periodic_values[{index}].clone())"), + ElemType::Ext => { + format!("AB::ExprEF::from(periodic_values[{index}].clone().into())") + }, + ElemType::ExtFieldElem => { + format!("AB::EF::from(periodic_values[{index}].clone())") + }, + } }, - _ => todo!(), - /*Value::PublicInputTable(air_ir::PublicInputTableAccess { - table_name, - bus_type, - num_cols: _, - }) => { - format!("reduced_{table_name}_{bus_type}") + Value::PublicInputTable(public_input_table_access) => { + let idx = ir + .reduced_public_input_table_accesses() + .iter() + .position(|pi| pi == public_input_table_access) + .unwrap(); + format!("aux_bus_boundary_values[{idx}].into()") }, Value::RandomValue(idx) => { - format!("aux_rand_elements.rand_elements()[{idx}]") - },*/ + if *idx == 0 { + if let ElemType::ExtFieldElem = elem_type { + format!("alpha") + } else { + format!("alpha.into()") + } + } else { + if let ElemType::ExtFieldElem = elem_type { + format!("beta_challenges[{}]", idx - 1) + } else { + format!("beta_challenges[{}].into()", idx - 1) + } + } + }, } } } /// Returns a string representation of a binary operation. -fn binary_op_to_string(ir: &Air, op: &Operation) -> String { +fn binary_op_to_string(ir: &Air, elem_type: ElemType, op: &Operation) -> String { match op { Operation::Add(l_idx, r_idx) => { - let lhs = l_idx.to_string(ir); - let rhs = r_idx.to_string(ir); + let lhs = l_idx.to_string(ir, elem_type); + let rhs = r_idx.to_string(ir, elem_type); format!("{lhs} + {rhs}") }, Operation::Sub(l_idx, r_idx) => { - let lhs = l_idx.to_string(ir); + let lhs = l_idx.to_string(ir, elem_type); let rhs = if ir.constraint_graph().node(r_idx).op().precedence() <= op.precedence() { - format!("({})", r_idx.to_string(ir)) + format!("({})", r_idx.to_string(ir, elem_type)) } else { - r_idx.to_string(ir) + r_idx.to_string(ir, elem_type) }; format!("{lhs} - {rhs}") }, @@ -101,14 +141,14 @@ fn binary_op_to_string(ir: &Air, op: &Operation) -> String { let rhs_op = ir.constraint_graph().node(r_idx).op(); let lhs = if lhs_op.precedence() < op.precedence() { - format!("({})", l_idx.to_string(ir)) + format!("({})", l_idx.to_string(ir, elem_type)) } else { - l_idx.to_string(ir) + l_idx.to_string(ir, elem_type) }; let rhs = if rhs_op.precedence() < op.precedence() { - format!("({})", r_idx.to_string(ir)) + format!("({})", r_idx.to_string(ir, elem_type)) } else { - r_idx.to_string(ir) + r_idx.to_string(ir, elem_type) }; match (lhs_op, rhs_op) { @@ -120,3 +160,63 @@ fn binary_op_to_string(ir: &Air, op: &Operation) -> String { _ => panic!("unsupported operation"), } } + +/// Recursively determines if the expression depends on extension field values (i.e., aux trace, +/// random values, periodic columns, or public input tables). +pub fn needs_extension_field(ir: &Air, expr_root: NodeIndex) -> bool { + let op = ir.constraint_graph().node(&expr_root).op(); + match op { + Operation::Value(value) => match value { + Value::TraceAccess(trace_access) => trace_access.segment == TraceSegmentId::Aux, + Value::Constant(_) => false, + Value::PeriodicColumn(_) => true, + Value::PublicInput(_) => false, + Value::PublicInputTable(_) => true, + Value::RandomValue(_) => true, + }, + Operation::Add(lhs, rhs) | Operation::Sub(lhs, rhs) | Operation::Mul(lhs, rhs) => { + needs_extension_field(ir, *lhs) || needs_extension_field(ir, *rhs) + }, + } +} + +/// Returns the appropriate domain flag string for the given [ConstraintDomain]. +fn get_boundary_domain_flag_str(domain: &ConstraintDomain) -> &'static str { + match domain { + ConstraintDomain::FirstRow => ".when_first_row()", + ConstraintDomain::LastRow => ".when_last_row()", + _ => unreachable!("Invalid domain for boundary constraints"), + } +} + +/// Returns the appropriate domain flag string for the given [ConstraintDomain]. +fn get_integrity_domain_flag_str(domain: &ConstraintDomain) -> &'static str { + match domain { + ConstraintDomain::EveryFrame(_) => ".when_transition()", + ConstraintDomain::EveryRow => "", + _ => unreachable!("Invalid domain for integrity constraints"), + } +} + +pub fn constraint_to_string(ir: &Air, constraint: &ConstraintRoot, in_boundary: bool) -> String { + let expr_root = constraint.node_index(); + let needs_extension_field = needs_extension_field(ir, *expr_root); + let elem_type = if needs_extension_field { + ElemType::Ext + } else { + ElemType::Base + }; + let expr_root_string = expr_root.to_string(ir, elem_type); + + // If the constraint is a transition constraint (depends on the next row), we do not + // evaluate it in the last row, with the `when_transition` method. + let domain_flag = if in_boundary { + get_boundary_domain_flag_str(&constraint.domain()) + } else { + get_integrity_domain_flag_str(&constraint.domain()) + }; + let extension_field_flag = if needs_extension_field { "_ext" } else { "" }; + let assertion = + format!("builder{domain_flag}.assert_zero{extension_field_flag}({expr_root_string});"); + assertion +} diff --git a/codegen/plonky3/src/air/integrity_constraints.rs b/codegen/plonky3/src/air/integrity_constraints.rs index 0d79dfcf0..bdcce45e1 100644 --- a/codegen/plonky3/src/air/integrity_constraints.rs +++ b/codegen/plonky3/src/air/integrity_constraints.rs @@ -1,22 +1,24 @@ -use air_ir::{Air, ConstraintDomain, TraceSegmentId}; +use air_ir::{Air, TraceSegmentId}; use codegen::Function; -use super::Codegen; +use crate::air::graph::constraint_to_string; /// Adds the main integrity constraints to the generated code. pub(super) fn add_main_integrity_constraints(eval_func: &mut Function, ir: &Air) { + eval_func.line(""); + eval_func.line("// Main integrity/transition constraints"); for constraint in ir.integrity_constraints(TraceSegmentId::Main) { - let expr_root = constraint.node_index(); - let expr_root_string = expr_root.to_string(ir); - - // If the constraint is a transition constraint (depends on the next row), we do not - // evaluate it in the last row, with the `when_transition` method. - let assertion = if let ConstraintDomain::EveryFrame(_) = constraint.domain() { - format!("builder.when_transition().assert_zero::<_>({expr_root_string});") - } else { - format!("builder.assert_zero::<_>({expr_root_string});") - }; + let assertion = constraint_to_string(ir, constraint, false); + eval_func.line(assertion); + } +} +/// Adds the aux integrity constraints to the generated code. +pub(super) fn add_aux_integrity_constraints(eval_func: &mut Function, ir: &Air) { + eval_func.line(""); + eval_func.line("// Aux integrity/transition constraints"); + for constraint in ir.integrity_constraints(TraceSegmentId::Aux) { + let assertion = constraint_to_string(ir, constraint, false); eval_func.line(assertion); } } diff --git a/codegen/plonky3/src/air/mod.rs b/codegen/plonky3/src/air/mod.rs index a11287f87..98b881f70 100644 --- a/codegen/plonky3/src/air/mod.rs +++ b/codegen/plonky3/src/air/mod.rs @@ -1,16 +1,23 @@ mod boundary_constraints; mod graph; -use graph::Codegen; mod integrity_constraints; use air_ir::Air; use super::Scope; use crate::air::{ - boundary_constraints::add_main_boundary_constraints, - integrity_constraints::add_main_integrity_constraints, + boundary_constraints::{add_aux_boundary_constraints, add_main_boundary_constraints}, + graph::Codegen, + integrity_constraints::{add_aux_integrity_constraints, add_main_integrity_constraints}, }; +#[derive(Debug, Clone, Copy)] +pub enum ElemType { + Base, + Ext, + ExtFieldElem, +} + // HELPERS TO GENERATE AN IMPLEMENTATION OF THE PLONKY3 AIR TRAIT // ================================================================================================ @@ -19,78 +26,233 @@ use crate::air::{ pub(super) fn add_air(scope: &mut Scope, ir: &Air) { let name = ir.name(); + // add the constants needed (outside any traits for object safety). + add_constants(scope, ir); + // add the Air struct and its base implementation. add_air_struct(scope, ir, name); - // add Plonky3 AirBuilder trait implementation for the provided AirIR. - add_air_trait(scope, ir, name); + // add the aux trace generation utils if needed + if ir.num_random_values > 0 { + add_aux_trace_utils(scope, ir, name); + } } -/// Updates the provided scope with a custom Air struct. -fn add_air_struct(scope: &mut Scope, ir: &Air, name: &str) { - scope.raw(format!("pub const NUM_COLUMNS: usize = {};", ir.trace_segment_widths[0])); - +/// Updates the provided scope with constants needed for the custom Air struct and trait +/// implementations. +fn add_constants(scope: &mut Scope, ir: &Air) { + let main_width = ir.trace_segment_widths[0]; + let aux_width = ir.trace_segment_widths.get(1).cloned().unwrap_or(0); + let num_periodic_values = ir.periodic_columns().count(); + let period = ir.periodic_columns().map(|col| col.period()).max().unwrap_or(0); let num_public_values = ir.public_inputs().map(|public_input| public_input.size()).sum::(); - scope.raw(format!("pub const NUM_PUBLIC_VALUES: usize = {num_public_values};")); + let max_beta_challenge_power = ir.num_random_values.saturating_sub(1); + + let constants = [ + format!("pub const MAIN_WIDTH: usize = {main_width};"), + format!("pub const AUX_WIDTH: usize = {aux_width};"), + format!("pub const NUM_PERIODIC_VALUES: usize = {num_periodic_values};"), + format!("pub const PERIOD: usize = {period};"), + format!("pub const NUM_PUBLIC_VALUES: usize = {num_public_values};"), + format!("pub const MAX_BETA_CHALLENGE_POWER: usize = {max_beta_challenge_power};"), + ]; + scope.raw(constants.join("\n")); +} + +/// Updates the provided scope with a custom Air struct. +fn add_air_struct(scope: &mut Scope, ir: &Air, name: &str) { // define the custom Air struct. scope.new_struct(name).vis("pub"); - // add the custom BaseAir implementation block - let base_air_impl = scope.new_impl(name).generic("F").impl_trait("BaseAir"); - base_air_impl.new_fn("width").arg_ref_self().ret("usize").line("NUM_COLUMNS"); + // add the custom MidenAir implementation block + let miden_air_impl = + scope.new_impl(name).generic("F").generic("EF").impl_trait("MidenAir"); - // add the custom BaseAirWithPublicValues implementation block - let base_air_with_public_values_impl = - scope.new_impl(name).generic("F").impl_trait("BaseAirWithPublicValues"); - base_air_with_public_values_impl - .new_fn("num_public_values") - .arg_ref_self() - .ret("usize") - .line("NUM_PUBLIC_VALUES"); - - // add the custom BaseAirWithPeriodicColumns implementation block - let base_air_with_periodic_columns_impl = scope - .new_impl(name) - .generic("F: PrimeCharacteristicRing") - .impl_trait("BaseAirWithPeriodicColumns"); - let base_air_with_periodic_columns_impl_func = base_air_with_periodic_columns_impl - .new_fn("get_periodic_columns") - .arg_ref_self() - .ret("Vec>"); - base_air_with_periodic_columns_impl_func.line("vec!["); - - for col in ir.periodic_columns() { - let values_str = col.values - .iter() - .map(|v| format!("F::from_u64({v})")) // or use a custom formatter if needed - .collect::>() - .join(", "); - base_air_with_periodic_columns_impl_func.line(format!(" vec![{values_str}],")); + if ir.num_random_values > 0 || ir.periodic_columns().count() > 0 { + miden_air_impl.bound("F", "Field").bound("EF", "ExtensionField"); } - base_air_with_periodic_columns_impl_func.line("]"); -} -/// Updates the provided scope with the custom Air struct and an Air trait implementation based on -/// the provided AirIR. -fn add_air_trait(scope: &mut Scope, ir: &Air, name: &str) { - // add the implementation block for the Air trait. - let air_impl = scope - .new_impl(name) - .generic("AB: AirBuilderWithPublicValues + AirBuilderWithPeriodicColumns") - .impl_trait("Air"); + // add the width function + miden_air_impl.new_fn("width").arg_ref_self().ret("usize").line("MAIN_WIDTH"); - let eval_func = air_impl.new_fn("eval").arg_ref_self().arg("builder", "&mut AB"); - eval_func.line("let main = builder.main();"); + // add the num_public_values function if needed + if ir.periodic_columns().count() > 0 { + // add the custom BaseAirWithPublicValues implementation block + miden_air_impl + .new_fn("num_public_values") + .arg_ref_self() + .ret("usize") + .line("NUM_PUBLIC_VALUES"); + } + + // add the periodic_table function if needed + if ir.periodic_columns().count() > 0 { + let periodic_table_func = + miden_air_impl.new_fn("periodic_table").arg_ref_self().ret("Vec>"); + periodic_table_func.line("vec!["); + for col in ir.periodic_columns() { + let values_str = col.values + .iter() + .map(|v| format!("F::from_u64({v})")) // or use a custom formatter if needed + .collect::>() + .join(", "); + periodic_table_func.line(format!(" vec![{values_str}],")); + } + periodic_table_func.line("]"); + } + + // add the num_randomness and aux_width functions if needed + if ir.num_random_values > 0 { + miden_air_impl + .new_fn("num_randomness") + .arg_ref_self() + .ret("usize") + .line("1 + MAX_BETA_CHALLENGE_POWER"); + + miden_air_impl.new_fn("aux_width").arg_ref_self().ret("usize").line("AUX_WIDTH"); + } + + // add the build_aux_trace function if needed + if ir.num_random_values > 0 { + let build_aux_trace_func = miden_air_impl + .new_fn("build_aux_trace") + .arg_ref_self() + .arg("_main", "&RowMajorMatrix") + .arg("_challenges", "&[EF]") + .ret("Option>"); + build_aux_trace_func.line("// Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders."); + build_aux_trace_func.line(""); + build_aux_trace_func.line("let num_rows = _main.height();"); + build_aux_trace_func.line("let trace_length = num_rows * AUX_WIDTH;"); + build_aux_trace_func.line("let mut long_trace = EF::zero_vec(trace_length);"); + build_aux_trace_func.line("let mut trace = RowMajorMatrix::new(long_trace, AUX_WIDTH);"); + build_aux_trace_func.line("let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[EF; AUX_WIDTH]>() };"); + build_aux_trace_func.line("assert!(prefix.is_empty(), \"Alignment should match\");"); + build_aux_trace_func.line("assert!(suffix.is_empty(), \"Alignment should match\");"); + build_aux_trace_func.line("assert_eq!(rows.len(), num_rows);"); + build_aux_trace_func.line("// Initialize first row"); + build_aux_trace_func.line("let initial_values = Self::buses_initial_values::();"); + build_aux_trace_func.line("for j in 0..AUX_WIDTH {"); + build_aux_trace_func.line(" rows[0][j] = initial_values[j];"); + build_aux_trace_func.line("}"); + build_aux_trace_func.line("// Fill subsequent rows using direct access to the rows array"); + build_aux_trace_func.line("for i in 0..num_rows-1 {"); + build_aux_trace_func.line(" let i_next = (i + 1) % num_rows;"); + build_aux_trace_func.line(" let main_local = _main.row_slice(i).unwrap(); // i < height so unwrap should never fail."); + build_aux_trace_func.line(" let main_next = _main.row_slice(i_next).unwrap(); // i_next < height so unwrap should never fail."); + build_aux_trace_func.line(" let main = VerticalPair::new("); + build_aux_trace_func.line(" RowMajorMatrixView::new_row(&*main_local),"); + build_aux_trace_func.line(" RowMajorMatrixView::new_row(&*main_next),"); + build_aux_trace_func.line(" );"); + build_aux_trace_func.line(format!(" let periodic_values: [_; NUM_PERIODIC_VALUES] = <{name} as MidenAir>::periodic_table(self).iter().map(|col| col[i % col.len()]).collect::>().try_into().expect(\"Wrong number of periodic values\");")); + build_aux_trace_func.line(" let prev_row = &rows[i];"); + build_aux_trace_func.line(" let next_row = Self::buses_transitions::("); + build_aux_trace_func.line(" &main,"); + build_aux_trace_func.line(" _challenges,"); + build_aux_trace_func.line(" &periodic_values,"); + build_aux_trace_func.line(" prev_row,"); + build_aux_trace_func.line(" );"); + build_aux_trace_func.line(" for j in 0..AUX_WIDTH {"); + build_aux_trace_func.line(" rows[i+1][j] = next_row[j];"); + build_aux_trace_func.line(" }"); + build_aux_trace_func.line("}"); + build_aux_trace_func.line("Some(trace)"); + } + + // add the eval function + let eval_func = miden_air_impl + .new_fn("eval") + .generic("AB") + .bound("AB", "MidenAirBuilder") + .arg_ref_self() + .arg("builder", "&mut AB"); eval_func.line("let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect(\"Wrong number of public values\");"); - eval_func.line("let periodic_values = builder.periodic_columns();"); + eval_func.line("let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect(\"Wrong number of periodic values\");"); + eval_func.line("let preprocessed = builder.preprocessed();"); + + eval_func.line("let main = builder.main();"); eval_func.line("let (main_current, main_next) = ("); eval_func.line(" main.row_slice(0).unwrap(),"); eval_func.line(" main.row_slice(1).unwrap(),"); eval_func.line(");"); + // Only add aux if there are random values + if ir.num_random_values > 0 { + eval_func.line("let (&alpha, beta_challenges) = builder.permutation_randomness().split_first().expect(\"Wrong number of randomness\");"); + eval_func.line("let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect(\"Wrong number of randomness\");"); + eval_func.line("let aux_bus_boundary_values: [_; AUX_WIDTH] = builder.aux_bus_boundary_values().try_into().expect(\"Wrong number of aux bus boundary values\");"); + eval_func.line("let aux = builder.permutation();"); + eval_func.line("let (aux_current, aux_next) = ("); + eval_func.line(" aux.row_slice(0).unwrap(),"); + eval_func.line(" aux.row_slice(1).unwrap(),"); + eval_func.line(");"); + } + add_main_boundary_constraints(eval_func, ir); add_main_integrity_constraints(eval_func, ir); + + add_aux_boundary_constraints(eval_func, ir); + + add_aux_integrity_constraints(eval_func, ir); +} + +/// Updates the provided scope with aux trace generation utilities. +fn add_aux_trace_utils(scope: &mut Scope, ir: &Air, name: &str) { + let aux_generation_impl = scope.new_impl(name); + + // add the bus_initial_values function + let buses_initial_values_func = aux_generation_impl + .new_fn("buses_initial_values") + .generic("F") + .generic("EF") + .bound("F", "Field") + .bound("EF", "ExtensionField") + .ret("Vec"); + buses_initial_values_func.line("vec!["); + for (_bus_id, value) in ir.buses_initial_values.iter() { + let value_str = value.to_string(ir, ElemType::ExtFieldElem); + + buses_initial_values_func.line(format!(" {},", value_str)); + } + buses_initial_values_func.line("]"); + + // add the bus_transitions function + let buses_transitions_func = aux_generation_impl + .new_fn("buses_transitions") + .generic("F") + .generic("EF") + .bound("F", "Field") + .bound("EF", "ExtensionField") + .arg("main", "&VerticalPair, RowMajorMatrixView>") + .arg("challenges", "&[EF]") + .arg("periodic_evals", "&[F]") + .arg("aux_current", "&[EF]") + .ret("Vec"); + + buses_transitions_func.line("let (main_current, main_next) = ("); + buses_transitions_func.line(" main.row_slice(0).unwrap(),"); + buses_transitions_func.line(" main.row_slice(1).unwrap(),"); + buses_transitions_func.line(");"); + buses_transitions_func.line("let (&alpha, beta_challenges) = challenges.split_first().expect(\"Wrong number of randomness\");"); + buses_transitions_func.line("let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect(\"Wrong number of randomness\");"); + + buses_transitions_func.line("let periodic_values: [_; NUM_PERIODIC_VALUES] = periodic_evals.try_into().expect(\"Wrong number of periodic values\");"); + + buses_transitions_func.line("vec!["); + for (_bus_id, (numerator, denominator)) in ir.buses_transitions.iter() { + let numerator_str = numerator.to_string(ir, ElemType::ExtFieldElem); + + let aux_next_value_str = if let Some(denom) = denominator { + let denominator_str = denom.to_string(ir, ElemType::ExtFieldElem); + format!("({}) * ({}).inverse()", numerator_str, denominator_str) + } else { + numerator_str + }; + + buses_transitions_func.line(format!(" {},", aux_next_value_str)); + } + buses_transitions_func.line("]"); } diff --git a/codegen/plonky3/src/imports.rs b/codegen/plonky3/src/imports.rs index 28ef3501b..8655c7161 100644 --- a/codegen/plonky3/src/imports.rs +++ b/codegen/plonky3/src/imports.rs @@ -3,13 +3,13 @@ use super::Scope; /// Adds the required imports to the provided scope. pub(super) fn add_imports(scope: &mut Scope) { // add plonky3 imports - scope.import("p3_air", "Air"); - scope.import("p3_air", "AirBuilder"); - scope.import("p3_air", "AirBuilderWithPublicValues"); - scope.import("p3_air", "BaseAir"); - scope.import("p3_air", "BaseAirWithPublicValues"); - scope.import("p3_matrix", "Matrix"); + scope.import("p3_field", "ExtensionField"); + scope.import("p3_field", "Field"); scope.import("p3_field", "PrimeCharacteristicRing"); - scope.import("crate::helpers", "AirBuilderWithPeriodicColumns"); - scope.import("crate::helpers", "BaseAirWithPeriodicColumns"); + scope.import("p3_matrix", "Matrix"); + scope.import("p3_matrix::dense", "RowMajorMatrixView"); + scope.import("p3_matrix::stack", "VerticalPair"); + scope.import("p3_miden_air", "MidenAir"); + scope.import("p3_miden_air", "MidenAirBuilder"); + scope.import("p3_miden_air", "RowMajorMatrix"); } diff --git a/scripts/generate_all_e2e_tests.sh b/scripts/generate_all_e2e_tests.sh new file mode 100644 index 000000000..b0808b9ec --- /dev/null +++ b/scripts/generate_all_e2e_tests.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# Notes: +# - Run from root of repository +# - We avoid looping on all found air-script/src/tests/**/*.air files to make it easier to notice changes + +cargo build --release + +# Winterfell Backend + +./target/release/airc transpile --target winterfell ./air-script/src/tests/binary/binary.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/bitwise/bitwise.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/buses/buses_complex.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/buses/buses_simple.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/buses/buses_simple_with_evaluators.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/buses/buses_varlen_boundary_both.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/buses/buses_varlen_boundary_last.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/buses/buses_varlen_boundary_first.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/constant_in_range/constant_in_range.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/constants/constants.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/constraint_comprehension/constraint_comprehension.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/evaluators/evaluators.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/fibonacci/fibonacci.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/functions/functions_simple.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/functions/functions_complex.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/indexed_trace_access/indexed_trace_access.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/list_comprehension/list_comprehension_nested.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/list_comprehension/list_comprehension.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/list_folding/list_folding.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/periodic_columns/periodic_columns.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/pub_inputs/pub_inputs.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/selectors/selectors_combine_complex.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/selectors/selectors_combine_simple.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/selectors/selectors_combine_with_list_comprehensions.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/selectors/selectors_with_evaluators.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/selectors/selectors.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/system/system.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/trace_col_groups/trace_col_groups.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/variables/variables.air + +# Plonky3 Backend +./target/release/airc transpile --target plonky3 ./air-script/src/tests/binary/binary.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/bitwise/bitwise.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/buses/buses_complex.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/buses/buses_simple.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/buses/buses_simple_with_evaluators.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/buses/buses_varlen_boundary_both.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/buses/buses_varlen_boundary_last.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/buses/buses_varlen_boundary_first.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/computed_indices/computed_indices_complex.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/computed_indices/computed_indices_simple.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/constant_in_range/constant_in_range.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/constants/constants.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/constraint_comprehension/constraint_comprehension.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/evaluators/evaluators.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/fibonacci/fibonacci.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/functions/functions_simple.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/functions/functions_complex.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/indexed_trace_access/indexed_trace_access.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/list_comprehension/list_comprehension_nested.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/list_comprehension/list_comprehension.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/list_folding/list_folding.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/periodic_columns/periodic_columns.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/pub_inputs/pub_inputs.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/selectors/selectors_combine_complex.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/selectors/selectors_combine_simple.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/selectors/selectors_combine_with_list_comprehensions.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/selectors/selectors_with_evaluators.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/selectors/selectors.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/system/system.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/trace_col_groups/trace_col_groups.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/variables/variables.air From 805649ff66baa182138807d4c4acab8297535edf Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Mon, 8 Dec 2025 07:58:32 +0100 Subject: [PATCH 22/83] tests: update tests after merge, add plonky3 E2E tests codegen and update script --- .../evaluators_nested_slice_call_plonky3.rs | 66 +++++++++++++++++++ .../evaluators/evaluators_slice_plonky3.rs | 66 +++++++++++++++++++ air-script/src/tests/plonky3.rs | 32 +++++++++ air-script/src/tests/winterfell.rs | 32 +++++++++ scripts/generate_all_e2e_tests.sh | 4 ++ 5 files changed, 200 insertions(+) create mode 100644 air-script/src/tests/evaluators/evaluators_nested_slice_call_plonky3.rs create mode 100644 air-script/src/tests/evaluators/evaluators_slice_plonky3.rs diff --git a/air-script/src/tests/evaluators/evaluators_nested_slice_call_plonky3.rs b/air-script/src/tests/evaluators/evaluators_nested_slice_call_plonky3.rs new file mode 100644 index 000000000..8dfd1d4bf --- /dev/null +++ b/air-script/src/tests/evaluators/evaluators_nested_slice_call_plonky3.rs @@ -0,0 +1,66 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 20; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct EvaluatorsSliceAir; + +impl MidenAir for EvaluatorsSliceAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into()); + builder.when_first_row().assert_zero(main_current[1].clone().into()); + builder.when_first_row().assert_zero(main_current[2].clone().into()); + builder.when_first_row().assert_zero(main_current[3].clone().into()); + builder.when_first_row().assert_zero(main_current[4].clone().into()); + builder.when_first_row().assert_zero(main_current[5].clone().into()); + builder.when_first_row().assert_zero(main_current[6].clone().into()); + builder.when_first_row().assert_zero(main_current[7].clone().into()); + builder.when_first_row().assert_zero(main_current[8].clone().into()); + builder.when_first_row().assert_zero(main_current[9].clone().into()); + builder.when_first_row().assert_zero(main_current[10].clone().into()); + builder.when_first_row().assert_zero(main_current[11].clone().into()); + builder.when_first_row().assert_zero(main_current[12].clone().into()); + builder.when_first_row().assert_zero(main_current[13].clone().into()); + builder.when_first_row().assert_zero(main_current[14].clone().into()); + builder.when_first_row().assert_zero(main_current[15].clone().into()); + builder.when_first_row().assert_zero(main_current[16].clone().into()); + builder.when_first_row().assert_zero(main_current[17].clone().into()); + builder.when_first_row().assert_zero(main_current[18].clone().into()); + builder.when_first_row().assert_zero(main_current[19].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[5].clone().into() * main_current[5].clone().into() - main_current[5].clone().into()); + builder.assert_zero(main_current[5].clone().into() * (main_current[6].clone().into() * main_current[6].clone().into() - main_current[6].clone().into())); + builder.assert_zero(main_current[5].clone().into() * main_current[6].clone().into() * (main_current[7].clone().into() * main_current[7].clone().into() - main_current[7].clone().into())); + builder.assert_zero(main_current[5].clone().into() * main_current[6].clone().into() * main_current[7].clone().into() * (main_current[8].clone().into() * main_current[8].clone().into() - main_current[8].clone().into())); + builder.assert_zero(main_current[5].clone().into() * main_current[6].clone().into() * main_current[7].clone().into() * main_current[8].clone().into() * (main_current[9].clone().into() * main_current[9].clone().into() - main_current[9].clone().into())); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/src/tests/evaluators/evaluators_slice_plonky3.rs b/air-script/src/tests/evaluators/evaluators_slice_plonky3.rs new file mode 100644 index 000000000..62f822ede --- /dev/null +++ b/air-script/src/tests/evaluators/evaluators_slice_plonky3.rs @@ -0,0 +1,66 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 20; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 16; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct EvaluatorsSliceAir; + +impl MidenAir for EvaluatorsSliceAir { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into()); + builder.when_first_row().assert_zero(main_current[1].clone().into()); + builder.when_first_row().assert_zero(main_current[2].clone().into()); + builder.when_first_row().assert_zero(main_current[3].clone().into()); + builder.when_first_row().assert_zero(main_current[4].clone().into()); + builder.when_first_row().assert_zero(main_current[5].clone().into()); + builder.when_first_row().assert_zero(main_current[6].clone().into()); + builder.when_first_row().assert_zero(main_current[7].clone().into()); + builder.when_first_row().assert_zero(main_current[8].clone().into()); + builder.when_first_row().assert_zero(main_current[9].clone().into()); + builder.when_first_row().assert_zero(main_current[10].clone().into()); + builder.when_first_row().assert_zero(main_current[11].clone().into()); + builder.when_first_row().assert_zero(main_current[12].clone().into()); + builder.when_first_row().assert_zero(main_current[13].clone().into()); + builder.when_first_row().assert_zero(main_current[14].clone().into()); + builder.when_first_row().assert_zero(main_current[15].clone().into()); + builder.when_first_row().assert_zero(main_current[16].clone().into()); + builder.when_first_row().assert_zero(main_current[17].clone().into()); + builder.when_first_row().assert_zero(main_current[18].clone().into()); + builder.when_first_row().assert_zero(main_current[19].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[0].clone().into() * main_current[0].clone().into() - main_current[0].clone().into()); + builder.assert_zero(main_current[0].clone().into() * (main_current[1].clone().into() * main_current[1].clone().into() - main_current[1].clone().into())); + builder.assert_zero(main_current[0].clone().into() * main_current[1].clone().into() * (main_current[2].clone().into() * main_current[2].clone().into() - main_current[2].clone().into())); + builder.assert_zero(main_current[0].clone().into() * main_current[1].clone().into() * main_current[2].clone().into() * (main_current[3].clone().into() * main_current[3].clone().into() - main_current[3].clone().into())); + builder.assert_zero(main_current[0].clone().into() * main_current[1].clone().into() * main_current[2].clone().into() * main_current[3].clone().into() * (main_current[4].clone().into() * main_current[4].clone().into() - main_current[4].clone().into())); + + // Aux boundary constraints + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/src/tests/plonky3.rs b/air-script/src/tests/plonky3.rs index e599d2189..03fb55e84 100644 --- a/air-script/src/tests/plonky3.rs +++ b/air-script/src/tests/plonky3.rs @@ -123,6 +123,38 @@ fn constants() { expected.assert_eq(&generated_air); } +#[test] +fn evaluators_nested_slice_call() { + let generated_air = Test::new("src/tests/evaluators/evaluators_nested_slice_call.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["evaluators/evaluators_nested_slice_call_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + +// TODO: add support for nested slicing in general expressions. +// +// #[test] +// fn evaluators_slice_slicing() { +// let generated_air = Test::new("src/tests/evaluators/evaluators_slice_slicing.air".to_string()) +// .transpile(Target::Plonky3) +// .unwrap(); +// +// let expected = expect_file!["evaluators/evaluators_slice_slicing_plonky3.rs"]; +// expected.assert_eq(&generated_air); +// } + +#[test] +fn evaluators_slice() { + let generated_air = Test::new("src/tests/evaluators/evaluators_slice.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["evaluators/evaluators_slice_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + #[test] fn evaluators() { let generated_air = Test::new("src/tests/evaluators/evaluators.air".to_string()) diff --git a/air-script/src/tests/winterfell.rs b/air-script/src/tests/winterfell.rs index 2b2683cb6..6a44ea7c8 100644 --- a/air-script/src/tests/winterfell.rs +++ b/air-script/src/tests/winterfell.rs @@ -143,6 +143,38 @@ fn constraint_comprehension() { expected.assert_eq(&generated_air); } +#[test] +fn evaluators_nested_slice_call() { + let generated_air = Test::new("src/tests/evaluators/evaluators_nested_slice_call.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["evaluators/evaluators_nested_slice_call.rs"]; + expected.assert_eq(&generated_air); +} + +// TODO: add support for nested slicing in general expressions. +// +// #[test] +// fn evaluators_slice_slicing() { +// let generated_air = Test::new("src/tests/evaluators/evaluators_slice_slicing.air".to_string()) +// .transpile(Target::Winterfell) +// .unwrap(); +// +// let expected = expect_file!["evaluators/evaluators_slice_slicing.rs"]; +// expected.assert_eq(&generated_air); +// } + +#[test] +fn evaluators_slice() { + let generated_air = Test::new("src/tests/evaluators/evaluators_slice.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["evaluators/evaluators_slice.rs"]; + expected.assert_eq(&generated_air); +} + #[test] fn evaluators() { let generated_air = Test::new("src/tests/evaluators/evaluators.air".to_string()) diff --git a/scripts/generate_all_e2e_tests.sh b/scripts/generate_all_e2e_tests.sh index b0808b9ec..9725910d1 100644 --- a/scripts/generate_all_e2e_tests.sh +++ b/scripts/generate_all_e2e_tests.sh @@ -19,6 +19,8 @@ cargo build --release ./target/release/airc transpile --target winterfell ./air-script/src/tests/constant_in_range/constant_in_range.air ./target/release/airc transpile --target winterfell ./air-script/src/tests/constants/constants.air ./target/release/airc transpile --target winterfell ./air-script/src/tests/constraint_comprehension/constraint_comprehension.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/evaluators/evaluators_nested_slice_call.air +./target/release/airc transpile --target winterfell ./air-script/src/tests/evaluators/evaluators_slice.air ./target/release/airc transpile --target winterfell ./air-script/src/tests/evaluators/evaluators.air ./target/release/airc transpile --target winterfell ./air-script/src/tests/fibonacci/fibonacci.air ./target/release/airc transpile --target winterfell ./air-script/src/tests/functions/functions_simple.air @@ -52,6 +54,8 @@ cargo build --release ./target/release/airc transpile --target plonky3 ./air-script/src/tests/constant_in_range/constant_in_range.air ./target/release/airc transpile --target plonky3 ./air-script/src/tests/constants/constants.air ./target/release/airc transpile --target plonky3 ./air-script/src/tests/constraint_comprehension/constraint_comprehension.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/evaluators/evaluators_nested_slice_call.air +./target/release/airc transpile --target plonky3 ./air-script/src/tests/evaluators/evaluators_slice.air ./target/release/airc transpile --target plonky3 ./air-script/src/tests/evaluators/evaluators.air ./target/release/airc transpile --target plonky3 ./air-script/src/tests/fibonacci/fibonacci.air ./target/release/airc transpile --target plonky3 ./air-script/src/tests/functions/functions_simple.air From 13d2ae0ccb1af7ca24d8633b6404fb2a6fa0d7c3 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Mon, 8 Dec 2025 08:09:05 +0100 Subject: [PATCH 23/83] fix: cargo fmt --- air-script/src/tests/plonky3.rs | 10 ++++++---- air-script/src/tests/winterfell.rs | 10 ++++++---- parser/src/ast/mod.rs | 25 ++++++++++++------------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/air-script/src/tests/plonky3.rs b/air-script/src/tests/plonky3.rs index 03fb55e84..baf9c3453 100644 --- a/air-script/src/tests/plonky3.rs +++ b/air-script/src/tests/plonky3.rs @@ -125,9 +125,10 @@ fn constants() { #[test] fn evaluators_nested_slice_call() { - let generated_air = Test::new("src/tests/evaluators/evaluators_nested_slice_call.air".to_string()) - .transpile(Target::Plonky3) - .unwrap(); + let generated_air = + Test::new("src/tests/evaluators/evaluators_nested_slice_call.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); let expected = expect_file!["evaluators/evaluators_nested_slice_call_plonky3.rs"]; expected.assert_eq(&generated_air); @@ -137,7 +138,8 @@ fn evaluators_nested_slice_call() { // // #[test] // fn evaluators_slice_slicing() { -// let generated_air = Test::new("src/tests/evaluators/evaluators_slice_slicing.air".to_string()) +// let generated_air = +// Test::new("src/tests/evaluators/evaluators_slice_slicing.air".to_string()) // .transpile(Target::Plonky3) // .unwrap(); // diff --git a/air-script/src/tests/winterfell.rs b/air-script/src/tests/winterfell.rs index 6a44ea7c8..5e9f044df 100644 --- a/air-script/src/tests/winterfell.rs +++ b/air-script/src/tests/winterfell.rs @@ -145,9 +145,10 @@ fn constraint_comprehension() { #[test] fn evaluators_nested_slice_call() { - let generated_air = Test::new("src/tests/evaluators/evaluators_nested_slice_call.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); + let generated_air = + Test::new("src/tests/evaluators/evaluators_nested_slice_call.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); let expected = expect_file!["evaluators/evaluators_nested_slice_call.rs"]; expected.assert_eq(&generated_air); @@ -157,7 +158,8 @@ fn evaluators_nested_slice_call() { // // #[test] // fn evaluators_slice_slicing() { -// let generated_air = Test::new("src/tests/evaluators/evaluators_slice_slicing.air".to_string()) +// let generated_air = +// Test::new("src/tests/evaluators/evaluators_slice_slicing.air".to_string()) // .transpile(Target::Winterfell) // .unwrap(); // diff --git a/parser/src/ast/mod.rs b/parser/src/ast/mod.rs index 1686455e7..1447c7b3c 100644 --- a/parser/src/ast/mod.rs +++ b/parser/src/ast/mod.rs @@ -508,19 +508,18 @@ impl Library { // importing module, if it was parsed from disk. If no path is available, // we default to the current working directory. - let (real_path, source_dir) = match codemap - .name(imports.first().unwrap().span().source_id()) - { - // If we have no source span, default to the current working directory - Err(_) => (false, cwd.clone()), - // If the file is virtual, then we've either already parsed imports for this module, - // or we have to fall back to the current working directory, but we have no relative - // path from which to base our search. - Ok(FileName::Virtual(_)) => (false, cwd.clone()), - Ok(FileName::Real(path)) => { - (true, path.parent().unwrap_or_else(|| Path::new(".")).to_path_buf()) - }, - }; + let (real_path, source_dir) = + match codemap.name(imports.first().unwrap().span().source_id()) { + // If we have no source span, default to the current working directory + Err(_) => (false, cwd.clone()), + // If the file is virtual, then we've either already parsed imports for this module, + // or we have to fall back to the current working directory, but we have no relative + // path from which to base our search. + Ok(FileName::Virtual(_)) => (false, cwd.clone()), + Ok(FileName::Real(path)) => { + (true, path.parent().unwrap_or_else(|| Path::new(".")).to_path_buf()) + }, + }; // For each module imported, try to load the module from the library, if it is // unavailable we must do extra work to load it into the library, as From b72ead4dd3fbfd8f1e8a7de82b3e86e3a8d82e6d Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Mon, 8 Dec 2025 08:18:29 +0100 Subject: [PATCH 24/83] fix: prev cargo fmt failed --- parser/src/ast/mod.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/parser/src/ast/mod.rs b/parser/src/ast/mod.rs index 1447c7b3c..1686455e7 100644 --- a/parser/src/ast/mod.rs +++ b/parser/src/ast/mod.rs @@ -508,18 +508,19 @@ impl Library { // importing module, if it was parsed from disk. If no path is available, // we default to the current working directory. - let (real_path, source_dir) = - match codemap.name(imports.first().unwrap().span().source_id()) { - // If we have no source span, default to the current working directory - Err(_) => (false, cwd.clone()), - // If the file is virtual, then we've either already parsed imports for this module, - // or we have to fall back to the current working directory, but we have no relative - // path from which to base our search. - Ok(FileName::Virtual(_)) => (false, cwd.clone()), - Ok(FileName::Real(path)) => { - (true, path.parent().unwrap_or_else(|| Path::new(".")).to_path_buf()) - }, - }; + let (real_path, source_dir) = match codemap + .name(imports.first().unwrap().span().source_id()) + { + // If we have no source span, default to the current working directory + Err(_) => (false, cwd.clone()), + // If the file is virtual, then we've either already parsed imports for this module, + // or we have to fall back to the current working directory, but we have no relative + // path from which to base our search. + Ok(FileName::Virtual(_)) => (false, cwd.clone()), + Ok(FileName::Real(path)) => { + (true, path.parent().unwrap_or_else(|| Path::new(".")).to_path_buf()) + }, + }; // For each module imported, try to load the module from the library, if it is // unavailable we must do extra work to load it into the library, as From f4f6c8621a47b93323fc91c9073abc0337613ea3 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Wed, 17 Dec 2025 17:58:24 +0100 Subject: [PATCH 25/83] feat: prove/verify and sync plonky3 (#523) --- CHANGELOG.md | 1 + air-script/Cargo.toml | 34 ++-- air-script/src/lib.rs | 3 + .../miden_vm_aux_trace_generator.rs | 5 +- .../src/test_utils/air_tester_macros.rs | 30 ++- air-script/src/test_utils/mod.rs | 6 +- air-script/src/test_utils/plonky3_traits.rs | 189 ------------------ .../test_utils/pub_inputs_conversion_utils.rs | 46 +++++ air-script/src/tests/binary/binary_plonky3.rs | 9 +- .../src/tests/binary/test_air_plonky3.rs | 11 +- .../src/tests/bitwise/bitwise_plonky3.rs | 9 +- .../src/tests/bitwise/test_air_plonky3.rs | 11 +- .../src/tests/buses/buses_complex_plonky3.rs | 25 ++- .../src/tests/buses/buses_simple_plonky3.rs | 24 ++- .../buses_varlen_boundary_both_plonky3.rs | 26 ++- .../buses_varlen_boundary_first_plonky3.rs | 27 +-- .../buses/buses_varlen_boundary_last.air | 19 +- .../tests/buses/buses_varlen_boundary_last.rs | 32 +-- .../buses_varlen_boundary_last_plonky3.rs | 37 ++-- air-script/src/tests/buses/mod.rs | 1 + .../src/tests/buses/test_air_plonky3.rs | 11 +- .../test_air_plonky3_varlen_boundary_last.rs | 64 ++++++ .../computed_indices_complex_plonky3.rs | 9 +- .../computed_indices_simple_plonky3.rs | 9 +- .../computed_indices/test_air_plonky3.rs | 11 +- .../constant_in_range_plonky3.rs | 9 +- .../constant_in_range/test_air_plonky3.rs | 11 +- .../src/tests/constants/constants_plonky3.rs | 9 +- .../src/tests/constants/test_air_plonky3.rs | 11 +- .../constraint_comprehension_plonky3.rs | 9 +- .../test_air_plonky3.rs | 11 +- .../evaluators_nested_slice_call_plonky3.rs | 9 +- .../tests/evaluators/evaluators_plonky3.rs | 9 +- .../evaluators/evaluators_slice_plonky3.rs | 9 +- .../src/tests/evaluators/test_air_plonky3.rs | 11 +- .../src/tests/fibonacci/fibonacci_plonky3.rs | 9 +- .../src/tests/fibonacci/test_air_plonky3.rs | 45 ++++- .../src/tests/functions/functions_complex.air | 2 +- .../src/tests/functions/functions_complex.rs | 4 +- .../functions/functions_complex_plonky3.rs | 11 +- .../functions/functions_simple_plonky3.rs | 9 +- .../src/tests/functions/test_air_plonky3.rs | 11 +- .../indexed_trace_access_plonky3.rs | 9 +- .../indexed_trace_access/test_air_plonky3.rs | 11 +- .../list_comprehension_nested_plonky3.rs | 9 +- .../list_comprehension_plonky3.rs | 9 +- .../list_comprehension/test_air_plonky3.rs | 11 +- .../list_folding/list_folding_plonky3.rs | 9 +- .../tests/list_folding/test_air_plonky3.rs | 11 +- .../periodic_columns_plonky3.rs | 9 +- .../periodic_columns/test_air_plonky3.rs | 11 +- .../tests/pub_inputs/pub_inputs_plonky3.rs | 9 +- .../src/tests/pub_inputs/test_air_plonky3.rs | 11 +- .../selectors_combine_complex_plonky3.rs | 22 +- .../selectors_combine_simple_plonky3.rs | 9 +- ...ombine_with_list_comprehensions_plonky3.rs | 9 +- .../src/tests/selectors/selectors_plonky3.rs | 9 +- .../selectors_with_evaluators_plonky3.rs | 9 +- .../src/tests/selectors/test_air_plonky3.rs | 11 +- air-script/src/tests/system/system_plonky3.rs | 9 +- .../src/tests/system/test_air_plonky3.rs | 11 +- .../trace_col_groups/test_air_plonky3.rs | 11 +- .../trace_col_groups_plonky3.rs | 9 +- .../src/tests/variables/test_air_plonky3.rs | 11 +- .../src/tests/variables/variables_plonky3.rs | 9 +- .../plonky3/src/air/boundary_constraints.rs | 1 + codegen/plonky3/src/air/mod.rs | 36 +++- codegen/plonky3/src/imports.rs | 1 + parser/src/ast/module.rs | 3 - 69 files changed, 581 insertions(+), 536 deletions(-) rename air-script/src/{test_utils => }/miden_vm_aux_trace_generator.rs (97%) delete mode 100644 air-script/src/test_utils/plonky3_traits.rs create mode 100644 air-script/src/test_utils/pub_inputs_conversion_utils.rs create mode 100644 air-script/src/tests/buses/test_air_plonky3_varlen_boundary_last.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index cf20d327a..19549d087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Support importing hierarchical modules (#507). - In Plonky3 codegen, use AirScriptAir and AirScriptBuilder traits, and generate aux constraints (#508). - In Plonky3 codegen, use MidenAir and MidenAirBuilder from 0xMiden's Plonky3 fork instead of AirScriptAir and AirScriptBuilder (#515). +- In Plonky3 codegen, use prove/verify workflow for tests (#523). ## 0.4.0 (2025-06-20) diff --git a/air-script/Cargo.toml b/air-script/Cargo.toml index 65813db4e..ed1709f76 100644 --- a/air-script/Cargo.toml +++ b/air-script/Cargo.toml @@ -28,28 +28,30 @@ log = { version = "0.4", default-features = false } miden-diagnostics = { workspace = true } mir = { package = "air-mir", path = "../mir", version = "0.5" } -[dev-dependencies] -expect-test = "1.4" - # 0xMiden Plonky3 Fork -p3-air = { package = "p3-air", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } -p3-challenger = { package = "p3-challenger", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } -p3-commit = { package = "p3-commit", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } -p3-dft = { package = "p3-dft", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } -p3-field = { package = "p3-field", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } -p3-fri = { package = "p3-fri", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } -p3-matrix = { package = "p3-matrix", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } -p3-merkle-tree = { package = "p3-merkle-tree", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } -p3-miden-air = { package = "miden-air", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } -p3-goldilocks = { package = "p3-goldilocks", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } -p3-sha256 = { package = "p3-sha256", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } -p3-symmetric = { package = "p3-symmetric", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } -p3-uni-stark = { package = "p3-uni-stark", git="https://github.com/0xMiden/Plonky3", rev = "5352d47ee57bd1b5d6008a90b64cbb7fe00aa17e", default-features = false } +p3-matrix = { package = "p3-matrix", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +p3-field = { package = "p3-field", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +p3-miden-air = { package = "miden-air", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } # MassaLabs fork miden-processor = { package = "miden-processor", git="https://github.com/massalabs/miden-vm", rev = "bc553af69a2543a0789830e8508b019694528181", default-features = false } miden-air = { package = "miden-air", git="https://github.com/massalabs/miden-vm", rev = "bc553af69a2543a0789830e8508b019694528181", default-features = false } +[dev-dependencies] +expect-test = "1.4" + +# 0xMiden Plonky3 Fork +p3-air = { package = "p3-air", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +p3-challenger = { package = "p3-challenger", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +p3-commit = { package = "p3-commit", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +p3-dft = { package = "p3-dft", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +p3-fri = { package = "p3-fri", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +p3-merkle-tree = { package = "p3-merkle-tree", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +p3-miden-prover = { package = "miden-prover", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +p3-goldilocks = { package = "p3-goldilocks", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +p3-sha256 = { package = "p3-sha256", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +p3-symmetric = { package = "p3-symmetric", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } + winter-air = { package = "winter-air", version = "0.12", default-features = false } winter-math = { package = "winter-math", version = "0.12", default-features = false } winter-utils = { package = "winter-utils", version = "0.12", default-features = false } diff --git a/air-script/src/lib.rs b/air-script/src/lib.rs index 67c255f21..d75247b94 100644 --- a/air-script/src/lib.rs +++ b/air-script/src/lib.rs @@ -2,6 +2,9 @@ pub use air_codegen_winter::CodeGenerator as WinterfellCodeGenerator; pub use air_ir::{Air, CompileError, compile}; pub use air_parser::{parse, parse_file, transforms}; +/// Miden VM auxiliary trace generator +pub mod miden_vm_aux_trace_generator; + #[cfg(test)] pub mod test_utils; #[cfg(test)] diff --git a/air-script/src/test_utils/miden_vm_aux_trace_generator.rs b/air-script/src/miden_vm_aux_trace_generator.rs similarity index 97% rename from air-script/src/test_utils/miden_vm_aux_trace_generator.rs rename to air-script/src/miden_vm_aux_trace_generator.rs index a6f9517c1..e233a970a 100644 --- a/air-script/src/test_utils/miden_vm_aux_trace_generator.rs +++ b/air-script/src/miden_vm_aux_trace_generator.rs @@ -59,7 +59,7 @@ pub fn build_aux_trace_with_miden_vm( main: &RowMajorMatrix, challenges: &[EF], module: MidenModule, -) -> RowMajorMatrix +) -> RowMajorMatrix where F: Field + PrimeField64, EF: ExtensionField, @@ -112,5 +112,6 @@ where } } - aux_trace + let aux_trace_f = aux_trace.flatten_to_base(); + aux_trace_f } diff --git a/air-script/src/test_utils/air_tester_macros.rs b/air-script/src/test_utils/air_tester_macros.rs index 53a64784e..7c5e305ee 100644 --- a/air-script/src/test_utils/air_tester_macros.rs +++ b/air-script/src/test_utils/air_tester_macros.rs @@ -53,7 +53,7 @@ macro_rules! generate_air_plonky3_test_with_airscript_traits { >; type Dft = p3_dft::Radix2DitParallel; type Pcs = p3_fri::TwoAdicFriPcs; - type MyConfig = p3_uni_stark::StarkConfig; + type MyConfig = p3_miden_prover::StarkConfig; let byte_hash = ByteHash {}; let field_hash = FieldHash::new(p3_sha256::Sha256); @@ -62,23 +62,35 @@ macro_rules! generate_air_plonky3_test_with_airscript_traits { let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); let challenger = Challenger::from_hasher(vec![], byte_hash); let dft = Dft::default(); - let fri_params = p3_fri::create_benchmark_fri_params(challenge_mmcs); + let mut fri_params = p3_fri::create_recursive_miden_fri_params(challenge_mmcs); let pcs = Pcs::new(dft, val_mmcs, fri_params); let config = MyConfig::new(pcs, challenger); + // Generate public inputs and convert them to Goldilocks field elements let inputs = generate_inputs(); - let inputs_goldilocks: Vec = inputs - .iter() - .map(|&x| ::from_u32(x)) - .collect(); + let inputs_goldilocks = crate::test_utils::pub_inputs_conversion_utils::convert_pub_inputs_to_goldilocks(&inputs); + + // Generate variable-length public inputs as a `Vec>>`. + // The outer `Vec` represents multiple tables (one for each bus in the AIR) + // The middle `Vec` represents the rows of each table + // The innermost `Vec` represents the tuple values contained by the bus, that will be combined with randomness into a single field element. + let var_len_pub_inputs = generate_var_len_pub_inputs(); + // Convert variable-length public inputs to Goldilocks field elements + let var_len_pub_inputs_goldilocks_vec = crate::test_utils::pub_inputs_conversion_utils::convert_var_len_pub_inputs_to_goldilocks(var_len_pub_inputs); + let var_len_pub_inputs_goldilocks_vec_slice = crate::test_utils::pub_inputs_conversion_utils::convert_inner_vec_to_slice(&var_len_pub_inputs_goldilocks_vec); + let var_len_pub_inputs_goldilocks = crate::test_utils::pub_inputs_conversion_utils::convert_mid_vec_to_slice(&var_len_pub_inputs_goldilocks_vec_slice); let trace = generate_trace_rows::(inputs); - check_constraints_with_airscript_traits::( + let proof = p3_miden_prover::prove(&config, &$air_name {}, &trace, &inputs_goldilocks); + p3_miden_prover::verify( + &config, &$air_name {}, - &trace, + &proof, &inputs_goldilocks, - ); + &var_len_pub_inputs_goldilocks, + ) + .expect("Verification failed"); } }; } diff --git a/air-script/src/test_utils/mod.rs b/air-script/src/test_utils/mod.rs index 601c64017..03ce65907 100644 --- a/air-script/src/test_utils/mod.rs +++ b/air-script/src/test_utils/mod.rs @@ -2,9 +2,7 @@ pub mod air_tester_macros; /// Code generation for tests/**/*.air files. pub mod codegen; -/// Miden VM auxiliary trace generator -pub mod miden_vm_aux_trace_generator; -/// Plonky3-specific Debug constraint builder implementation -pub mod plonky3_traits; +/// Conversion utilities for test inputs (both public inputs and variable-length public inputs). +pub mod pub_inputs_conversion_utils; /// Winterfell-specific traits pub mod winterfell_traits; diff --git a/air-script/src/test_utils/plonky3_traits.rs b/air-script/src/test_utils/plonky3_traits.rs deleted file mode 100644 index 1985d1558..000000000 --- a/air-script/src/test_utils/plonky3_traits.rs +++ /dev/null @@ -1,189 +0,0 @@ -use p3_field::{ExtensionField, Field}; -use p3_matrix::{ - Matrix, - dense::{DenseMatrix, RowMajorMatrix, RowMajorMatrixView}, - stack::VerticalPair, -}; -use p3_miden_air::{MidenAir, MidenAirBuilder, impl_p3_air_builder_traits}; - -/// A builder that runs constraint assertions during testing. -/// -/// Used in conjunction with [`check_constraints`] to simulate an execution trace -/// and verify that the AIR logic enforces all constraints. -#[derive(Debug)] -pub struct DebugConstraintBuilderWithAirScriptTraits<'a, F: Field, EF: ExtensionField> { - /// The index of the row currently being evaluated. - row_index: usize, - /// A view of the current and next main row as a vertical pair. - main: VerticalPair, RowMajorMatrixView<'a, F>>, - /// A view of the current and next preprocessed row as a vertical pair. - preprocessed: VerticalPair, RowMajorMatrixView<'a, F>>, - /// A view of the current and next aux row as a vertical pair. - aux: VerticalPair, RowMajorMatrixView<'a, EF>>, - /// The public values provided for constraint validation (e.g. inputs or outputs). - public_values: &'a [F], - /// A flag indicating whether this is the first row. - is_first_row: F, - /// A flag indicating whether this is the last row. - is_last_row: F, - /// A flag indicating whether this is a transition row (not the last row). - is_transition: F, - /// The periodic columns provided for constraint validation. - periodic_columns: Vec, - /// The permutation randomness in the extension field. - permutation_randomness: Vec, - /// The aux bus boundary values in the extension field. - aux_bus_boundary_values: Vec, -} - -impl<'a, F, EF> MidenAirBuilder for DebugConstraintBuilderWithAirScriptTraits<'a, F, EF> -where - F: Field, - EF: ExtensionField, -{ - type F = F; - type Expr = F; - type Var = F; - type M = VerticalPair, DenseMatrix>; - type PublicVar = F; - type PeriodicVal = EF; - type EF = EF; - type ExprEF = EF; - type VarEF = EF; - type MP = VerticalPair, DenseMatrix>; - type RandomVar = EF; - - fn main(&self) -> Self::M { - self.main - } - - fn is_first_row(&self) -> Self::Expr { - self.is_first_row - } - - fn is_last_row(&self) -> Self::Expr { - self.is_last_row - } - - fn is_transition_window(&self, size: usize) -> Self::Expr { - if size == 2 { - self.is_transition - } else { - panic!("only supports a window size of 2") - } - } - - fn assert_zero>(&mut self, x: I) { - assert_eq!(x.into(), F::ZERO, "constraints had nonzero value on row {}", self.row_index); - } - - fn public_values(&self) -> &[Self::PublicVar] { - self.public_values - } - - fn periodic_evals(&self) -> &[::PeriodicVal] { - self.periodic_columns.as_slice() - } - - fn preprocessed(&self) -> Self::M { - self.preprocessed - } - - fn assert_zero_ext(&mut self, x: I) - where - I: Into, - { - assert_eq!( - x.into(), - EF::ZERO, - "constraints on ext field had nonzero value on row {}", - self.row_index - ); - } - - fn permutation(&self) -> Self::MP { - self.aux - } - - fn permutation_randomness(&self) -> &[Self::RandomVar] { - self.permutation_randomness.as_slice() - } - - fn aux_bus_boundary_values(&self) -> &[::VarEF] { - self.aux_bus_boundary_values.as_slice() - } -} - -impl_p3_air_builder_traits!(DebugConstraintBuilderWithAirScriptTraits<'a, F, EF> where F: Field, EF: ExtensionField); - -pub(crate) fn check_constraints_with_airscript_traits( - air: &A, - main: &RowMajorMatrix, - public_values: &Vec, -) where - F: Field, - EF: ExtensionField, - A: MidenAir, -{ - let height = main.height(); - - let aux_bus_boundary_values: Vec<_> = (0..air.aux_width()).map(|_| EF::GENERATOR).collect(); - let alpha = EF::from_basis_coefficients_iter( - (0..EF::DIMENSION).map(|i| F::from_u64(123456789 * (i as u64 + 1))), - ) - .unwrap(); - let beta = EF::from_u64(987654321); - let beta_powers: Vec = (0..(air.num_randomness().saturating_sub(1))) - .map(|power| beta.exp_u64(power as u64)) - .collect(); - let mut permutation_randomness = Vec::with_capacity(air.num_randomness()); - permutation_randomness.push(alpha); - permutation_randomness.extend_from_slice(beta_powers.as_slice()); - - let aux_trace = air - .build_aux_trace(main, &permutation_randomness) - .unwrap_or(DenseMatrix::default(0, height)); - - (0..height).for_each(|i| { - let i_next = (i + 1) % height; - - let main_local = main.row_slice(i).unwrap(); // i < height so unwrap should never fail. - let main_next = main.row_slice(i_next).unwrap(); // i_next < height so unwrap should never fail. - let main = VerticalPair::new( - RowMajorMatrixView::new_row(&*main_local), - RowMajorMatrixView::new_row(&*main_next), - ); - let aux_local_ref = aux_trace.row_slice(i); - let aux_next_ref = aux_trace.row_slice(i_next); - let aux_local = aux_local_ref.as_deref().unwrap_or_default(); - let aux_next = aux_next_ref.as_deref().unwrap_or_default(); - let aux = VerticalPair::new( - RowMajorMatrixView::new_row(&*aux_local), - RowMajorMatrixView::new_row(&*aux_next), - ); - let preprocessed = VerticalPair::new::( - RowMajorMatrixView::new(&[], 0), - RowMajorMatrixView::new(&[], 0), - ); - - let periodic_columns_base: Vec<_> = - air.periodic_table().iter().map(|col| col[i % col.len()]).collect(); - let periodic_columns: Vec = - periodic_columns_base.iter().map(|&v| EF::from(v)).collect(); - - let mut builder = DebugConstraintBuilderWithAirScriptTraits { - row_index: i, - main, - preprocessed, - aux, - public_values, - is_first_row: F::from_bool(i == 0), - is_last_row: F::from_bool(i == height - 1), - is_transition: F::from_bool(i != height - 1), - periodic_columns, - permutation_randomness: permutation_randomness.clone(), - aux_bus_boundary_values: aux_bus_boundary_values.clone(), - }; - air.eval(&mut builder); - }); -} diff --git a/air-script/src/test_utils/pub_inputs_conversion_utils.rs b/air-script/src/test_utils/pub_inputs_conversion_utils.rs new file mode 100644 index 000000000..894ba75d6 --- /dev/null +++ b/air-script/src/test_utils/pub_inputs_conversion_utils.rs @@ -0,0 +1,46 @@ +type Val = p3_goldilocks::Goldilocks; + +/// Converts public inputs from u64 to Goldilocks field elements. +pub(crate) fn convert_pub_inputs_to_goldilocks(pub_inputs: &[u64]) -> Vec { + pub_inputs + .iter() + .map(|&x| ::from_u64(x)) + .collect() +} + +/// Converts variable-length public inputs from u64 to Goldilocks field elements. +/// The input should be a Vec of tables (Vec>) in a RowMajor format. +pub(crate) fn convert_var_len_pub_inputs_to_goldilocks( + var_len_pub_inputs: Vec>>, +) -> Vec>> { + let mut var_len_pub_inputs_goldilocks_vec: Vec>> = vec![]; + for arr in var_len_pub_inputs.iter() { + let mut goldilocks_arr: Vec> = vec![]; + for slice in arr.iter() { + let goldilocks_slice: Vec = slice + .iter() + .map(|&x| ::from_u64(x)) + .collect(); + goldilocks_arr.push(goldilocks_slice); + } + var_len_pub_inputs_goldilocks_vec.push(goldilocks_arr); + } + var_len_pub_inputs_goldilocks_vec +} + +/// Converts the innermost vectors of variable-length public inputs to slices. +pub(crate) fn convert_inner_vec_to_slice<'a>( + var_len_pub_inputs: &'a Vec>>, +) -> Vec> { + var_len_pub_inputs + .iter() + .map(|outer| outer.iter().map(|inner| inner.as_slice()).collect()) + .collect() +} + +/// Converts the middle vectors of variable-length public inputs to slices. +pub(crate) fn convert_mid_vec_to_slice<'a>( + var_len_pub_inputs: &'a Vec>, +) -> Vec<&'a [&'a [Val]]> { + var_len_pub_inputs.iter().map(|v| v.as_slice()).collect() +} diff --git a/air-script/src/tests/binary/binary_plonky3.rs b/air-script/src/tests/binary/binary_plonky3.rs index 0c514ea56..9f64d933c 100644 --- a/air-script/src/tests/binary/binary_plonky3.rs +++ b/air-script/src/tests/binary/binary_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 2; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for BinaryAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -37,8 +38,6 @@ impl MidenAir for BinaryAir { builder.assert_zero(main_current[0].clone().into() * main_current[0].clone().into() - main_current[0].clone().into()); builder.assert_zero(main_current[1].clone().into() * main_current[1].clone().into() - main_current[1].clone().into()); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/binary/test_air_plonky3.rs b/air-script/src/tests/binary/test_air_plonky3.rs index eced0b469..ba508f868 100644 --- a/air-script/src/tests/binary/test_air_plonky3.rs +++ b/air-script/src/tests/binary/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::binary::binary_plonky3::{BinaryAir, MAIN_WIDTH}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -37,8 +36,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, BinaryAir); diff --git a/air-script/src/tests/bitwise/bitwise_plonky3.rs b/air-script/src/tests/bitwise/bitwise_plonky3.rs index bfded5b9b..9811db8cf 100644 --- a/air-script/src/tests/bitwise/bitwise_plonky3.rs +++ b/air-script/src/tests/bitwise/bitwise_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 14; pub const AUX_WIDTH: usize = 0; @@ -33,11 +33,12 @@ where F: Field, } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -66,8 +67,6 @@ where F: Field, builder.when_transition().assert_zero_ext(AB::ExprEF::from(periodic_values[1].clone().into()) * (AB::ExprEF::from(main_current[12].clone().into()) - AB::ExprEF::from(main_next[11].clone().into()))); builder.assert_zero((AB::Expr::ONE - main_current[0].clone().into()) * (main_current[12].clone().into() - (main_current[11].clone().into() * AB::Expr::from_u64(16) + main_current[3].clone().into() * main_current[7].clone().into() + main_current[4].clone().into().double() * main_current[8].clone().into() + AB::Expr::from_u64(4) * main_current[5].clone().into() * main_current[9].clone().into() + AB::Expr::from_u64(8) * main_current[6].clone().into() * main_current[10].clone().into())) + main_current[0].clone().into() * (main_current[12].clone().into() - (main_current[11].clone().into() * AB::Expr::from_u64(16) + main_current[3].clone().into() + main_current[7].clone().into() - main_current[3].clone().into().double() * main_current[7].clone().into() + (main_current[4].clone().into() + main_current[8].clone().into() - main_current[4].clone().into().double() * main_current[8].clone().into()).double() + AB::Expr::from_u64(4) * (main_current[5].clone().into() + main_current[9].clone().into() - main_current[5].clone().into().double() * main_current[9].clone().into()) + AB::Expr::from_u64(8) * (main_current[6].clone().into() + main_current[10].clone().into() - main_current[6].clone().into().double() * main_current[10].clone().into())))); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/bitwise/test_air_plonky3.rs b/air-script/src/tests/bitwise/test_air_plonky3.rs index d969cb1a6..91b2c0637 100644 --- a/air-script/src/tests/bitwise/test_air_plonky3.rs +++ b/air-script/src/tests/bitwise/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::bitwise::bitwise_plonky3::{BitwiseAir, MAIN_WIDTH}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -75,8 +74,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, BitwiseAir); diff --git a/air-script/src/tests/buses/buses_complex_plonky3.rs b/air-script/src/tests/buses/buses_complex_plonky3.rs index 00d266cf9..2d038524d 100644 --- a/air-script/src/tests/buses/buses_complex_plonky3.rs +++ b/air-script/src/tests/buses/buses_complex_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 7; pub const AUX_WIDTH: usize = 2; @@ -29,7 +29,14 @@ where F: Field, AUX_WIDTH } - fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { + fn bus_types(&self) -> Vec { + vec![ + BusType::Multiset, + BusType::Logup, + ] + } + + fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { // Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders. let num_rows = _main.height(); @@ -66,15 +73,17 @@ where F: Field, rows[i+1][j] = next_row[j]; } } - Some(trace) + let trace_f = trace.flatten_to_base(); + Some(trace_f) } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -96,12 +105,6 @@ where F: Field, builder.assert_zero(main_current[2].clone().into() * main_current[2].clone().into() - main_current[2].clone().into()); builder.assert_zero(main_current[3].clone().into() * main_current[3].clone().into() - main_current[3].clone().into()); - // Aux boundary constraints - builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); - builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); - builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); - builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); - // Aux integrity/transition constraints builder.when_transition().assert_zero_ext(((alpha.into() + beta_challenges[0].into() + (AB::ExprEF::from_u64(3) + AB::ExprEF::from(main_current[1].clone().into())) * beta_challenges[1].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[2].into()) * AB::ExprEF::from(main_current[2].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[2].clone().into())) * ((alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[1].clone().into()) * beta_challenges[1].into()) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[2].clone().into())) + AB::ExprEF::from(main_current[2].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into() + (AB::ExprEF::from_u64(3) + AB::ExprEF::from(main_current[1].clone().into())) * beta_challenges[1].into() + AB::ExprEF::from(main_current[1].clone().into()) * beta_challenges[2].into()) * AB::ExprEF::from(main_current[3].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[3].clone().into())) * ((alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[3].clone().into())) + AB::ExprEF::from(main_current[3].clone().into())) * AB::ExprEF::from(aux_next[0].clone().into())); builder.when_transition().assert_zero_ext((alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[1].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(aux_current[1].clone().into()) + (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[1].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(main_current[4].clone().into()) + (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[1].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(main_current[5].clone().into()) - ((alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[1].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(aux_next[1].clone().into()) + (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(main_current[6].clone().into()))); diff --git a/air-script/src/tests/buses/buses_simple_plonky3.rs b/air-script/src/tests/buses/buses_simple_plonky3.rs index 1ee51d533..65efcf8fb 100644 --- a/air-script/src/tests/buses/buses_simple_plonky3.rs +++ b/air-script/src/tests/buses/buses_simple_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 1; pub const AUX_WIDTH: usize = 2; @@ -29,7 +29,14 @@ where F: Field, AUX_WIDTH } - fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { + fn bus_types(&self) -> Vec { + vec![ + BusType::Multiset, + BusType::Logup, + ] + } + + fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { // Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders. let num_rows = _main.height(); @@ -66,15 +73,17 @@ where F: Field, rows[i+1][j] = next_row[j]; } } - Some(trace) + let trace_f = trace.flatten_to_base(); + Some(trace_f) } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -93,11 +102,6 @@ where F: Field, // Main integrity/transition constraints - // Aux boundary constraints - builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); - builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); - builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); - // Aux integrity/transition constraints builder.when_transition().assert_zero_ext(((alpha.into() + beta_challenges[0].into()) * AB::ExprEF::from(main_current[0].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into()) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) + AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(aux_next[0].clone().into())); builder.when_transition().assert_zero_ext((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_current[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_next[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()).double())); diff --git a/air-script/src/tests/buses/buses_varlen_boundary_both_plonky3.rs b/air-script/src/tests/buses/buses_varlen_boundary_both_plonky3.rs index 13ed74cc7..b32d738e1 100644 --- a/air-script/src/tests/buses/buses_varlen_boundary_both_plonky3.rs +++ b/air-script/src/tests/buses/buses_varlen_boundary_both_plonky3.rs @@ -2,13 +2,13 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 1; pub const AUX_WIDTH: usize = 2; pub const NUM_PERIODIC_VALUES: usize = 0; pub const PERIOD: usize = 0; -pub const NUM_PUBLIC_VALUES: usize = 6; +pub const NUM_PUBLIC_VALUES: usize = 0; pub const MAX_BETA_CHALLENGE_POWER: usize = 2; pub struct BusesAir; @@ -29,7 +29,14 @@ where F: Field, AUX_WIDTH } - fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { + fn bus_types(&self) -> Vec { + vec![ + BusType::Multiset, + BusType::Logup, + ] + } + + fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { // Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders. let num_rows = _main.height(); @@ -66,15 +73,17 @@ where F: Field, rows[i+1][j] = next_row[j]; } } - Some(trace) + let trace_f = trace.flatten_to_base(); + Some(trace_f) } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -93,11 +102,6 @@ where F: Field, // Main integrity/transition constraints - // Aux boundary constraints - builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - aux_bus_boundary_values[0].into()); - builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); - builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into()) - aux_bus_boundary_values[1].into()); - // Aux integrity/transition constraints builder.when_transition().assert_zero_ext(((alpha.into() + beta_challenges[0].into()) * AB::ExprEF::from(main_current[0].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into()) * (AB::ExprEF::from(main_current[0].clone().into()) - AB::ExprEF::ONE) + AB::ExprEF::ONE - (AB::ExprEF::from(main_current[0].clone().into()) - AB::ExprEF::ONE)) * AB::ExprEF::from(aux_next[0].clone().into())); builder.when_transition().assert_zero_ext((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_current[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_next[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()).double())); diff --git a/air-script/src/tests/buses/buses_varlen_boundary_first_plonky3.rs b/air-script/src/tests/buses/buses_varlen_boundary_first_plonky3.rs index 7a8c42bda..b32d738e1 100644 --- a/air-script/src/tests/buses/buses_varlen_boundary_first_plonky3.rs +++ b/air-script/src/tests/buses/buses_varlen_boundary_first_plonky3.rs @@ -2,13 +2,13 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 1; pub const AUX_WIDTH: usize = 2; pub const NUM_PERIODIC_VALUES: usize = 0; pub const PERIOD: usize = 0; -pub const NUM_PUBLIC_VALUES: usize = 2; +pub const NUM_PUBLIC_VALUES: usize = 0; pub const MAX_BETA_CHALLENGE_POWER: usize = 2; pub struct BusesAir; @@ -29,7 +29,14 @@ where F: Field, AUX_WIDTH } - fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { + fn bus_types(&self) -> Vec { + vec![ + BusType::Multiset, + BusType::Logup, + ] + } + + fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { // Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders. let num_rows = _main.height(); @@ -66,15 +73,17 @@ where F: Field, rows[i+1][j] = next_row[j]; } } - Some(trace) + let trace_f = trace.flatten_to_base(); + Some(trace_f) } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -93,12 +102,6 @@ where F: Field, // Main integrity/transition constraints - // Aux boundary constraints - builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - aux_bus_boundary_values[0].into()); - builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); - builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into()) - aux_bus_boundary_values[1].into()); - builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); - // Aux integrity/transition constraints builder.when_transition().assert_zero_ext(((alpha.into() + beta_challenges[0].into()) * AB::ExprEF::from(main_current[0].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into()) * (AB::ExprEF::from(main_current[0].clone().into()) - AB::ExprEF::ONE) + AB::ExprEF::ONE - (AB::ExprEF::from(main_current[0].clone().into()) - AB::ExprEF::ONE)) * AB::ExprEF::from(aux_next[0].clone().into())); builder.when_transition().assert_zero_ext((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_current[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_next[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()).double())); diff --git a/air-script/src/tests/buses/buses_varlen_boundary_last.air b/air-script/src/tests/buses/buses_varlen_boundary_last.air index 9819cbc21..39e48144a 100644 --- a/air-script/src/tests/buses/buses_varlen_boundary_last.air +++ b/air-script/src/tests/buses/buses_varlen_boundary_last.air @@ -1,7 +1,7 @@ def BusesAir trace_columns { - main: [a], + main: [a, sp_insert, sp_remove, sq_insert_twice, sq_remove], } buses { @@ -10,20 +10,21 @@ buses { } public_inputs { - outputs: [[2]], + outputs_p: [[2]], + outputs_q: [[2]], } boundary_constraints { enf p.first = null; enf q.first = null; - enf p.last = outputs; - enf q.last = outputs; + enf p.last = outputs_p; + enf q.last = outputs_q; } integrity_constraints { - p.insert(1) when a; - p.remove(1) when (a - 1); - q.insert(1, 2) when a; - q.insert(1, 2) when a; - q.remove(1, 2) with 2; + p.insert(a) when sp_insert; + p.remove(a) when sp_remove; + q.insert(2, a) when sq_insert_twice; + q.insert(2, a) when sq_insert_twice; + q.remove(2, a) with sq_remove; } diff --git a/air-script/src/tests/buses/buses_varlen_boundary_last.rs b/air-script/src/tests/buses/buses_varlen_boundary_last.rs index 686604e08..65dbbda92 100644 --- a/air-script/src/tests/buses/buses_varlen_boundary_last.rs +++ b/air-script/src/tests/buses/buses_varlen_boundary_last.rs @@ -4,32 +4,36 @@ use winter_math::{ExtensionOf, FieldElement, ToElements}; use winter_utils::{ByteWriter, Serializable}; pub struct PublicInputs { - outputs: Vec<[Felt; 2]>, + outputs_p: Vec<[Felt; 2]>, + outputs_q: Vec<[Felt; 2]>, } impl PublicInputs { - pub fn new(outputs: Vec<[Felt; 2]>) -> Self { - Self { outputs } + pub fn new(outputs_p: Vec<[Felt; 2]>, outputs_q: Vec<[Felt; 2]>) -> Self { + Self { outputs_p, outputs_q } } } impl Serializable for PublicInputs { fn write_into(&self, target: &mut W) { - self.outputs.write_into(target); + self.outputs_p.write_into(target); + self.outputs_q.write_into(target); } } impl ToElements for PublicInputs { fn to_elements(&self) -> Vec { let mut elements = Vec::new(); - self.outputs.iter().for_each(|row| elements.extend_from_slice(row)); + self.outputs_p.iter().for_each(|row| elements.extend_from_slice(row)); + self.outputs_q.iter().for_each(|row| elements.extend_from_slice(row)); elements } } pub struct BusesAir { context: AirContext, - outputs: Vec<[Felt; 2]>, + outputs_p: Vec<[Felt; 2]>, + outputs_q: Vec<[Felt; 2]>, } impl BusesAir { @@ -75,7 +79,7 @@ impl Air for BusesAir { fn new(trace_info: TraceInfo, public_inputs: PublicInputs, options: WinterProofOptions) -> Self { let main_degrees = vec![]; - let aux_degrees = vec![TransitionConstraintDegree::new(2), TransitionConstraintDegree::new(1)]; + let aux_degrees = vec![TransitionConstraintDegree::new(3), TransitionConstraintDegree::new(4)]; let num_main_assertions = 0; let num_aux_assertions = 4; @@ -88,7 +92,7 @@ impl Air for BusesAir { options, ) .set_num_transition_exemptions(2); - Self { context, outputs: public_inputs.outputs } + Self { context, outputs_p: public_inputs.outputs_p, outputs_q: public_inputs.outputs_q } } fn get_periodic_column_values(&self) -> Vec> { @@ -102,12 +106,12 @@ impl Air for BusesAir { fn get_aux_assertions>(&self, aux_rand_elements: &AuxRandElements) -> Vec> { let mut result = Vec::new(); - let reduced_outputs_multiset = Self::bus_multiset_boundary_varlen(aux_rand_elements, &self.outputs); - let reduced_outputs_logup = Self::bus_logup_boundary_varlen(aux_rand_elements, &self.outputs); + let reduced_outputs_p_multiset = Self::bus_multiset_boundary_varlen(aux_rand_elements, &self.outputs_p); + let reduced_outputs_q_logup = Self::bus_logup_boundary_varlen(aux_rand_elements, &self.outputs_q); result.push(Assertion::single(0, 0, E::ONE)); - result.push(Assertion::single(0, self.last_step(), reduced_outputs_multiset)); + result.push(Assertion::single(0, self.last_step(), reduced_outputs_p_multiset)); result.push(Assertion::single(1, 0, E::ZERO)); - result.push(Assertion::single(1, self.last_step(), reduced_outputs_logup)); + result.push(Assertion::single(1, self.last_step(), reduced_outputs_q_logup)); result } @@ -124,7 +128,7 @@ impl Air for BusesAir { let main_next = main_frame.next(); let aux_current = aux_frame.current(); let aux_next = aux_frame.next(); - result[0] = ((aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1]) * E::from(main_current[0]) + E::ONE - E::from(main_current[0])) * aux_current[0] - ((aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1]) * (E::from(main_current[0]) - E::ONE) + E::ONE - (E::from(main_current[0]) - E::ONE)) * aux_next[0]; - result[1] = (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * aux_current[1] + (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * E::from(main_current[0]) + (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * E::from(main_current[0]) - ((aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * aux_next[1] + (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + aux_rand_elements.rand_elements()[1] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[2]) * E::from(Felt::new(2_u64))); + result[0] = ((aux_rand_elements.rand_elements()[0] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[1]) * E::from(main_current[1]) + E::ONE - E::from(main_current[1])) * aux_current[0] - ((aux_rand_elements.rand_elements()[0] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[1]) * E::from(main_current[2]) + E::ONE - E::from(main_current[2])) * aux_next[0]; + result[1] = (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * aux_current[1] + (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * E::from(main_current[3]) + (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * E::from(main_current[3]) - ((aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * aux_next[1] + (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * (aux_rand_elements.rand_elements()[0] + E::from(Felt::new(2_u64)) * aux_rand_elements.rand_elements()[1] + E::from(main_current[0]) * aux_rand_elements.rand_elements()[2]) * E::from(main_current[4])); } } \ No newline at end of file diff --git a/air-script/src/tests/buses/buses_varlen_boundary_last_plonky3.rs b/air-script/src/tests/buses/buses_varlen_boundary_last_plonky3.rs index fa895389e..dba927c11 100644 --- a/air-script/src/tests/buses/buses_varlen_boundary_last_plonky3.rs +++ b/air-script/src/tests/buses/buses_varlen_boundary_last_plonky3.rs @@ -2,13 +2,13 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; -pub const MAIN_WIDTH: usize = 1; +pub const MAIN_WIDTH: usize = 5; pub const AUX_WIDTH: usize = 2; pub const NUM_PERIODIC_VALUES: usize = 0; pub const PERIOD: usize = 0; -pub const NUM_PUBLIC_VALUES: usize = 2; +pub const NUM_PUBLIC_VALUES: usize = 0; pub const MAX_BETA_CHALLENGE_POWER: usize = 2; pub struct BusesAir; @@ -29,7 +29,14 @@ where F: Field, AUX_WIDTH } - fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { + fn bus_types(&self) -> Vec { + vec![ + BusType::Multiset, + BusType::Logup, + ] + } + + fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { // Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders. let num_rows = _main.height(); @@ -66,15 +73,17 @@ where F: Field, rows[i+1][j] = next_row[j]; } } - Some(trace) + let trace_f = trace.flatten_to_base(); + Some(trace_f) } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -93,15 +102,9 @@ where F: Field, // Main integrity/transition constraints - // Aux boundary constraints - builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); - builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - aux_bus_boundary_values[0].into()); - builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into())); - builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[1].clone().into()) - aux_bus_boundary_values[1].into()); - // Aux integrity/transition constraints - builder.when_transition().assert_zero_ext(((alpha.into() + beta_challenges[0].into()) * AB::ExprEF::from(main_current[0].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into()) * (AB::ExprEF::from(main_current[0].clone().into()) - AB::ExprEF::ONE) + AB::ExprEF::ONE - (AB::ExprEF::from(main_current[0].clone().into()) - AB::ExprEF::ONE)) * AB::ExprEF::from(aux_next[0].clone().into())); - builder.when_transition().assert_zero_ext((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_current[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) - ((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(aux_next[1].clone().into()) + (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()).double())); + builder.when_transition().assert_zero_ext(((alpha.into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[0].into()) * AB::ExprEF::from(main_current[1].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[1].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[0].into()) * AB::ExprEF::from(main_current[2].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[2].clone().into())) * AB::ExprEF::from(aux_next[0].clone().into())); + builder.when_transition().assert_zero_ext((alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(aux_current[1].clone().into()) + (alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(main_current[3].clone().into()) + (alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(main_current[3].clone().into()) - ((alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(aux_next[1].clone().into()) + (alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * (alpha.into() + beta_challenges[0].into().double() + AB::ExprEF::from(main_current[0].clone().into()) * beta_challenges[1].into()) * AB::ExprEF::from(main_current[4].clone().into()))); } } @@ -128,8 +131,8 @@ impl BusesAir { let beta_challenges: [_; MAX_BETA_CHALLENGE_POWER] = beta_challenges.try_into().expect("Wrong number of randomness"); let periodic_values: [_; NUM_PERIODIC_VALUES] = periodic_evals.try_into().expect("Wrong number of periodic values"); vec![ - (((alpha + beta_challenges[0]) * EF::from(main_current[0].clone()) + EF::ONE - EF::from(main_current[0].clone())) * EF::from(aux_current[0].clone())) * ((alpha + beta_challenges[0]) * (EF::from(main_current[0].clone()) - EF::ONE) + EF::ONE - (EF::from(main_current[0].clone()) - EF::ONE)).inverse(), - ((alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(aux_current[1].clone()) + (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(main_current[0].clone()) + (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * EF::from(main_current[0].clone()) - (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()).double()) * ((alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double()) * (alpha + beta_challenges[0] + beta_challenges[1].double())).inverse(), + (((alpha + EF::from(main_current[0].clone()) * beta_challenges[0]) * EF::from(main_current[1].clone()) + EF::ONE - EF::from(main_current[1].clone())) * EF::from(aux_current[0].clone())) * ((alpha + EF::from(main_current[0].clone()) * beta_challenges[0]) * EF::from(main_current[2].clone()) + EF::ONE - EF::from(main_current[2].clone())).inverse(), + ((alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1]) * EF::from(aux_current[1].clone()) + (alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1]) * EF::from(main_current[3].clone()) + (alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1]) * EF::from(main_current[3].clone()) - (alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1]) * EF::from(main_current[4].clone())) * ((alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1]) * (alpha + beta_challenges[0].double() + EF::from(main_current[0].clone()) * beta_challenges[1])).inverse(), ] } } \ No newline at end of file diff --git a/air-script/src/tests/buses/mod.rs b/air-script/src/tests/buses/mod.rs index 95eeeb07b..5c0ca958c 100644 --- a/air-script/src/tests/buses/mod.rs +++ b/air-script/src/tests/buses/mod.rs @@ -36,4 +36,5 @@ mod buses_varlen_boundary_first_plonky3; mod buses_varlen_boundary_last_plonky3; mod test_air_plonky3; +mod test_air_plonky3_varlen_boundary_last; mod test_air_winterfell; diff --git a/air-script/src/tests/buses/test_air_plonky3.rs b/air-script/src/tests/buses/test_air_plonky3.rs index 20919a31e..676d87701 100644 --- a/air-script/src/tests/buses/test_air_plonky3.rs +++ b/air-script/src/tests/buses/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::buses::buses_complex_plonky3::{BusesAir, MAIN_WIDTH}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -52,8 +51,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 2] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![vec![], vec![], vec![]] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, BusesAir); diff --git a/air-script/src/tests/buses/test_air_plonky3_varlen_boundary_last.rs b/air-script/src/tests/buses/test_air_plonky3_varlen_boundary_last.rs new file mode 100644 index 000000000..6415f669e --- /dev/null +++ b/air-script/src/tests/buses/test_air_plonky3_varlen_boundary_last.rs @@ -0,0 +1,64 @@ +use p3_field::PrimeField64; +use p3_miden_air::RowMajorMatrix; + +use crate::{ + generate_air_plonky3_test_with_airscript_traits, + tests::buses::buses_varlen_boundary_last_plonky3::{BusesAir, MAIN_WIDTH}, +}; + +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; + let trace_length = num_rows * MAIN_WIDTH; + + let mut long_trace = F::zero_vec(trace_length); + + let mut trace = RowMajorMatrix::new(long_trace, MAIN_WIDTH); + + let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<[F; MAIN_WIDTH]>() }; + assert!(prefix.is_empty(), "Alignment should match"); + assert!(suffix.is_empty(), "Alignment should match"); + assert_eq!(rows.len(), num_rows); + + // Initialize first row + rows[0][0] = F::ONE; + rows[0][1] = F::ZERO; + rows[0][2] = F::ZERO; + rows[0][3] = F::ZERO; + rows[0][4] = F::ZERO; + + // Fill subsequent rows using direct access to the rows array + for i in 1..num_rows { + let a_prev = rows[i - 1][0]; + let b_prev = rows[i - 1][1]; + let c_prev = rows[i - 1][2]; + let d_prev = rows[i - 1][3]; + let e_prev = rows[i - 1][4]; + + // Update current row based on previous values + rows[i][0] = F::ONE; + rows[i][1] = if i > 3 && i < 8 { F::ONE } else { F::ZERO }; // sp_insert is true 4 times + rows[i][2] = if i > 3 && i < 7 { F::ONE } else { F::ZERO }; // sp_remove is true 3 times + rows[i][3] = if i > 4 && i < 10 { F::ONE } else { F::ZERO }; // sq_insert_twice is true 5 times + rows[i][4] = if i > 5 && i < 10 { + F::from_canonical_checked(2).unwrap() + } else { + F::ZERO + }; // sq_remove has value "2" 4 times + } + + trace +} + +fn generate_inputs() -> Vec { + vec![] +} + +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + // At the end, the bus p will have the tuple (a) (that equals (1)) inserted once + let var_len_p = vec![vec![1]]; + // At the end, the bus q will have the tuple (2, a) (that equals (2, 1)) inserted twice + let var_len_q = vec![vec![2, 1], vec![2, 1]]; + vec![var_len_p, var_len_q] +} + +generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, BusesAir); diff --git a/air-script/src/tests/computed_indices/computed_indices_complex_plonky3.rs b/air-script/src/tests/computed_indices/computed_indices_complex_plonky3.rs index ae3ee2903..6b87e196b 100644 --- a/air-script/src/tests/computed_indices/computed_indices_complex_plonky3.rs +++ b/air-script/src/tests/computed_indices/computed_indices_complex_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 4; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for ComputedIndicesAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -36,8 +37,6 @@ impl MidenAir for ComputedIndicesAir { // Main integrity/transition constraints builder.assert_zero(main_current[2].clone().into() * AB::Expr::from_u64(3) + main_current[3].clone().into() * AB::Expr::from_u64(4)); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/computed_indices/computed_indices_simple_plonky3.rs b/air-script/src/tests/computed_indices/computed_indices_simple_plonky3.rs index 40c374ad3..1ab566b59 100644 --- a/air-script/src/tests/computed_indices/computed_indices_simple_plonky3.rs +++ b/air-script/src/tests/computed_indices/computed_indices_simple_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 8; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for ComputedIndicesAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -43,8 +44,6 @@ impl MidenAir for ComputedIndicesAir { builder.when_transition().assert_zero(main_next[6].clone().into() - AB::Expr::from_u64(6) * main_current[6].clone().into()); builder.when_transition().assert_zero(main_next[7].clone().into() - AB::Expr::from_u64(12) * main_current[7].clone().into()); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/computed_indices/test_air_plonky3.rs b/air-script/src/tests/computed_indices/test_air_plonky3.rs index 5f02cc466..0ebed55a3 100644 --- a/air-script/src/tests/computed_indices/test_air_plonky3.rs +++ b/air-script/src/tests/computed_indices/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::computed_indices::computed_indices_simple_plonky3::{ComputedIndicesAir, MAIN_WIDTH}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -55,8 +54,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, ComputedIndicesAir); diff --git a/air-script/src/tests/constant_in_range/constant_in_range_plonky3.rs b/air-script/src/tests/constant_in_range/constant_in_range_plonky3.rs index b1238f7cc..134d141eb 100644 --- a/air-script/src/tests/constant_in_range/constant_in_range_plonky3.rs +++ b/air-script/src/tests/constant_in_range/constant_in_range_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 12; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for ConstantInRangeAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -36,8 +37,6 @@ impl MidenAir for ConstantInRangeAir { // Main integrity/transition constraints builder.assert_zero(main_current[0].clone().into() - (main_current[1].clone().into() - main_current[4].clone().into() - main_current[8].clone().into() + AB::Expr::ONE + main_current[2].clone().into() - main_current[5].clone().into() - main_current[9].clone().into() + AB::Expr::from_u64(2) + main_current[3].clone().into() - main_current[6].clone().into() - main_current[10].clone().into())); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/constant_in_range/test_air_plonky3.rs b/air-script/src/tests/constant_in_range/test_air_plonky3.rs index 9dd399e1c..e1959450e 100644 --- a/air-script/src/tests/constant_in_range/test_air_plonky3.rs +++ b/air-script/src/tests/constant_in_range/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::constant_in_range::constant_in_range_plonky3::{ConstantInRangeAir, MAIN_WIDTH}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -56,8 +55,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, ConstantInRangeAir); diff --git a/air-script/src/tests/constants/constants_plonky3.rs b/air-script/src/tests/constants/constants_plonky3.rs index 632331aab..72218cfbb 100644 --- a/air-script/src/tests/constants/constants_plonky3.rs +++ b/air-script/src/tests/constants/constants_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 7; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for ConstantsAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -45,8 +46,6 @@ impl MidenAir for ConstantsAir { builder.when_transition().assert_zero(main_next[5].clone().into() - (main_current[5].clone().into() + AB::Expr::ONE)); builder.assert_zero(main_current[4].clone().into() - AB::Expr::ONE); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/constants/test_air_plonky3.rs b/air-script/src/tests/constants/test_air_plonky3.rs index cf2c54ebd..5c906ff3b 100644 --- a/air-script/src/tests/constants/test_air_plonky3.rs +++ b/air-script/src/tests/constants/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::constants::constants_plonky3::{ConstantsAir, MAIN_WIDTH}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -52,8 +51,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 32] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, ConstantsAir); diff --git a/air-script/src/tests/constraint_comprehension/constraint_comprehension_plonky3.rs b/air-script/src/tests/constraint_comprehension/constraint_comprehension_plonky3.rs index e333b9626..b34c6181b 100644 --- a/air-script/src/tests/constraint_comprehension/constraint_comprehension_plonky3.rs +++ b/air-script/src/tests/constraint_comprehension/constraint_comprehension_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 14; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for ConstraintComprehensionAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -39,8 +40,6 @@ impl MidenAir for ConstraintComprehensionAir { builder.assert_zero(main_current[8].clone().into() - main_current[12].clone().into()); builder.assert_zero(main_current[9].clone().into() - main_current[13].clone().into()); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/constraint_comprehension/test_air_plonky3.rs b/air-script/src/tests/constraint_comprehension/test_air_plonky3.rs index f9fa0d39c..09f942eb3 100644 --- a/air-script/src/tests/constraint_comprehension/test_air_plonky3.rs +++ b/air-script/src/tests/constraint_comprehension/test_air_plonky3.rs @@ -3,14 +3,13 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::constraint_comprehension::constraint_comprehension_plonky3::{ ConstraintComprehensionAir, MAIN_WIDTH, }, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -39,8 +38,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, ConstraintComprehensionAir); diff --git a/air-script/src/tests/evaluators/evaluators_nested_slice_call_plonky3.rs b/air-script/src/tests/evaluators/evaluators_nested_slice_call_plonky3.rs index 8dfd1d4bf..45e132b6c 100644 --- a/air-script/src/tests/evaluators/evaluators_nested_slice_call_plonky3.rs +++ b/air-script/src/tests/evaluators/evaluators_nested_slice_call_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 20; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for EvaluatorsSliceAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -59,8 +60,6 @@ impl MidenAir for EvaluatorsSliceAir { builder.assert_zero(main_current[5].clone().into() * main_current[6].clone().into() * main_current[7].clone().into() * (main_current[8].clone().into() * main_current[8].clone().into() - main_current[8].clone().into())); builder.assert_zero(main_current[5].clone().into() * main_current[6].clone().into() * main_current[7].clone().into() * main_current[8].clone().into() * (main_current[9].clone().into() * main_current[9].clone().into() - main_current[9].clone().into())); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/evaluators/evaluators_plonky3.rs b/air-script/src/tests/evaluators/evaluators_plonky3.rs index 07f19ea21..5c0ad3895 100644 --- a/air-script/src/tests/evaluators/evaluators_plonky3.rs +++ b/air-script/src/tests/evaluators/evaluators_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 7; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for EvaluatorsAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -45,8 +46,6 @@ impl MidenAir for EvaluatorsAir { builder.assert_zero(main_current[5].clone().into() - AB::Expr::ONE); builder.assert_zero(main_current[6].clone().into() - AB::Expr::from_u64(4)); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/evaluators/evaluators_slice_plonky3.rs b/air-script/src/tests/evaluators/evaluators_slice_plonky3.rs index 62f822ede..cd19cef43 100644 --- a/air-script/src/tests/evaluators/evaluators_slice_plonky3.rs +++ b/air-script/src/tests/evaluators/evaluators_slice_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 20; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for EvaluatorsSliceAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -59,8 +60,6 @@ impl MidenAir for EvaluatorsSliceAir { builder.assert_zero(main_current[0].clone().into() * main_current[1].clone().into() * main_current[2].clone().into() * (main_current[3].clone().into() * main_current[3].clone().into() - main_current[3].clone().into())); builder.assert_zero(main_current[0].clone().into() * main_current[1].clone().into() * main_current[2].clone().into() * main_current[3].clone().into() * (main_current[4].clone().into() * main_current[4].clone().into() - main_current[4].clone().into())); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/evaluators/test_air_plonky3.rs b/air-script/src/tests/evaluators/test_air_plonky3.rs index 050ba860a..1d70dcb45 100644 --- a/air-script/src/tests/evaluators/test_air_plonky3.rs +++ b/air-script/src/tests/evaluators/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::evaluators::evaluators_plonky3::{EvaluatorsAir, MAIN_WIDTH}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -52,8 +51,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, EvaluatorsAir); diff --git a/air-script/src/tests/fibonacci/fibonacci_plonky3.rs b/air-script/src/tests/fibonacci/fibonacci_plonky3.rs index b5476e071..15e40b79d 100644 --- a/air-script/src/tests/fibonacci/fibonacci_plonky3.rs +++ b/air-script/src/tests/fibonacci/fibonacci_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 2; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for FibonacciAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -39,8 +40,6 @@ impl MidenAir for FibonacciAir { builder.when_transition().assert_zero(main_next[1].clone().into() - (main_current[0].clone().into() + main_current[1].clone().into())); builder.when_transition().assert_zero(main_next[0].clone().into() - main_current[1].clone().into()); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/fibonacci/test_air_plonky3.rs b/air-script/src/tests/fibonacci/test_air_plonky3.rs index 1d76e60d2..e41da1047 100644 --- a/air-script/src/tests/fibonacci/test_air_plonky3.rs +++ b/air-script/src/tests/fibonacci/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::fibonacci::fibonacci_plonky3::{FibonacciAir, MAIN_WIDTH}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 31; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -37,10 +36,44 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { + let zero = 0; let one = 1; - let last = 2178309; // 32nd Fibonacci number - vec![one, one, last] + let last = fibonacci_field::(512).as_canonical_u64(); // 512nd Fibonacci number in Goldilock's field + vec![zero, one, last] +} + +fn fibonacci_field(n: i32) -> F { + if n < 0 { + panic!("{} is negative!", n); + } else if n == 0 { + return F::ZERO; + } else if n == 1 { + return F::ONE; + } + + let mut sum = F::ZERO; + let mut last = F::ZERO; + let mut curr = F::ONE; + for _i in 1..n { + sum = last + curr; + last = curr; + curr = sum; + } + sum +} + +#[test] +fn test_goldilocks_fibonacci_computation() { + type F = p3_goldilocks::Goldilocks; + let f_32 = fibonacci_field::(32); + let f_512 = fibonacci_field::(512); + assert_eq!(f_32, F::new(2178309)); + assert_eq!(f_512, F::new(12556846397060607923)); +} + +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] } generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, FibonacciAir); diff --git a/air-script/src/tests/functions/functions_complex.air b/air-script/src/tests/functions/functions_complex.air index 2b512e262..e427b5b93 100644 --- a/air-script/src/tests/functions/functions_complex.air +++ b/air-script/src/tests/functions/functions_complex.air @@ -29,7 +29,7 @@ boundary_constraints { integrity_constraints { let f = get_multiplicity_flags(s0, s1); - let z = v^7 * f[3] + v^2 * f[2] + v * f[1] + f[0]; + let z = v^5 * f[3] + v^2 * f[2] + v * f[1] + f[0]; enf b_range' = b_range * (z * t - t + 1); let y = fold_scalar_and_vec(v, b); enf v' = y; diff --git a/air-script/src/tests/functions/functions_complex.rs b/air-script/src/tests/functions/functions_complex.rs index f165c2d0e..9a438bb07 100644 --- a/air-script/src/tests/functions/functions_complex.rs +++ b/air-script/src/tests/functions/functions_complex.rs @@ -47,7 +47,7 @@ impl Air for FunctionsAir { } fn new(trace_info: TraceInfo, public_inputs: PublicInputs, options: WinterProofOptions) -> Self { - let main_degrees = vec![TransitionConstraintDegree::new(11), TransitionConstraintDegree::new(1)]; + let main_degrees = vec![TransitionConstraintDegree::new(9), TransitionConstraintDegree::new(1)]; let aux_degrees = vec![]; let num_main_assertions = 1; let num_aux_assertions = 0; @@ -82,7 +82,7 @@ impl Air for FunctionsAir { fn evaluate_transition>(&self, frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E]) { let main_current = frame.current(); let main_next = frame.next(); - result[0] = main_next[16] - main_current[16] * ((main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[1] * main_current[2] + main_current[3] * main_current[3] * (E::ONE - main_current[1]) * main_current[2] + main_current[3] * main_current[1] * (E::ONE - main_current[2]) + (E::ONE - main_current[1]) * (E::ONE - main_current[2])) * main_current[0] - main_current[0] + E::ONE); + result[0] = main_next[16] - main_current[16] * ((main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[3] * main_current[1] * main_current[2] + main_current[3] * main_current[3] * (E::ONE - main_current[1]) * main_current[2] + main_current[3] * main_current[1] * (E::ONE - main_current[2]) + (E::ONE - main_current[1]) * (E::ONE - main_current[2])) * main_current[0] - main_current[0] + E::ONE); result[1] = main_next[3] - (main_current[4] + main_current[5] + main_current[6] + main_current[7] + main_current[8] + main_current[9] + main_current[10] + main_current[11] + main_current[12] + main_current[13] + main_current[14] + main_current[15] + E::ONE) * E::from(Felt::new(2_u64)); } diff --git a/air-script/src/tests/functions/functions_complex_plonky3.rs b/air-script/src/tests/functions/functions_complex_plonky3.rs index b42205283..238cc3741 100644 --- a/air-script/src/tests/functions/functions_complex_plonky3.rs +++ b/air-script/src/tests/functions/functions_complex_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 17; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for FunctionsAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -34,11 +35,9 @@ impl MidenAir for FunctionsAir { builder.when_first_row().assert_zero(main_current[3].clone().into()); // Main integrity/transition constraints - builder.when_transition().assert_zero(main_next[16].clone().into() - main_current[16].clone().into() * ((main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[1].clone().into() * main_current[2].clone().into() + main_current[3].clone().into() * main_current[3].clone().into() * (AB::Expr::ONE - main_current[1].clone().into()) * main_current[2].clone().into() + main_current[3].clone().into() * main_current[1].clone().into() * (AB::Expr::ONE - main_current[2].clone().into()) + (AB::Expr::ONE - main_current[1].clone().into()) * (AB::Expr::ONE - main_current[2].clone().into())) * main_current[0].clone().into() - main_current[0].clone().into() + AB::Expr::ONE)); + builder.when_transition().assert_zero(main_next[16].clone().into() - main_current[16].clone().into() * ((main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[3].clone().into() * main_current[1].clone().into() * main_current[2].clone().into() + main_current[3].clone().into() * main_current[3].clone().into() * (AB::Expr::ONE - main_current[1].clone().into()) * main_current[2].clone().into() + main_current[3].clone().into() * main_current[1].clone().into() * (AB::Expr::ONE - main_current[2].clone().into()) + (AB::Expr::ONE - main_current[1].clone().into()) * (AB::Expr::ONE - main_current[2].clone().into())) * main_current[0].clone().into() - main_current[0].clone().into() + AB::Expr::ONE)); builder.when_transition().assert_zero(main_next[3].clone().into() - (main_current[4].clone().into() + main_current[5].clone().into() + main_current[6].clone().into() + main_current[7].clone().into() + main_current[8].clone().into() + main_current[9].clone().into() + main_current[10].clone().into() + main_current[11].clone().into() + main_current[12].clone().into() + main_current[13].clone().into() + main_current[14].clone().into() + main_current[15].clone().into() + AB::Expr::ONE).double()); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/functions/functions_simple_plonky3.rs b/air-script/src/tests/functions/functions_simple_plonky3.rs index c5b131f49..1b72a7dfb 100644 --- a/air-script/src/tests/functions/functions_simple_plonky3.rs +++ b/air-script/src/tests/functions/functions_simple_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 9; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for FunctionsAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -43,8 +44,6 @@ impl MidenAir for FunctionsAir { builder.assert_zero(main_current[4].clone().into() + main_current[5].clone().into() + main_current[6].clone().into() + main_current[7].clone().into() - AB::Expr::ONE); builder.assert_zero((main_current[4].clone().into() + main_current[5].clone().into() + main_current[6].clone().into() + main_current[7].clone().into()) * AB::Expr::from_u64(4) - AB::Expr::ONE); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/functions/test_air_plonky3.rs b/air-script/src/tests/functions/test_air_plonky3.rs index f52390339..d9f6c39d6 100644 --- a/air-script/src/tests/functions/test_air_plonky3.rs +++ b/air-script/src/tests/functions/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::functions::functions_complex_plonky3::{FunctionsAir, MAIN_WIDTH}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -82,8 +81,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, FunctionsAir); diff --git a/air-script/src/tests/indexed_trace_access/indexed_trace_access_plonky3.rs b/air-script/src/tests/indexed_trace_access/indexed_trace_access_plonky3.rs index d1f6eb420..9b1bea28c 100644 --- a/air-script/src/tests/indexed_trace_access/indexed_trace_access_plonky3.rs +++ b/air-script/src/tests/indexed_trace_access/indexed_trace_access_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 4; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for TraceAccessAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -36,8 +37,6 @@ impl MidenAir for TraceAccessAir { // Main integrity/transition constraints builder.when_transition().assert_zero(main_next[0].clone().into() - (main_current[1].clone().into() + AB::Expr::ONE)); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/indexed_trace_access/test_air_plonky3.rs b/air-script/src/tests/indexed_trace_access/test_air_plonky3.rs index 4b0a31c39..74d0b60d4 100644 --- a/air-script/src/tests/indexed_trace_access/test_air_plonky3.rs +++ b/air-script/src/tests/indexed_trace_access/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::indexed_trace_access::indexed_trace_access_plonky3::{MAIN_WIDTH, TraceAccessAir}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -39,8 +38,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, TraceAccessAir); diff --git a/air-script/src/tests/list_comprehension/list_comprehension_nested_plonky3.rs b/air-script/src/tests/list_comprehension/list_comprehension_nested_plonky3.rs index 734706746..5525f2068 100644 --- a/air-script/src/tests/list_comprehension/list_comprehension_nested_plonky3.rs +++ b/air-script/src/tests/list_comprehension/list_comprehension_nested_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 2; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for ListComprehensionAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -38,8 +39,6 @@ impl MidenAir for ListComprehensionAir { builder.assert_zero(main_current[0].clone().into().double() + main_current[1].clone().into() * AB::Expr::from_u64(3) - AB::Expr::from_u64(5)); builder.assert_zero(main_current[0].clone().into() * AB::Expr::from_u64(3) + main_current[1].clone().into() * AB::Expr::from_u64(4) - AB::Expr::from_u64(7)); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/list_comprehension/list_comprehension_plonky3.rs b/air-script/src/tests/list_comprehension/list_comprehension_plonky3.rs index 727cc9cc0..9d13e2e8b 100644 --- a/air-script/src/tests/list_comprehension/list_comprehension_plonky3.rs +++ b/air-script/src/tests/list_comprehension/list_comprehension_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 16; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for ListComprehensionAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -41,8 +42,6 @@ impl MidenAir for ListComprehensionAir { builder.assert_zero(main_current[1].clone().into() - (main_current[5].clone().into() - main_current[8].clone().into() - main_current[12].clone().into() + AB::Expr::from_u64(10) + main_current[6].clone().into() - main_current[9].clone().into() - main_current[13].clone().into() + AB::Expr::from_u64(20) + main_current[7].clone().into() - main_current[10].clone().into() - main_current[14].clone().into())); builder.assert_zero(main_current[14].clone().into() - AB::Expr::from_u64(10)); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/list_comprehension/test_air_plonky3.rs b/air-script/src/tests/list_comprehension/test_air_plonky3.rs index a99c57065..42080ad1f 100644 --- a/air-script/src/tests/list_comprehension/test_air_plonky3.rs +++ b/air-script/src/tests/list_comprehension/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::list_comprehension::list_comprehension_plonky3::{ListComprehensionAir, MAIN_WIDTH}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -79,8 +78,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, ListComprehensionAir); diff --git a/air-script/src/tests/list_folding/list_folding_plonky3.rs b/air-script/src/tests/list_folding/list_folding_plonky3.rs index 1a455fdd9..44f4753a3 100644 --- a/air-script/src/tests/list_folding/list_folding_plonky3.rs +++ b/air-script/src/tests/list_folding/list_folding_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 17; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for ListFoldingAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -39,8 +40,6 @@ impl MidenAir for ListFoldingAir { builder.when_transition().assert_zero(main_next[7].clone().into() - (main_current[9].clone().into() * main_current[13].clone().into() + main_current[10].clone().into() * main_current[14].clone().into() + main_current[11].clone().into() * main_current[15].clone().into() + main_current[12].clone().into() * main_current[16].clone().into() + (main_current[9].clone().into() + main_current[13].clone().into()) * (main_current[10].clone().into() + main_current[14].clone().into()) * (main_current[11].clone().into() + main_current[15].clone().into()) * (main_current[12].clone().into() + main_current[16].clone().into()))); builder.when_transition().assert_zero(main_next[8].clone().into() - (main_current[1].clone().into() + main_current[9].clone().into() * main_current[13].clone().into() + main_current[10].clone().into() * main_current[14].clone().into() + main_current[11].clone().into() * main_current[15].clone().into() + main_current[12].clone().into() * main_current[16].clone().into() + main_current[9].clone().into() * main_current[13].clone().into() + main_current[10].clone().into() * main_current[14].clone().into() + main_current[11].clone().into() * main_current[15].clone().into() + main_current[12].clone().into() * main_current[16].clone().into())); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/list_folding/test_air_plonky3.rs b/air-script/src/tests/list_folding/test_air_plonky3.rs index a87dae25b..144e4f27b 100644 --- a/air-script/src/tests/list_folding/test_air_plonky3.rs +++ b/air-script/src/tests/list_folding/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::list_folding::list_folding_plonky3::{ListFoldingAir, MAIN_WIDTH}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -82,8 +81,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, ListFoldingAir); diff --git a/air-script/src/tests/periodic_columns/periodic_columns_plonky3.rs b/air-script/src/tests/periodic_columns/periodic_columns_plonky3.rs index 22acad484..df36acbd7 100644 --- a/air-script/src/tests/periodic_columns/periodic_columns_plonky3.rs +++ b/air-script/src/tests/periodic_columns/periodic_columns_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 3; pub const AUX_WIDTH: usize = 0; @@ -33,11 +33,12 @@ where F: Field, } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -51,8 +52,6 @@ where F: Field, builder.assert_zero_ext(AB::ExprEF::from(periodic_values[0].clone().into()) * (AB::ExprEF::from(main_current[1].clone().into()) + AB::ExprEF::from(main_current[2].clone().into()))); builder.when_transition().assert_zero_ext(AB::ExprEF::from(periodic_values[1].clone().into()) * (AB::ExprEF::from(main_next[0].clone().into()) - AB::ExprEF::from(main_current[0].clone().into()))); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/periodic_columns/test_air_plonky3.rs b/air-script/src/tests/periodic_columns/test_air_plonky3.rs index 42091fa46..d732ffbed 100644 --- a/air-script/src/tests/periodic_columns/test_air_plonky3.rs +++ b/air-script/src/tests/periodic_columns/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::periodic_columns::periodic_columns_plonky3::{MAIN_WIDTH, PeriodicColumnsAir}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -40,8 +39,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, PeriodicColumnsAir); diff --git a/air-script/src/tests/pub_inputs/pub_inputs_plonky3.rs b/air-script/src/tests/pub_inputs/pub_inputs_plonky3.rs index bbdef9bf9..4cc281a74 100644 --- a/air-script/src/tests/pub_inputs/pub_inputs_plonky3.rs +++ b/air-script/src/tests/pub_inputs/pub_inputs_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 4; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for PubInputsAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -43,8 +44,6 @@ impl MidenAir for PubInputsAir { // Main integrity/transition constraints builder.when_transition().assert_zero(main_next[0].clone().into() - (main_current[1].clone().into() + main_current[2].clone().into())); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/pub_inputs/test_air_plonky3.rs b/air-script/src/tests/pub_inputs/test_air_plonky3.rs index 8595358b4..2a7ad24d0 100644 --- a/air-script/src/tests/pub_inputs/test_air_plonky3.rs +++ b/air-script/src/tests/pub_inputs/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::pub_inputs::pub_inputs_plonky3::{MAIN_WIDTH, PubInputsAir}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -43,8 +42,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![0; 32] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, PubInputsAir); diff --git a/air-script/src/tests/selectors/selectors_combine_complex_plonky3.rs b/air-script/src/tests/selectors/selectors_combine_complex_plonky3.rs index 7714372f2..a89f39d38 100644 --- a/air-script/src/tests/selectors/selectors_combine_complex_plonky3.rs +++ b/air-script/src/tests/selectors/selectors_combine_complex_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 6; pub const AUX_WIDTH: usize = 1; @@ -29,7 +29,13 @@ where F: Field, AUX_WIDTH } - fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { + fn bus_types(&self) -> Vec { + vec![ + BusType::Multiset, + ] + } + + fn build_aux_trace(&self, _main: &RowMajorMatrix, _challenges: &[EF]) -> Option> { // Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders. let num_rows = _main.height(); @@ -66,15 +72,17 @@ where F: Field, rows[i+1][j] = next_row[j]; } } - Some(trace) + let trace_f = trace.flatten_to_base(); + Some(trace_f) } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -97,10 +105,6 @@ where F: Field, builder.assert_zero((AB::Expr::ONE - main_current[0].clone().into()) * (main_current[5].clone().into() - AB::Expr::from_u64(5)) + main_current[0].clone().into() * (main_current[4].clone().into() - AB::Expr::from_u64(4))); builder.assert_zero(main_current[0].clone().into() * (main_current[5].clone().into() - AB::Expr::from_u64(20)) + (AB::Expr::ONE - main_current[0].clone().into()) * main_current[1].clone().into() * (main_current[4].clone().into() - AB::Expr::from_u64(31))); - // Aux boundary constraints - builder.when_first_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); - builder.when_last_row().assert_zero_ext(AB::ExprEF::from(aux_current[0].clone().into()) - AB::ExprEF::ONE); - // Aux integrity/transition constraints builder.when_transition().assert_zero_ext(((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * AB::ExprEF::from(main_current[0].clone().into()) * AB::ExprEF::from(main_current[5].clone().into()) + AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into()) * AB::ExprEF::from(main_current[5].clone().into())) * ((alpha.into() + beta_challenges[0].into() + beta_challenges[1].into().double()) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(main_current[1].clone().into()) * AB::ExprEF::from(main_current[5].clone().into()) + AB::ExprEF::ONE - (AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * AB::ExprEF::from(main_current[1].clone().into()) * AB::ExprEF::from(main_current[5].clone().into())) * AB::ExprEF::from(aux_current[0].clone().into()) - ((alpha.into() + AB::ExprEF::from_u64(3) * beta_challenges[0].into() + AB::ExprEF::from_u64(4) * beta_challenges[1].into()) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[1].clone().into())) * AB::ExprEF::from(main_current[4].clone().into()) + AB::ExprEF::ONE - (AB::ExprEF::ONE - AB::ExprEF::from(main_current[0].clone().into())) * (AB::ExprEF::ONE - AB::ExprEF::from(main_current[1].clone().into())) * AB::ExprEF::from(main_current[4].clone().into())) * AB::ExprEF::from(aux_next[0].clone().into())); } diff --git a/air-script/src/tests/selectors/selectors_combine_simple_plonky3.rs b/air-script/src/tests/selectors/selectors_combine_simple_plonky3.rs index df958a51e..ed223440f 100644 --- a/air-script/src/tests/selectors/selectors_combine_simple_plonky3.rs +++ b/air-script/src/tests/selectors/selectors_combine_simple_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 4; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for SelectorsAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -37,8 +38,6 @@ impl MidenAir for SelectorsAir { builder.when_transition().assert_zero(main_next[1].clone().into() - main_current[2].clone().into()); builder.when_transition().assert_zero(main_current[3].clone().into() * (main_next[0].clone().into() - (main_current[0].clone().into() + main_current[1].clone().into())) + (AB::Expr::ONE - main_current[3].clone().into()) * (main_next[0].clone().into() - main_current[0].clone().into() * main_current[1].clone().into())); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs b/air-script/src/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs index c7c4e3940..d482f2c9d 100644 --- a/air-script/src/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs +++ b/air-script/src/tests/selectors/selectors_combine_with_list_comprehensions_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 6; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for SelectorsAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -38,8 +39,6 @@ impl MidenAir for SelectorsAir { builder.assert_zero((AB::Expr::ONE - main_current[0].clone().into()) * (main_current[5].clone().into() - AB::Expr::from_u64(8)) + main_current[0].clone().into() * (main_current[4].clone().into() - AB::Expr::from_u64(2))); builder.assert_zero(main_current[0].clone().into() * (main_current[5].clone().into() - AB::Expr::from_u64(4)) + (AB::Expr::ONE - main_current[0].clone().into()) * main_current[1].clone().into() * (main_current[4].clone().into() - AB::Expr::from_u64(6))); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/selectors/selectors_plonky3.rs b/air-script/src/tests/selectors/selectors_plonky3.rs index 51f70004b..e16eb2f28 100644 --- a/air-script/src/tests/selectors/selectors_plonky3.rs +++ b/air-script/src/tests/selectors/selectors_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 4; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for SelectorsAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -37,8 +38,6 @@ impl MidenAir for SelectorsAir { builder.when_transition().assert_zero(main_current[0].clone().into() * (AB::Expr::ONE - main_current[1].clone().into()) * main_next[3].clone().into()); builder.when_transition().assert_zero(main_current[0].clone().into() * main_current[1].clone().into() * main_current[2].clone().into() * (main_next[3].clone().into() - main_current[3].clone().into()) + (AB::Expr::ONE - main_current[1].clone().into()) * (AB::Expr::ONE - main_current[2].clone().into()) * (main_next[3].clone().into() - AB::Expr::ONE)); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/selectors/selectors_with_evaluators_plonky3.rs b/air-script/src/tests/selectors/selectors_with_evaluators_plonky3.rs index 1dde4fecf..42affcf13 100644 --- a/air-script/src/tests/selectors/selectors_with_evaluators_plonky3.rs +++ b/air-script/src/tests/selectors/selectors_with_evaluators_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 4; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for SelectorsAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -37,8 +38,6 @@ impl MidenAir for SelectorsAir { builder.when_transition().assert_zero(main_current[0].clone().into() * (AB::Expr::ONE - main_current[1].clone().into()) * main_next[3].clone().into()); builder.when_transition().assert_zero(main_current[1].clone().into() * main_current[2].clone().into() * main_current[0].clone().into() * (main_next[3].clone().into() - main_current[3].clone().into()) + (AB::Expr::ONE - main_current[1].clone().into()) * (AB::Expr::ONE - main_current[2].clone().into()) * (main_next[3].clone().into() - AB::Expr::ONE)); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/selectors/test_air_plonky3.rs b/air-script/src/tests/selectors/test_air_plonky3.rs index ef3c01f81..c8cfbffa2 100644 --- a/air-script/src/tests/selectors/test_air_plonky3.rs +++ b/air-script/src/tests/selectors/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::selectors::selectors_with_evaluators_plonky3::{MAIN_WIDTH, SelectorsAir}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -43,8 +42,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, SelectorsAir); diff --git a/air-script/src/tests/system/system_plonky3.rs b/air-script/src/tests/system/system_plonky3.rs index 574bccd9b..acc5e98be 100644 --- a/air-script/src/tests/system/system_plonky3.rs +++ b/air-script/src/tests/system/system_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 3; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for SystemAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -36,8 +37,6 @@ impl MidenAir for SystemAir { // Main integrity/transition constraints builder.when_transition().assert_zero(main_next[0].clone().into() - (main_current[0].clone().into() + AB::Expr::ONE)); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/system/test_air_plonky3.rs b/air-script/src/tests/system/test_air_plonky3.rs index 404ea03d7..952a7412f 100644 --- a/air-script/src/tests/system/test_air_plonky3.rs +++ b/air-script/src/tests/system/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::system::system_plonky3::{MAIN_WIDTH, SystemAir}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -40,8 +39,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, SystemAir); diff --git a/air-script/src/tests/trace_col_groups/test_air_plonky3.rs b/air-script/src/tests/trace_col_groups/test_air_plonky3.rs index 1b4522ecf..920a7f3db 100644 --- a/air-script/src/tests/trace_col_groups/test_air_plonky3.rs +++ b/air-script/src/tests/trace_col_groups/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::trace_col_groups::trace_col_groups_plonky3::{MAIN_WIDTH, TraceColGroupAir}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -58,8 +57,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 16] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, TraceColGroupAir); diff --git a/air-script/src/tests/trace_col_groups/trace_col_groups_plonky3.rs b/air-script/src/tests/trace_col_groups/trace_col_groups_plonky3.rs index 026304e2d..5a0b38979 100644 --- a/air-script/src/tests/trace_col_groups/trace_col_groups_plonky3.rs +++ b/air-script/src/tests/trace_col_groups/trace_col_groups_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 9; pub const AUX_WIDTH: usize = 0; @@ -19,11 +19,12 @@ impl MidenAir for TraceColGroupAir { } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -37,8 +38,6 @@ impl MidenAir for TraceColGroupAir { builder.when_transition().assert_zero(main_next[2].clone().into() - (main_current[2].clone().into() + AB::Expr::ONE)); builder.when_transition().assert_zero(main_next[1].clone().into() - (main_current[1].clone().into() - AB::Expr::ONE)); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/air-script/src/tests/variables/test_air_plonky3.rs b/air-script/src/tests/variables/test_air_plonky3.rs index b8d2d88ef..356a4d9fb 100644 --- a/air-script/src/tests/variables/test_air_plonky3.rs +++ b/air-script/src/tests/variables/test_air_plonky3.rs @@ -3,12 +3,11 @@ use p3_miden_air::RowMajorMatrix; use crate::{ generate_air_plonky3_test_with_airscript_traits, - test_utils::plonky3_traits::check_constraints_with_airscript_traits, tests::variables::variables_plonky3::{MAIN_WIDTH, VariablesAir}, }; -pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { - let num_rows = 32; +pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix { + let num_rows = 512; let trace_length = num_rows * MAIN_WIDTH; let mut long_trace = F::zero_vec(trace_length); @@ -43,8 +42,12 @@ pub fn generate_trace_rows(inputs: Vec) -> RowMajorMatrix< trace } -fn generate_inputs() -> Vec { +fn generate_inputs() -> Vec { vec![1; 32] } +fn generate_var_len_pub_inputs<'a>() -> Vec>> { + vec![] +} + generate_air_plonky3_test_with_airscript_traits!(test_air_plonky3, VariablesAir); diff --git a/air-script/src/tests/variables/variables_plonky3.rs b/air-script/src/tests/variables/variables_plonky3.rs index bfe0da908..57cbc460d 100644 --- a/air-script/src/tests/variables/variables_plonky3.rs +++ b/air-script/src/tests/variables/variables_plonky3.rs @@ -2,7 +2,7 @@ use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; use p3_matrix::Matrix; use p3_matrix::dense::RowMajorMatrixView; use p3_matrix::stack::VerticalPair; -use p3_miden_air::{MidenAir, MidenAirBuilder, RowMajorMatrix}; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; pub const MAIN_WIDTH: usize = 4; pub const AUX_WIDTH: usize = 0; @@ -32,11 +32,12 @@ where F: Field, } fn eval(&self, builder: &mut AB) - where AB: MidenAirBuilder, + where AB: MidenAirBuilder, { let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); - let preprocessed = builder.preprocessed(); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); let main = builder.main(); let (main_current, main_next) = ( main.row_slice(0).unwrap(), @@ -53,8 +54,6 @@ where F: Field, builder.assert_zero((AB::Expr::ONE - main_current[0].clone().into()) * (main_current[3].clone().into() - main_current[1].clone().into() - main_current[2].clone().into()) - (AB::Expr::from_u64(6) - (AB::Expr::from_u64(7) - main_current[0].clone().into()))); builder.when_transition().assert_zero(main_current[0].clone().into() * (main_current[3].clone().into() - main_current[1].clone().into() * main_current[2].clone().into()) - (AB::Expr::ONE - main_next[0].clone().into())); - // Aux boundary constraints - // Aux integrity/transition constraints } } \ No newline at end of file diff --git a/codegen/plonky3/src/air/boundary_constraints.rs b/codegen/plonky3/src/air/boundary_constraints.rs index aba2206b0..79a4f4f7a 100644 --- a/codegen/plonky3/src/air/boundary_constraints.rs +++ b/codegen/plonky3/src/air/boundary_constraints.rs @@ -13,6 +13,7 @@ pub(super) fn add_main_boundary_constraints(eval_func: &mut Function, ir: &Air) } } +#[allow(dead_code)] /// Adds the aux boundary constraints to the generated code. pub(super) fn add_aux_boundary_constraints(eval_func: &mut Function, ir: &Air) { eval_func.line(""); diff --git a/codegen/plonky3/src/air/mod.rs b/codegen/plonky3/src/air/mod.rs index 98b881f70..32a585ce9 100644 --- a/codegen/plonky3/src/air/mod.rs +++ b/codegen/plonky3/src/air/mod.rs @@ -6,7 +6,7 @@ use air_ir::Air; use super::Scope; use crate::air::{ - boundary_constraints::{add_aux_boundary_constraints, add_main_boundary_constraints}, + boundary_constraints::add_main_boundary_constraints, graph::Codegen, integrity_constraints::{add_aux_integrity_constraints, add_main_integrity_constraints}, }; @@ -45,8 +45,13 @@ fn add_constants(scope: &mut Scope, ir: &Air) { let aux_width = ir.trace_segment_widths.get(1).cloned().unwrap_or(0); let num_periodic_values = ir.periodic_columns().count(); let period = ir.periodic_columns().map(|col| col.period()).max().unwrap_or(0); - let num_public_values = - ir.public_inputs().map(|public_input| public_input.size()).sum::(); + let num_public_values = ir + .public_inputs() + .map(|public_input| match public_input { + air_ir::PublicInput::Vector { size, .. } => size, + air_ir::PublicInput::Table { .. } => &0, + }) + .sum::(); let max_beta_challenge_power = ir.num_random_values.saturating_sub(1); let constants = [ @@ -112,6 +117,17 @@ fn add_air_struct(scope: &mut Scope, ir: &Air, name: &str) { .line("1 + MAX_BETA_CHALLENGE_POWER"); miden_air_impl.new_fn("aux_width").arg_ref_self().ret("usize").line("AUX_WIDTH"); + + let bus_types_fn = miden_air_impl.new_fn("bus_types").arg_ref_self().ret("Vec"); + bus_types_fn.line("vec!["); + for (_id, bus) in &ir.buses { + let bus_type_str = match bus.bus_type { + air_ir::BusType::Multiset => "BusType::Multiset", + air_ir::BusType::Logup => "BusType::Logup", + }; + bus_types_fn.line(format!(" {bus_type_str},")); + } + bus_types_fn.line("]"); } // add the build_aux_trace function if needed @@ -121,7 +137,7 @@ fn add_air_struct(scope: &mut Scope, ir: &Air, name: &str) { .arg_ref_self() .arg("_main", "&RowMajorMatrix") .arg("_challenges", "&[EF]") - .ret("Option>"); + .ret("Option>"); build_aux_trace_func.line("// Note: consider using Some(build_aux_trace_with_miden_vm::(_main, _challenges, module)) if you want to build the aux trace using Miden VM aux trace builders."); build_aux_trace_func.line(""); build_aux_trace_func.line("let num_rows = _main.height();"); @@ -158,19 +174,22 @@ fn add_air_struct(scope: &mut Scope, ir: &Air, name: &str) { build_aux_trace_func.line(" rows[i+1][j] = next_row[j];"); build_aux_trace_func.line(" }"); build_aux_trace_func.line("}"); - build_aux_trace_func.line("Some(trace)"); + build_aux_trace_func.line("let trace_f = trace.flatten_to_base();"); + build_aux_trace_func.line("Some(trace_f)"); } // add the eval function let eval_func = miden_air_impl .new_fn("eval") .generic("AB") - .bound("AB", "MidenAirBuilder") + .bound("AB", "MidenAirBuilder") .arg_ref_self() .arg("builder", "&mut AB"); eval_func.line("let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect(\"Wrong number of public values\");"); eval_func.line("let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect(\"Wrong number of periodic values\");"); - eval_func.line("let preprocessed = builder.preprocessed();"); + + eval_func.line("// Note: for now, we do not have any preprocessed values"); + eval_func.line("// let preprocessed = builder.preprocessed();"); eval_func.line("let main = builder.main();"); eval_func.line("let (main_current, main_next) = ("); @@ -194,7 +213,8 @@ fn add_air_struct(scope: &mut Scope, ir: &Air, name: &str) { add_main_integrity_constraints(eval_func, ir); - add_aux_boundary_constraints(eval_func, ir); + // Note: Plonky3 automatically adds aux boundary constraints + //add_aux_boundary_constraints(eval_func, ir); add_aux_integrity_constraints(eval_func, ir); } diff --git a/codegen/plonky3/src/imports.rs b/codegen/plonky3/src/imports.rs index 8655c7161..d96c0dd40 100644 --- a/codegen/plonky3/src/imports.rs +++ b/codegen/plonky3/src/imports.rs @@ -9,6 +9,7 @@ pub(super) fn add_imports(scope: &mut Scope) { scope.import("p3_matrix", "Matrix"); scope.import("p3_matrix::dense", "RowMajorMatrixView"); scope.import("p3_matrix::stack", "VerticalPair"); + scope.import("p3_miden_air", "BusType"); scope.import("p3_miden_air", "MidenAir"); scope.import("p3_miden_air", "MidenAirBuilder"); scope.import("p3_miden_air", "RowMajorMatrix"); diff --git a/parser/src/ast/module.rs b/parser/src/ast/module.rs index 323eaf417..9ffbb41ae 100644 --- a/parser/src/ast/module.rs +++ b/parser/src/ast/module.rs @@ -435,9 +435,6 @@ impl Module { conflicting_declaration(diagnostics, "function", prev.span(), function.name.span()); return Err(SemanticAnalysisError::NameConflict(function.name.span())); } - - println!("Declared function: {:?}", function.name); - self.functions.insert(function.name, function); Ok(()) From 0e99eefde84ab4694e51a782b72989e74d6eb3af Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Tue, 6 Jan 2026 14:38:10 +0100 Subject: [PATCH 26/83] feat: Target p3-miden repo --- air-script/Cargo.toml | 53 +++++++++++++------ .../src/test_utils/air_tester_macros.rs | 4 +- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/air-script/Cargo.toml b/air-script/Cargo.toml index ed1709f76..7e05c4e4b 100644 --- a/air-script/Cargo.toml +++ b/air-script/Cargo.toml @@ -28,10 +28,13 @@ log = { version = "0.4", default-features = false } miden-diagnostics = { workspace = true } mir = { package = "air-mir", path = "../mir", version = "0.5" } -# 0xMiden Plonky3 Fork -p3-matrix = { package = "p3-matrix", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } -p3-field = { package = "p3-field", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } -p3-miden-air = { package = "miden-air", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +# Upstream Plonky3 dependencies +p3-field = { version = "0.4.2", default-features = false } +p3-matrix = { version = "0.4.2", default-features = false } +# Internal p3-miden crates +#p3-miden-air = { version = "0.4.0", default-features = false } +# Internal p3-miden crates +p3-miden-air = { package = "p3-miden-air", path = "../../p3-miden/p3-miden-air", default-features = false } # MassaLabs fork miden-processor = { package = "miden-processor", git="https://github.com/massalabs/miden-vm", rev = "bc553af69a2543a0789830e8508b019694528181", default-features = false } @@ -40,17 +43,37 @@ miden-air = { package = "miden-air", git="https://github.com/massalabs/miden-vm" [dev-dependencies] expect-test = "1.4" -# 0xMiden Plonky3 Fork -p3-air = { package = "p3-air", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } -p3-challenger = { package = "p3-challenger", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } -p3-commit = { package = "p3-commit", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } -p3-dft = { package = "p3-dft", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } -p3-fri = { package = "p3-fri", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } -p3-merkle-tree = { package = "p3-merkle-tree", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } -p3-miden-prover = { package = "miden-prover", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } -p3-goldilocks = { package = "p3-goldilocks", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } -p3-sha256 = { package = "p3-sha256", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } -p3-symmetric = { package = "p3-symmetric", git="https://github.com/0xMiden/Plonky3", rev = "632fb26a9141cc70f903e5378035ca8d5855c490", default-features = false } +# Upstream Plonky3 dependencies +p3-air = { version = "0.4.2", default-features = false } +p3-baby-bear = { version = "0.4.2", default-features = false } +p3-challenger = { version = "0.4.2", default-features = false } +p3-circle = { version = "0.4.2", default-features = false } +p3-commit = { version = "0.4.2", default-features = false } +p3-dft = { version = "0.4.2", default-features = false } +p3-field-testing = { version = "0.4.2", default-features = false } +p3-fri = { version = "0.4.2", default-features = false } +p3-goldilocks = { version = "0.4.2", default-features = false } +p3-interpolation = { version = "0.4.2", default-features = false } +p3-keccak = { version = "0.4.2", default-features = false } +p3-matrix = { version = "0.4.2", default-features = false } +p3-maybe-rayon = { version = "0.4.2", default-features = false } +p3-mds = { version = "0.4.2", default-features = false } +p3-merkle-tree = { version = "0.4.2", default-features = false } +p3-mersenne-31 = { version = "0.4.2", default-features = false } +p3-poseidon = { version = "0.4.2", default-features = false } +p3-poseidon2 = { version = "0.4.2", default-features = false } +p3-sha256 = { version = "0.4.2", default-features = false } +p3-symmetric = { version = "0.4.2", default-features = false } +p3-uni-stark = { version = "0.4.2", default-features = false } +p3-util = { version = "0.4.2", default-features = false } +# Internal p3-miden crates +#p3-miden-fri = { package = "p3-miden-fri", git = "https://github.com/0xMiden/p3-miden", rev = "134c14d3b438e3c077cd0fba28903d41fe189e52", default-features = false } +#p3-miden-prover = { package = "p3-miden-prover", git = "https://github.com/0xMiden/p3-miden", rev = "134c14d3b438e3c077cd0fba28903d41fe189e52", default-features = false } +#p3-miden-uni-stark = { package = "p3-miden-uni-stark", git = "https://github.com/0xMiden/p3-miden", rev = "134c14d3b438e3c077cd0fba28903d41fe189e52", default-features = false } +# Internal p3-miden crates +p3-miden-fri = { package = "p3-miden-fri", path = "../../p3-miden/p3-miden-fri", default-features = false } +p3-miden-prover = { package = "p3-miden-prover", path = "../../p3-miden/p3-miden-prover", default-features = false } +p3-miden-uni-stark = { package = "p3-miden-uni-stark", path = "../../p3-miden/p3-miden-uni-stark", default-features = false } winter-air = { package = "winter-air", version = "0.12", default-features = false } winter-math = { package = "winter-math", version = "0.12", default-features = false } diff --git a/air-script/src/test_utils/air_tester_macros.rs b/air-script/src/test_utils/air_tester_macros.rs index 7c5e305ee..663e53e7f 100644 --- a/air-script/src/test_utils/air_tester_macros.rs +++ b/air-script/src/test_utils/air_tester_macros.rs @@ -52,7 +52,7 @@ macro_rules! generate_air_plonky3_test_with_airscript_traits { p3_challenger::HashChallenger, >; type Dft = p3_dft::Radix2DitParallel; - type Pcs = p3_fri::TwoAdicFriPcs; + type Pcs = p3_miden_fri::TwoAdicFriPcs; type MyConfig = p3_miden_prover::StarkConfig; let byte_hash = ByteHash {}; @@ -62,7 +62,7 @@ macro_rules! generate_air_plonky3_test_with_airscript_traits { let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); let challenger = Challenger::from_hasher(vec![], byte_hash); let dft = Dft::default(); - let mut fri_params = p3_fri::create_recursive_miden_fri_params(challenge_mmcs); + let mut fri_params = p3_miden_fri::create_recursive_miden_fri_params(challenge_mmcs); let pcs = Pcs::new(dft, val_mmcs, fri_params); let config = MyConfig::new(pcs, challenger); From e02364d8c98227e9fed09704c320b49dae51f99f Mon Sep 17 00:00:00 2001 From: al <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 12:44:59 +0400 Subject: [PATCH 27/83] Add Miden VM constraint files --- Cargo.toml | 1 + constraints/Cargo.toml | 23 + constraints/ace.air | 313 ++++++++++ constraints/bitwise.air | 158 +++++ constraints/chiplets.air | 161 +++++ constraints/decoder.air | 14 + constraints/hasher.air | 256 ++++++++ constraints/memory.air | 237 ++++++++ constraints/miden-vm-old/bitwise.air | 114 ++++ constraints/miden-vm-old/chiplets.air | 38 ++ constraints/miden-vm-old/decoder.air | 654 +++++++++++++++++++++ constraints/miden-vm-old/hash.air | 208 +++++++ constraints/miden-vm-old/memory.air | 120 ++++ constraints/miden-vm-old/range_checker.air | 62 ++ constraints/miden_vm.air | 184 ++++++ constraints/range_checker.air | 86 +++ constraints/rpo.air | 97 +++ constraints/stack.air | 15 + constraints/system.air | 56 ++ constraints/tests/miden_vm.rs | 45 ++ constraints/utils.air | 142 +++++ 21 files changed, 2984 insertions(+) create mode 100644 constraints/Cargo.toml create mode 100644 constraints/ace.air create mode 100644 constraints/bitwise.air create mode 100644 constraints/chiplets.air create mode 100644 constraints/decoder.air create mode 100644 constraints/hasher.air create mode 100644 constraints/memory.air create mode 100644 constraints/miden-vm-old/bitwise.air create mode 100644 constraints/miden-vm-old/chiplets.air create mode 100644 constraints/miden-vm-old/decoder.air create mode 100644 constraints/miden-vm-old/hash.air create mode 100644 constraints/miden-vm-old/memory.air create mode 100644 constraints/miden-vm-old/range_checker.air create mode 100644 constraints/miden_vm.air create mode 100644 constraints/range_checker.air create mode 100644 constraints/rpo.air create mode 100644 constraints/stack.air create mode 100644 constraints/system.air create mode 100644 constraints/tests/miden_vm.rs create mode 100644 constraints/utils.air diff --git a/Cargo.toml b/Cargo.toml index 36de3cedc..8683cb805 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "codegen/winterfell", "codegen/ace", "codegen/plonky3", + "constraints", ] resolver = "2" diff --git a/constraints/Cargo.toml b/constraints/Cargo.toml new file mode 100644 index 000000000..2263eadd9 --- /dev/null +++ b/constraints/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "constraints" +version = "0.1.0" +edition = "2021" + +[dependencies] +air-ir = { path = "../air", version = "0.5" } +air-parser = { path = "../parser", version = "0.5" } +air-pass = { path = "../pass", version = "0.5" } +air-mir = { path = "../mir", version = "0.5" } +air-codegen-ace = { path = "../codegen/ace", version = "0.5" } +miden-diagnostics = { workspace = true } +miden-core = { package = "miden-core", version = "0.13", default-features = false } + +winter-air = { package = "winter-air", version = "0.12", default-features = false } +winter-math = { package = "winter-math", version = "0.12", default-features = false } +winter-utils = { package = "winter-utils", version = "0.12", default-features = false } +winter-prover = { package = "winter-prover", version = "0.12", default-features = false } +winter-verifier = { package = "winter-verifier", version = "0.12", default-features = false } +winterfell = { package = "winterfell", version = "0.12", default-features = false } + +[dev-dependencies] +anyhow = "1" \ No newline at end of file diff --git a/constraints/ace.air b/constraints/ace.air new file mode 100644 index 000000000..f822adbb3 --- /dev/null +++ b/constraints/ace.air @@ -0,0 +1,313 @@ +########################################################################################## +# ACE CHIPLET CONSTRAINTS MODULE +########################################################################################## +# +# The ACE (Arithmetic Circuit Evaluation) chiplet reduces the number of cycles required +# when recursively verifying a STARK proof in Miden assembly by evaluating arithmetic +# circuits and ensuring they evaluate to zero over given inputs. +# +# OVERVIEW: +# The ACE chiplet operates in two phases: +# 1. READ blocks: Load extension field elements from memory and assign node IDs +# 2. EVAL blocks: Execute arithmetic operations on previously loaded nodes +# +# Each section starts with READ operations to load inputs, followed by EVAL operations +# to compute intermediate and final results. The final result must evaluate to zero +# to represent a valid constraint satisfaction. +# +# ACE TRACE TABLE LAYOUT (16 columns): +# +# ┌─────────────┬────────────────────────────────────────────────────────────────────────────┐ +# │ Column │ Purpose │ +# ├─────────────┼────────────────────────────────────────────────────────────────────────────┤ +# │ sstart │ Section start flag (binary): 1 = first row of section, 0 = otherwise │ +# │ sblock │ Block selector (binary): 0 = READ block, 1 = EVAL block │ +# │ ctx │ Memory access context (constant throughout section) │ +# │ ptr │ Memory pointer: increments by +4 in READ, +1 in EVAL blocks │ +# │ clk │ Memory access clock cycle (constant within section) │ +# │ op │ Operation type (EVAL only): -1 = SUB, 0 = MUL, 1 = ADD │ +# │ id0 │ Result node ID: decrements by -2 in READ, -1 in EVAL │ +# │ v0_0 │ Result node value (extension field element, coefficient 0) │ +# │ v0_1 │ Result node value (extension field element, coefficient 1) │ +# │ id1 │ First operand node ID │ +# │ v1_0 │ First operand value (extension field element, coefficient 0) │ +# │ v1_1 │ First operand value (extension field element, coefficient 1) │ +# │ id2 / n_eval│ Dual use: 2nd operand node ID (EVAL) / #evaluations in section (READ) │ +# │ v2_0 │ Second operand value (extension field element, coefficient 0) │ +# │ v2_1 │ Second operand value (extension field element, coefficient 1) │ +# │ m0 │ Wire bus multiplicity (for node tracking, not yet implemented) │ +# └─────────────┴────────────────────────────────────────────────────────────────────────────┘ +# +# STATUS: Core constraints implemented, wire bus constraints not implemented +# +# REFERENCES: +# - ACE Design: https://0xmiden.github.io/miden-vm/design/chiplets/ace.html +########################################################################################## + +mod ace + +use chiplets::*; +use utils::*; + +########################################################################################## +# ACE CHIPLET CONSTRAINTS +########################################################################################## + +# Enforces constraints on all rows of ACE chiplet. +ev ace_chiplet_constraints_all_rows([s3, ace[16]]) { + let sstart = ace[0]; # Section start flag + let sblock = ace[1]; # Block selector (0=READ, 1=EVAL) + let ctx = ace[2]; # Memory access context + let ptr = ace[3]; # Memory pointer + let clk = ace[4]; # Memory access clock + let op = ace[5]; # Operation type (for EVAL block) + let id0 = ace[6]; # First node identifier + let v0_0 = ace[7]; # First node value (element 0) + let v0_1 = ace[8]; # First node value (element 1) + let id1 = ace[9]; # Second node identifier + let v1_0 = ace[10]; # Second node value (element 0) + let v1_1 = ace[11]; # Second node value (element 1) + let id2 = ace[12]; # Third node identifier (result node in EVAL) + let n_eval = ace[12]; # Total number of arithmetic operations + let v2_0 = ace[13]; # Third node value (element 0) + let v2_1 = ace[14]; # Third node value (element 1) + let m0 = ace[15]; # Multiplicity for wire bus operations + + let flag_ace_next = flag_ace_current_and_next(s3'); + let flag_ace_last = flag_ace_last(s3'); + + # Section and block flags constraints + enf section_block_flags_constraints([s3, sstart, sstart', sblock, id0, n_eval]); + + # Section general constraints + enf section_constraints([sstart, sblock, ctx, ptr, clk, id0]) when flag_ace_next; + + # Section specific constraints: READ block + enf enforce_read_block_constraints([sblock, id0, id1]); + + # Section specific constraints: EVAL block + enf enforce_eval_block_constraints([sblock, op, v0_0, v0_1, v1_0, v1_1, v2_0, v2_1]); + + # Finalization constraints + let f_end = binary_or(binary_and(flag_ace_next, sstart'), flag_ace_last); + enf finalization_constraints([id0, v0_0, v0_1]) when f_end; +} + +# Enforces that on the first row of ACE chiplet we should have sstart' = 1 +ev ace_chiplet_constraints_first_row([ace[16]]) { + let sstart = ace[0]; # Section start flag + + enf sstart' = 1; +} + +########################################################################################## +# CONSTRAINT EVALUATORS +########################################################################################## + +# Section and block flag management +# +# This evaluator enforces the proper sequencing of ACE chiplet operations: +# - Sections must start with READ blocks and end with EVAL blocks +# - Block transitions follow valid patterns (READ→EVAL within sections) +# - Section boundaries are correctly marked with sstart flags +# - READ→EVAL transition occurs when n_eval matches id0 +ev section_block_flags_constraints([s3, sstart, sstart_next, sblock, id0, n_eval]) { + let flag_ace_next = flag_ace_current_and_next(s3'); + let flag_ace_last = flag_ace_last(s3'); + + let section_flags = section_flags(s3', sstart, sstart_next); + let f_start = section_flags[0]; + let f_next = section_flags[1]; + let f_end = section_flags[2]; + + let block_flags = block_flags(sblock); + let block_flags_next = block_flags(sblock'); + let f_read = block_flags[0]; + let f_eval = block_flags[1]; + let f_read_next = block_flags_next[0]; + let f_eval_next = block_flags_next[1]; + + # Binary constraints for section and block flags + enf enforce_binary_columns([sstart, sblock]); + + # Last row of ACE chiplet cannot be section start + enf sstart = 0 when flag_ace_last; + + # Prevent consecutive section starts within ACE chiplet + enf binary_and(sstart, sstart') = 0 when flag_ace_next; + + # Sections must start with READ blocks (not EVAL) + enf f_eval = 0 when f_start; + + # EVAL blocks cannot be followed by READ blocks within same section + enf f_read_next = 0 when binary_and(f_next, f_eval); + + # Sections must end with EVAL blocks (not READ) + enf f_read = 0 when f_end; + + # In a READ block, n_eval stays constant. + # When transitioning to an EVAL block, the next id0 must equal n_eval - 1. + # This ensures proper sequencing: READ loads nodes with IDs, EVAL starts processing + # from the highest loaded node ID (n_eval - 1). + enf select(f_read_next, n_eval', id0' + 1) = n_eval when f_read; +} + +# Constrains binary columns for flags and selectors +ev enforce_binary_columns([sstart, sblock]) { + # Section start flag must be binary + enf is_binary([sstart]); + + # Block selector must be binary + enf is_binary([sblock]); +} + +# Section-level constraints (common to both READ and EVAL blocks) +# +# These constraints apply throughout an entire section regardless of block type: +# - Context (ctx) and clock (clk) remain constant within a section +# - Memory pointer (ptr) increments with fixed increments: +4 in READ, +1 in EVAL +# - Node identifiers (id0) decrement with fixed decrements: -2 in READ, -1 in EVAL +# +# Note: These constraints are active when the next row is NOT the start of a new section, +# i.e., all but the last rows of the section (intra-section transitions only). +ev section_constraints([sstart, sblock, ctx, ptr, clk, id0]) { + let flag_within_section = binary_not(sstart'); + let flag_read = !sblock; + let flag_eval = sblock; + + # Context consistency within a section + enf ctx' = ctx when flag_within_section; + + # Clock consistency within a section + enf clk' = clk when flag_within_section; + + # Memory pointer increases by 4 in READ blocks and by 1 in EVAL blocks + enf ptr' = ptr + 4 * flag_read + flag_eval when flag_within_section; + + # Node identifiers decrease by 2 in READ blocks and by 1 in EVAL blocks + enf id0 = id0' + 2 * flag_read + flag_eval when flag_within_section; +} + +# Finalization constraints (ensure final result is zero) +ev finalization_constraints([id0, v0_0, v0_1]) { + # The final result of the arithmetic circuit evaluation must be zero + # This ensures the circuit represents a valid constraint + enf v0_0 = 0; + enf v0_1 = 0; + + # Final node ID should be the root node + enf id0 = 0; +} + +# READ block operation constraints +# +# The only specific constraint to READ blocks is a constraint ensuring that the IDs of the two +# loaded extension field elements is consecutive. +ev enforce_read_block_constraints([sblock, id0, id1]) { + let is_read_block = binary_not(sblock); + + # In READ block, we read two extension field elements and assign node IDs + # The two node IDs should be consecutive. Note that the node IDs are decreasing. + enf id1 = id0 - 1 when is_read_block; +} + +# EVAL block operation constraints +# +# Enforces arithmetic circuit evaluation during EVAL operations: +# - Validates operation codes (op ∈ {-1, 0, 1} for SUB, MUL, ADD) +# - Performs extension field arithmetic on input nodes (v1, v2) +ev enforce_eval_block_constraints([sblock, op, v0_0, v0_1, v1_0, v1_1, v2_0, v2_1]) { + let is_eval_block = sblock; + + # In EVAL block, we decode instruction and perform arithmetic operation + # The 'op' column contains the operation type and must be equal to either -1, 0, or 1 + enf op * (op - 1) * (op + 1) = 0 when is_eval_block; + + # Arithmetic operation constraint based on op type + enf enforce_arithmetic_operation([op, v0_0, v0_1, v1_0, v1_1, v2_0, v2_1]) when is_eval_block; +} + + +# Arithmetic operation evaluation for EVAL block +# +# Performs extension field arithmetic based on operation code: +# - op = -1: Subtraction (v0 = v1 - v2) +# - op = 0: Multiplication (v0 = v1 × v2) +# - op = 1: Addition (v0 = v1 + v2) +# +# All arithmetic is in the quadratic extension field 𝔽ₚ[x]/(x² - x + 2) +ev enforce_arithmetic_operation([op, v0_0, v0_1, v1_0, v1_1, v2_0, v2_1]) { + # Decode operation and enforce arithmetic constraint for extension field elements + # Extension field elements are represented as (a_0, a_1) where element = a_0 + a_1 * α + + let linear_op = compute_linear_op(op, v1_0, v1_1, v2_0, v2_1); + let linear_op_0 = linear_op[0]; + let linear_op_1 = linear_op[1]; + + let non_linear_op = compute_non_linear_op(v1_0, v1_1, v2_0, v2_1); + let non_linear_op_0 = non_linear_op[0]; + let non_linear_op_1 = non_linear_op[1]; + + let op_square = op^2; + + enf op_square * (linear_op_0 - non_linear_op_0) + non_linear_op_0 = v0_0; + enf op_square * (linear_op_1 - non_linear_op_1) + non_linear_op_1 = v0_1; +} + +# Computes linear operations (addition and subtraction) in extension field +# - op = -1: Subtraction (v0 = v1 - v2) +# - op = 1: Addition (v0 = v1 + v2) +fn compute_linear_op(op: felt, v1_0: felt, v1_1: felt, v2_0: felt, v2_1: felt) -> felt[2] { + let res0 = v1_0 + op * v2_0; + let res1 = v1_1 + op * v2_1; + return [res0, res1]; +} + +# Multiplication in the quadratic extension field. +# +# The extension is 𝔽ₚ[x]/(x² - x + 2) +fn compute_non_linear_op(v1_0: felt, v1_1: felt, v2_0: felt, v2_1: felt) -> felt[2] { + let a0 = v1_0; + let a1 = v1_1; + let b0 = v2_0; + let b1 = v2_1; + + let res0 = a0 * b0 - 2 * a1 * b1; + let res1 = (b0 + b1) * (a0 + a1) - a0 * b0; + + return [res0, res1]; +} + +########################################################################################## +# HELPER FUNCTIONS +########################################################################################## + +# ACE chiplet active in current row and continuing to next row +fn flag_ace_current_and_next(s3_next: felt) -> felt { + return !s3_next; +} + +# ACE chiplet active in current row but transitioning out in next row +fn flag_ace_last(s3_next: felt) -> felt { + return s3_next; +} + +# Computes section-level flags: [f_start, f_next, f_end] +fn section_flags(s3_next: felt, s_start: felt, s_start_next: felt) -> felt[3] { + let f_ace_next = flag_ace_current_and_next(s3_next); + let f_ace_last = flag_ace_last(s3_next); + + let f_start = s_start; + let f_next = !s_start_next; + let f_end = binary_or(binary_and(f_ace_next, s_start_next), f_ace_last); + + return [f_start, f_next, f_end]; +} + +# Computes block-level flags: [f_read, f_eval] +fn block_flags(s_block: felt) -> felt[2] { + let f_read = !s_block; + let f_eval = s_block; + + return [f_read, f_eval]; +} \ No newline at end of file diff --git a/constraints/bitwise.air b/constraints/bitwise.air new file mode 100644 index 000000000..78efb50a6 --- /dev/null +++ b/constraints/bitwise.air @@ -0,0 +1,158 @@ +########################################################################################## +# BITWISE OPERATIONS CHIPLET +########################################################################################## +# +# The Bitwise chiplet handles bitwise logical operations (AND, XOR) on 32-bit values. +# It decomposes field elements into their binary representation and performs bit-level +# operations efficiently using field arithmetic. +# +# Each bitwise operation spans exactly 8 rows, with limbs processed in little-endian order. +# +# STATUS: Partially implemented (missing bus constraints) +# +# REFERENCES: +# - Bitwise Chiplet: https://0xmiden.github.io/miden-vm/design/chiplets/bitwise.html +########################################################################################## + +mod bitwise + +use utils::*; + +########################################################################################## +# BITWISE OPERATION CONSTANTS (Periodic Columns) +########################################################################################## +# +# Periodic constants contain values needed to switch various constraints on or off. +# +periodic_columns { + k_first: [1, 0, 0, 0, 0, 0, 0, 0], + k_transition: [1, 1, 1, 1, 1, 1, 1, 0], +} + +########################################################################################## +# BITWISE CHIPLET TRANSITION CONSTRAINTS +########################################################################################## +# +# The bitwise chiplet processes 32-bit integers by decomposing them into individual +# bits, applying bitwise operations, and recomposing the results. This approach +# enables verification of bitwise logic within the field arithmetic constraint system. + +# Enforces the constraints on the bitwise chiplet given its columns. +# +# Max constraint degree: 4 +ev bitwise_chiplet_constraints([op_flag, a, b, a_limb[4], b_limb[4], zp, z]) { + enf bitwise_op_flag([op_flag]); + enf input_decomposition([a, b, a_limb, b_limb]); + enf output_aggregation([op_flag, a, b, a_limb, b_limb, zp, z]); +} + +########################################################################################## +# BUS CONSTRAINTS +########################################################################################## +# +# The bitwise chiplet communicates with the stack component using the chiplets bus. +# +# MESSAGE FORMAT: +# +# Each bitwise operation message contains: +# - Operation type (AND/XOR) +# - Input operands (a, b) as 32-bit values +# - Expected result (c) +# +########################################################################################## + +########################################################################################## +# HELPERS +########################################################################################## + +### Helper evaluators ############################################################################# + +# Enforces that the bitwise operation flag is valid. +# +# Max constraint degree: 2 +ev bitwise_op_flag([op_flag]) { + # Enforce operation flag is binary (0 for AND, 1 for XOR). + # Constraint degree: 2 + enf is_binary([op_flag]); + + # Enforce operation flag should stay the same throughout the 8-row cycle. + # Constraint degree: 2 + enf op_flag' = op_flag when k_transition; +} + + +# Enforces that the input to the bitwise chiplet is decomposed into limbs correctly. +# +# Max constraint degree: 2 +ev input_decomposition([a, b, a_limb[4], b_limb[4]]) { + # Enforce that the input is decomposed into valid bits. + # Constraints degree: 2 + enf is_binary([a]) for a in a_limb; + enf is_binary([b]) for b in b_limb; + + # Enforce that the value in the first row of column `a` of the current 8-row cycle should be + # the aggregation of the decomposed bit columns `a_limb` (little-endian). + let a_aggr = aggregate_limbs(a_limb); + # Constraint degree: 2 + enf a = a_aggr when k_first; + + # Enforce that the value in the first row of column `b` of the current 8-row cycle should be + # the aggregation of the decomposed bit columns `b_limb` (little-endian). + let b_aggr = aggregate_limbs(b_limb); + # Constraint degree: 2 + enf b = b_aggr when k_first; + + # Enforce that for all rows in an 8-row cycle, except for the last one, the values in a and b + # columns are increased by the values contained in the individual bit columns a_limb and + # b_limb. + # Constraints degree: 2 + enf a' = a * 16 + a_aggr when k_transition; + enf b' = b * 16 + b_aggr when k_transition; +} + + +# Enforces that the output of the bitwise operation is aggregated correctly from the decomposed +# limbs. +# +# Max constraint degree: 3 +ev output_aggregation([op_flag, a, b, a_limb[4], b_limb[4], zp, z]) { + # Enforce that in the first row, the aggregated output value of the previous row should be 0. + # Constraint degree: 2 + enf zp = 0 when k_first; + + # Enforce that for each row except the last, the aggregated output value must equal the + # previous aggregated output value in the next row. + # Constraint degree: 2 + enf zp' = z when k_transition; + + # Enforce that for all rows the value in the z column is computed by multiplying the previous + # output value (from the zp column in the current row) by 16 and then adding it to the bitwise + # operation applied to the row's set of bits of a_limb and b_limb. The entire constraint must + # also be multiplied by the operation selector flag to ensure it is only applied for the + # appropriate operation. The constraint for AND is enforced when op_flag = 0 and the constraint for + # XOR is enforced when op_flag = 1. Because the selectors for the AND and XOR operations are mutually + # exclusive, the constraints for different operations can be aggregated into the same result + # indices. + # Constraints degree: 3 + let a_and_b = compute_limb_and(a_limb, b_limb); + let a_xor_b = compute_limb_xor(a_limb, b_limb); + + enf z = zp * 16 + op_flag * (a_xor_b - a_and_b) + a_and_b; +} + +### Helper functions ############################################################################## + +# Returns value aggregated from limbs in little-endian order. +fn aggregate_limbs(limbs: felt[4]) -> felt { + return sum([2^i * limb for (i, limb) in (0..4, limbs)]); +} + +# Computes AND operation result for a 4-bit limb using fold/reduce approach. +fn compute_limb_and(a_limb: felt[4], b_limb: felt[4]) -> felt { + return sum([2^i * binary_and(a_bit, b_bit) for (i, a_bit, b_bit) in (0..4, a_limb, b_limb)]); +} + +# Computes XOR operation result for a 4-bit limb using fold/reduce approach. +fn compute_limb_xor(a_limb: felt[4], b_limb: felt[4]) -> felt { + return sum([2^i * binary_xor(a_bit, b_bit) for (i, a_bit, b_bit) in (0..4, a_limb, b_limb)]); +} diff --git a/constraints/chiplets.air b/constraints/chiplets.air new file mode 100644 index 000000000..cbb22b94f --- /dev/null +++ b/constraints/chiplets.air @@ -0,0 +1,161 @@ +########################################################################################## +# CHIPLETS CONSTRAINTS MODULE +########################################################################################## +# +# The Chiplets module contains specialized computation units that handle complex operations +# like cryptographic hashing, bitwise operations, and memory access. Each chiplet uses a +# hierarchical selector system to identify which operations are active. +# +# CHIPLETS COLUMN LAYOUT (20 columns): +# ┌─────────┬──────────────────────────────────────────────────────────────────────┐ +# │ Columns │ Purpose │ +# ├─────────┼──────────────────────────────────────────────────────────────────────┤ +# │ 0-4 │ s[5] - Hierarchical selector flags │ +# │ 5-19 │ Chiplet-specific data (hasher, bitwise ops, memory, ACE, kernel ROM) │ +# └─────────┴──────────────────────────────────────────────────────────────────────┘ +# +# STATUS: Not fully implemented +# +# REFERENCES: +# - Chiplets Design: https://0xmiden.github.io/miden-vm/design/chiplets/main.html +########################################################################################## + +mod chiplets + +use ace::ace_chiplet_constraints_all_rows; +use ace::ace_chiplet_constraints_first_row; + +use bitwise::bitwise_chiplet_constraints; + +use memory::memory_chiplet_constraints_all_rows; +use memory::memory_chiplet_constraints_all_rows_except_last; +use memory::flag_memory_active_not_last_row; +use memory::flag_next_row_first_row_memory; +use memory::memory_chiplet_constraints_first_row; + +use utils::*; + +########################################################################################## +# CHIPLETS CONSTRAINTS +########################################################################################## + +ev chiplets_constraints([chiplets[20]]) { + # Chiplets' flag constraints + let s0 = chiplets[0]; + let s1 = chiplets[1]; + let s2 = chiplets[2]; + let s3 = chiplets[3]; + let s4 = chiplets[4]; + enf chiplet_selectors([s0, s1, s2, s3, s4]); + + # MAIN CHIPLET CONSTRAINT: + + # The chiplet system uses a hierarchical binary selector scheme where each selector + # bit (s0, s1, s2, s3, s4) determines which chiplet is active at any given row. + # + # This hierarchy ensures exactly one chiplet is active per row and provides + # deterministic transitions between chiplets based on selector state changes. + let hash_active = hasher_chiplet_flag(s0); # Active when: !s0 + let bitwise_active = bitwise_chiplet_flag(s0, s1); # Active when: s0 * !s1 + let memory_active = memory_chiplet_flag(s0, s1, s2); # Active when: s0 * s1 * !s2 + let ace_active = ace_chiplet_flag(s0, s1, s2, s3); # Active when: s0 * s1 * s2 * !s3 + let ker_rom_active = ker_rom_chiplet_flag(s0, s1, s2, s3, s4); # Active when: s0 * s1 * s2 * s3 * !s4 + + # Apply chiplet-specific constraints based on hierarchical selector state. + enf match { + #case hash_active: hash_chiplet_constraints([chiplets[1..20]]), + case bitwise_active: bitwise_chiplet_constraints([chiplets[2..15]]), + case memory_active: memory_chiplet_constraints_all_rows([chiplets[3..18]]), + case ace_active: ace_chiplet_constraints_all_rows([chiplets[3..20]]), + #case ker_rom_active: ker_rom_chiplet_constraints([chiplets[5..20]]), + }; + + # CHIPLET CONSTRAINTS REQUIRING SPECIAL HANDLING + + ## MEMORY + + ### The memory chiplet requires special handling for both initialization and transition constraints + ### to ensure proper memory access sequencing and state transitions. + let flag_next_row_first_row_memory = flag_next_row_first_row_memory(s0, s1, s2); # Transitioning into memory chiplet + let flag_memory_active_not_last_row = flag_memory_active_not_last_row(s0, s1, s2); # Active in memory, not exiting + + ### MEMORY INITIALIZATION CONSTRAINTS: + ### Apply specialized initialization constraints when first entering the memory chiplet. + enf memory_chiplet_constraints_first_row([chiplets[3..18]]) when flag_next_row_first_row_memory; + + ### MEMORY TRANSITION CONSTRAINTS ON ALL BUT LAST ROW: + ### Apply standard transition constraints while active in memory chiplet, excluding the final row. + enf memory_chiplet_constraints_all_rows_except_last([chiplets[3..18]]) when flag_memory_active_not_last_row; + + ## ACE + + ### Apply ACE chiplet initialization constraints at the transition point + let next_row_first_ace = binary_and(memory_active, s2'); # Transitioning into ACE chiplet + enf ace_chiplet_constraints_first_row([chiplets[4..20]]) when next_row_first_ace; + +} + +########################################################################################## +# CHIPLET SELECTOR SYSTEM +########################################################################################## + +# Hierarchical chiplet selector constraints +# +# CONSTRAINT DEGREE: 2 (quadratic due to binary constraints) +# +ev chiplet_selectors([s[5]]) { + #################################################################################### + # BINARY CONSTRAINTS - Ensure all selectors are valid binary values + #################################################################################### + + enf is_binary([s[0]]); + + enf is_binary([s[1]]) when s[0]; + enf is_binary([s[2]]) when s[0] & s[1]; + enf is_binary([s[3]]) when s[0] & s[1] & s[2]; + enf is_binary([s[4]]) when s[0] & s[1] & s[2] & s[3]; + + #################################################################################### + # STABILITY CONSTRAINTS - Prevent deactivation (forbids 1→0 transitions) + #################################################################################### + + # Once a selector level becomes active (1), it must remain active + # This ensures chiplet operations maintain consistent state throughout execution + enf s[0]' = s[0] when s[0]; + enf s[1]' = s[1] when s[0] & s[1]; + enf s[2]' = s[2] when s[0] & s[1] & s[2]; + enf s[3]' = s[3] when s[0] & s[1] & s[2] & s[3]; + enf s[4]' = s[4] when s[0] & s[1] & s[2] & s[3] & s[4]; +} + +########################################################################################## +# CHIPLET ACTIVATION FUNCTIONS +########################################################################################## + +# These functions decode the hierarchical selector pattern to identify active chiplets. +# Each chiplet activates when its selector pattern matches and the next level is inactive. + +# Hasher chiplet: Active when root selector is inactive +fn hasher_chiplet_flag(s_0: felt) -> felt { + return !s_0; +} + +# Bitwise chiplet: Active when s0=1, s1=0 +fn bitwise_chiplet_flag(s_0: felt, s_1: felt) -> felt { + return s_0 * !s_1; +} + +# Memory chiplet: Active when s0=1, s1=1, s2=0 +fn memory_chiplet_flag(s_0: felt, s_1: felt, s_2: felt) -> felt { + return s_0 * s_1 * !s_2; +} + +# ACE chiplet: Active when s0=1, s1=1, s2=1, s3=0 +fn ace_chiplet_flag(s_0: felt, s_1: felt, s_2: felt, s_3: felt) -> felt { + return s_0 * s_1 * s_2 * !s_3; +} + +# Kernel ROM chiplet: Active when s0=1, s1=1, s2=1, s3=1, s4=0 +fn ker_rom_chiplet_flag(s_0: felt, s_1: felt, s_2: felt, s_3: felt, s_4: felt) -> felt { + return s_0 * s_1 * s_2 * s_3 * !s_4; +} diff --git a/constraints/decoder.air b/constraints/decoder.air new file mode 100644 index 000000000..6a20ea287 --- /dev/null +++ b/constraints/decoder.air @@ -0,0 +1,14 @@ +########################################################################################## +# DECODER CONSTRAINTS +########################################################################################## +# +# Miden VM program decoder is responsible for ensuring that a program with a given MAST +# root is executed by the VM. +# +# STATUS: Not implemented +# +# REFERENCES: +# - Decoder Design: https://0xmiden.github.io/miden-vm/design/decoder/main.html +########################################################################################## + +mod decoder diff --git a/constraints/hasher.air b/constraints/hasher.air new file mode 100644 index 000000000..599d4e25b --- /dev/null +++ b/constraints/hasher.air @@ -0,0 +1,256 @@ +########################################################################################## +# HASHER CONSTRAINTS MODULE +########################################################################################## +# +# The Hasher module is responsible for all hash-related operations, which includes: +# +# 1. A single permutation of Rescue Prime Optimized (RPO). +# 2. A simple 2-to-1 hash. +# 3. A linear hash of n field elements. +# 4. Merkle path verification. +# 5. Merkle root update. +# +# STATUS: Partially implemented (missing bus interactions) +# +# REFERENCES: +# - Hasher chiplet design: https://0xmiden.github.io/miden-vm/design/chiplets/hasher.html +########################################################################################## + +mod hasher + +use utils::*; +use rpo::enforce_rpo_round; + +########################################################################################## +# HASHER CHIPLET TRANSITION CONSTRAINTS +########################################################################################## + +# Enforces the constraints on the hash chiplet given its columns. +ev hash_chiplet([s[3], h[12], i]) { + # Selector columns constraints + enf selector_columns([s]); + + # Node index constraints + enf node_index([s, i]); + + # Hasher state constraints + enf hasher_state([s, h, i]); +} + +########################################################################################## +# HELPER EVALUATORS +########################################################################################## + +# Enforce selector columns constraints +ev selector_columns([s[3]]) { + # Enforce that selector columns are binary. + enf is_binary([selector]) for selector in s; + + # Compute relevant flags + let f_abp = get_f_abp(s); + let f_mpa = get_f_mpa(s); + let f_mva = get_f_mva(s); + let f_mua = get_f_mua(s); + let f_out = get_f_out(s); + let f_out_next = get_f_out_next(s); + + # Enforce that unless f_out = 1 or f_out' = 1, the values in columns s[1] and s[2] are copied + # over to the next row. + # This encodes the fact that we can change the op flags only at the end of a cycle in order + # to output a result, or at the start of a new cycle to initiate a new operation. + enf is_unchanged([s[1]]) when !f_out & !f_out_next; + enf is_unchanged([s[2]]) when !f_out & !f_out_next; + + # Flag that is true when the performed operation is one of the operations represented by flags + # f_abp, f_mpa, f_mva or f_mua + let f_comp = f_abp + f_mpa + f_mva + f_mua; + + # Enforce that if any of f_abp, f_mpa, f_mva, f_mua flags is set to 1, the next value of s[0] + # is 0. + # This basically enforces the exclusion of all op which initiate a new op (at the start of a new + # cycle). Note that f_comp is a flag that is set only on rows which are 1 less than a multiple of 8 + # and hence the following constrains the 0-th selector at the start of a new cycle. + enf s[0]' = 0 when f_comp; + + # Enforce that no invalid combinations of flags are allowed. + # This enforces that if s[0] is 0 then the either f_hout or f_sout is set.§§ + enf s[1] = 0 when binary_and(cycle_row_7, binary_not(s[0])); +} + +# Enforce node index constraints +ev node_index([s[3], i]) { + # Compute relevant flags + let f_mp = get_f_mp(s); + let f_mv = get_f_mv(s); + let f_mu = get_f_mu(s); + let f_mpa = get_f_mpa(s); + let f_mva = get_f_mva(s); + let f_mua = get_f_mua(s); + let f_out = get_f_out(s); + + # Flag indicating to enforce the constraint that b is binary only when a new node is absorbed into + # the hasher state (when the hash operation is either one of Merkle path verification or + # Merkle root update) + let f_an = f_mp + f_mv + f_mu + f_mpa + f_mva + f_mua; + + # b is the value of the bit which is discarded during shift by one bit to the right. + let b = i - 2 * i'; + + # Enforce that b is binary only when a new node is absorbed into the hasher state. + enf b^2 - b = 0 when f_an; + + # Enforce that when a computation is finished i = 0. + enf i = 0 when f_out; + + # Enforce that the value in i is copied over to the next row unless we are absorbing a new row + # or the computation is finished. + let not_absorbing_nor_comp_finished = 1 - (f_an + f_out); + enf is_unchanged([i]) when not_absorbing_nor_comp_finished; +} + +# Enforce hasher state constraints +ev hasher_state([s[3], h[12], i]) { + # Enforce the RPO permutation round constraints + enf enforce_rpo_round([h]) when !cycle_row_7; + + let f_mp = get_f_mp(s); + let f_mv = get_f_mv(s); + let f_mu = get_f_mu(s); + let f_abp = get_f_abp(s); + + # Flag that is true when the performed operation includes absorbing the next node during Merkle + # path computation. + let f_absorb_node = f_mp + f_mv + f_mu; + + # b is the value of the bit which is discarded during shift by one bit to the right. + let b = i - 2 * i'; + + # Enforce that when absorbing the next set of elements into the state during linear hash + # computation (i.e. f_abp = 1) the first 4 elements (the capacity portion) are carried over to + # the next row. + enf f_abp * (h' - h) = 0 for h in h; + + # TODO: Double check the following and fix both docs and VM if there is a typo + # + # Enforce that when absorbing the next node during Merkle path computation + # (i.e. f_mp + f_mv + f_mu = 1), the result of the previous hash (h[4], ..., h[7]) are copied + # over either to (h[4]', ..., h[7]') or to (h[8]', ..., h[11]') depending on the value of b. + # + # TODO: uncomment when computed indices are supported + # enf match { + # !b & f_absorb_node: is_unchanged(h[j + 4]) for j in 0..4, + # b & f_absorb_node: h[j + 8]' = h[j + 4] for j in 0..4 + # } +} + +########################################################################################## +# HELPER FUNCTIONS +########################################################################################## + +########################################################################################## +# INSTRUCTION FLAGS - DETAILED DESCRIPTIONS +########################################################################################## +# +# The hasher chiplet uses selector columns s[0], s[1], s[2] to encode different operations: +# +# INITIALIZATION FLAGS (on rows which are multiples of 8 - cycle_row_0): +# • f_bp: (1,0,0) - Begin Permutation +# Initiates: single permutation, 2-to-1 hash, or linear hash computation +# • f_mp: (1,0,1) - Merkle Path verification +# Starts standard Merkle path verification computation +# • f_mv: (1,1,0) - Merkle path Verification for "old" node +# Begins verification for old leaf value during Merkle root update +# • f_mu: (1,1,1) - Merkle path verification for "new" node +# Starts verification for new leaf value during Merkle root update +# +# ABSORPTION FLAGS (on rows 1 less than multiple of 8 - cycle_row_7): +# • f_abp: (1,0,0) - Absorb elements for linear hash (continuing computation) +# Absorbs next set of elements into hasher state during linear hash +# • f_mpa: (1,0,1) - Merkle Path Absorb during standard verification +# Absorbs next Merkle path node during standard verification +# • f_mva: (1,1,0) - Merkle path absorb for "old" node verification +# Absorbs next node during "old" leaf verification (Merkle root update) +# • f_mua: (1,1,1) - Merkle path absorb for "new" node verification +# Absorbs next node during "new" leaf verification (Merkle root update) +# +# OUTPUT FLAGS (on rows 1 less than multiple of 8 - cycle_row_7): +# • f_hout: (0,0,0) - Hash Output +# Returns the result of the currently running hash computation +# • f_sout: (0,0,1) - State Output +# Returns the entire 12-element hasher state +# • f_out: Combined flag (f_hout | f_sout) - any output operation +# +########################################################################################## + +# f_mp: Merkle Path verification flag (1,0,1) on cycle_row_0 +# Initiates standard Merkle path verification computation. +fn get_f_mp(s: felt[3]) -> felt { + return cycle_row_0 & s[0] & binary_not(s[1]) & s[2]; +} + +# f_mv: Merkle path Verification for "old" node flag (1,1,0) on cycle_row_0 +# Begins verification for old leaf value during Merkle root update computation. +fn get_f_mv(s: felt[3]) -> felt { + return cycle_row_0 & s[0] & s[1] & binary_not(s[2]); +} + +# f_mu: Merkle path verification for "new" node flag (1,1,1) on cycle_row_0 +# Starts verification for new leaf value during Merkle root update computation. +fn get_f_mu(s: felt[3]) -> felt { + return cycle_row_0 & s[0] & s[1] & s[2]; +} + +# f_abp: Absorb elements for linear hash flag (1,0,0) on cycle_row_7 +# Absorbs next set of elements into hasher state during linear hash computation. +fn get_f_abp(s: felt[3]) -> felt { + return cycle_row_7 & s[0] & binary_not(s[1]) & binary_not(s[2]); +} + +# f_mpa: Merkle Path Absorb flag (1,0,1) on cycle_row_7 +# Absorbs next Merkle path node during standard verification computation. +fn get_f_mpa(s: felt[3]) -> felt { + return cycle_row_7 & s[0] & binary_not(s[1]) & s[2]; +} + +# f_mva: Merkle path absorb for "old" node flag (1,1,0) on cycle_row_7 +# Absorbs next node during "old" leaf verification (Merkle root update computation). +fn get_f_mva(s: felt[3]) -> felt { + return cycle_row_7 & s[0] & s[1] & binary_not(s[2]); +} + +# f_mua: Merkle path absorb for "new" node flag (1,1,1) on cycle_row_7 +# Absorbs next node during "new" leaf verification (Merkle root update computation). +fn get_f_mua(s: felt[3]) -> felt { + return cycle_row_7 & s[0] & s[1] & s[2]; +} + +# We can define two flags: +# 1. Flag f_hout = cycle_row_7 & binary_not(s[0]) & binary_not(s[1]) & binary_not(s[2]), +# which is set to 1 when selector flags are (0,0,0) on rows which are 1 less than a multiple +# of 8. This flag is for the instruction that returns the resulting digest of the currently +# running computation. +# 2. Flag f_sout = cycle_row_7 & binary_not(s[0]) & binary_not(s[1]) & s[2], which is set to 1 +# when selector flags are (0,0,1) on rows which are 1 less than a multiple of 8. This flag is +# for the instruction that returns the whole hasher state. +# +# Flag f_out is set to 1 when either f_hout = 1 or f_sout = 1 in the current row. +fn get_f_out(s: felt[3]) -> felt { + return cycle_row_7 & binary_not(s[0]) & binary_not(s[1]); +} + +# Flag f_out_next is set to 1 when either f_hout = 1 or f_sout = 1 in the next row. +fn get_f_out_next(s: felt[3]) -> felt { + return cycle_row_6 & binary_not(s[0]') & binary_not(s[1]'); +} + +########################################################################################## +# PERIODIC COLUMNS +########################################################################################## +# +# There are 3 periodic columns used to help select the instruction executed at a given row +# +periodic_columns { + cycle_row_0: [1, 0, 0, 0, 0, 0, 0, 0], + cycle_row_6: [0, 0, 0, 0, 0, 0, 1, 0], + cycle_row_7: [0, 0, 0, 0, 0, 0, 0, 1], +} diff --git a/constraints/memory.air b/constraints/memory.air new file mode 100644 index 000000000..efb9579d4 --- /dev/null +++ b/constraints/memory.air @@ -0,0 +1,237 @@ +########################################################################################## +# MEMORY CONSTRAINTS MODULE +########################################################################################## +# +# The Memory chiplet provides linear read-write random access memory for the Miden VM. +# Memory is element-addressable with addresses in range [0, 2^32), supporting both individual +# element access and optimized word-aligned batch operations (4 elements per word). +# +# MEMORY TABLE LAYOUT: +# ┌─────────────────┬────────────────────────────────────────────────────────────────┐ +# │ Column │ Purpose │ +# ├─────────────────┼────────────────────────────────────────────────────────────────┤ +# │ is_read │ Read/write selector: 1=read, 0=write │ +# │ is_word_access │ Element/word access selector: 0=element, 1=word │ +# │ ctx │ Context ID for execution context separation │ +# │ addr │ Memory address (word-aligned for word access) │ +# │ idx0/1 │ Element index within word [0,3] - binary decomposition │ +# │ clk │ Clock cycle when operation occurred │ +# │ v0-v3 │ Four field elements stored in memory word │ +# │ d0/d1 │ Delta tracking columns for monotonicity verification │ +# │ d_inv │ Inverse delta column for consecutive transitions │ +# │ f_scw │ Same context/word flag for sequential access optimization │ +# └─────────────────┴────────────────────────────────────────────────────────────────┘ +# +# STATUS: Partially implemented (missing bus integration) +# +# REFERENCES: +# - Memory Design: https://0xmiden.github.io/miden-vm/design/chiplets/memory.html +########################################################################################## + +mod memory + +use utils::*; + +########################################################################################## +# MEMORY CHIPLET CONSTRAINTS +########################################################################################## + +# Enforces proper memory initialization when entering memory chiplet +ev memory_chiplet_constraints_first_row([memory[15]]) { + let is_read = memory[0]; # Read(1)/Write(0) selector + let is_word_access = memory[1]; # Element(0)/Word(1) access selector + let ctx = memory[2]; # Context ID + let addr = memory[3]; # Memory address + let idx0 = memory[4]; # Element index bit 0 + let idx1 = memory[5]; # Element index bit 1 + let v0 = memory[7]; # Memory value 0 + let v1 = memory[8]; # Memory value 1 + let v2 = memory[9]; # Memory value 2 + let v3 = memory[10]; # Memory value 3 + + let is_constrained_value = compute_element_access_flags(idx0', idx1', is_read', is_word_access'); + + # Enforce that when v'[i] is not written to, then v'[i] must be 0. + enf v0' = 0 when is_constrained_value[0]; + enf v1' = 0 when is_constrained_value[1]; + enf v2' = 0 when is_constrained_value[2]; + enf v3' = 0 when is_constrained_value[3]; +} + +# Enforces constraints that apply to every row in the memory chiplet +# These are basic validity constraints for selectors and indices +ev memory_chiplet_constraints_all_rows([memory[15]]) { + let is_read = memory[0]; # Read(1)/Write(0) selector + let is_word_access = memory[1]; # Element(0)/Word(1) access selector + let idx0 = memory[4]; # Element index bit 0 + let idx1 = memory[5]; # Element index bit 1 + + # Read/write selector must be binary + enf is_binary([is_read]); + + # Element/word access selector must be binary + enf is_binary([is_word_access]); + + # Index bit 0 must be binary + enf is_binary([idx0]); + + # Index bit 1 must be binary + enf is_binary([idx1]); +} + +# Enforces memory state transition constraints for all rows except the final row +# Includes monotonicity, value consistency, and proper read/write semantics +ev memory_chiplet_constraints_all_rows_except_last([memory[15]]) { + let is_read = memory[0]; # Read(1)/Write(0) selector + let is_word_access = memory[1]; # Element(0)/Word(1) access selector + let ctx = memory[2]; # Context ID + let addr = memory[3]; # Memory address + let idx0 = memory[4]; # Element index bit 0 + let idx1 = memory[5]; # Element index bit 1 + let clk = memory[6]; # Clock cycle + let v0 = memory[7]; # Memory value 0 + let v1 = memory[8]; # Memory value 1 + let v2 = memory[9]; # Memory value 2 + let v3 = memory[10]; # Memory value 3 + let d0 = memory[11]; # Context delta + let d1 = memory[12]; # Address delta + let d_inv = memory[13]; # Delta inverse + let f_scw = memory[14]; # Same context/word flag + + # Delta inverse constraints + enf enforce_d_inv([ctx, addr, clk, d0, d1, d_inv]); + + # Context/address/clock delta constraints + enf enforce_delta([ctx, addr, clk, d0, d1, d_inv]); + + # Same context/word flag constraints + enf enforce_flag_same_context_and_word([ctx, addr, d_inv, f_scw]); + + # Same context/word/addr/clock access constraints + enf enforce_same_context_word_addr_and_clock([is_read, clk, f_scw, d_inv]); + + # Memory value constraints + enf enforce_values_consistency([is_read, is_word_access, idx0, idx1, v0, v1, v2, v3, f_scw]); +} + +########################################################################################## +# HELPER EVALUATORS +########################################################################################## + +# Constrains the delta inverse column +ev enforce_d_inv([ctx, addr, clk, d0, d1, d_inv]) { + let ctx_delta = ctx' - ctx; + let addr_delta = addr' - addr; + let is_ctx_changed = ctx_delta * d_inv'; + let is_addr_changed = addr_delta * d_inv'; + + # is_ctx_changed is binary + enf binary_constraint(is_ctx_changed) = 0; + + # When context changes, is_ctx_changed must be 1 + enf ctx_delta = 0 when !is_ctx_changed; + + # When is_ctx_changed is 0 then is_addr_changed is binary + enf binary_constraint(is_addr_changed) = 0 when !is_ctx_changed; + + # When is_ctx_changed and is_addr_changed are both 0, then address must not change + enf addr_delta = 0 when binary_not(is_ctx_changed) * binary_not(is_addr_changed); +} + +# Enforces monotonicity constraints for context, address, and clock transitions +ev enforce_delta([ctx, addr, clk, d0, d1, d_inv]) { + let ctx_delta = ctx' - ctx; + let addr_delta = addr' - addr; + let clk_delta = clk' - clk; + let delta_next = d1' * 2^16 + d0'; + let is_ctx_changed = ctx_delta * d_inv'; + let is_addr_changed = addr_delta * d_inv'; + + enf is_ctx_changed * ctx_delta + binary_not(is_ctx_changed) * (is_addr_changed * addr_delta + binary_not(is_addr_changed) * clk_delta) = delta_next; +} + +# Enforces correct f_scw flag computation +ev enforce_flag_same_context_and_word([ctx, addr, d_inv, f_scw]) { + let ctx_delta = ctx' - ctx; + let addr_delta = addr' - addr; + let is_ctx_changed = ctx_delta * d_inv'; + let is_addr_changed = addr_delta * d_inv'; + + enf f_scw' = binary_not(is_ctx_changed) * binary_not(is_addr_changed); +} + +# Enforces that accesses to same context, word address, and clock must be reads +ev enforce_same_context_word_addr_and_clock([is_read, clk, f_scw, d_inv]) { + let clk_delta = clk' - clk; + let clk_no_change = binary_not(clk_delta * d_inv'); + + enf f_scw' * clk_no_change * binary_not(is_read) * binary_not(is_read') = 0; +} + +# Enforces memory value consistency, and proper read/write semantics +ev enforce_values_consistency([is_read, is_word_access, idx0, idx1, v0, v1, v2, v3, f_scw]) { + let is_constrained_value = compute_element_access_flags(idx0', idx1', is_read', is_word_access'); + + # Non-first row constraints: if v[i]' is not written to and, + # - (f_scw' = 1) then its value needs to be copied over from the previous row, + # - (f_scw' = 0) then its value needs to be set to 0. + enf f_scw' * (v0' - v0) + binary_not(f_scw') * v0' = 0 when is_constrained_value[0]; + enf f_scw' * (v1' - v1) + binary_not(f_scw') * v1' = 0 when is_constrained_value[1]; + enf f_scw' * (v2' - v2) + binary_not(f_scw') * v2' = 0 when is_constrained_value[2]; + enf f_scw' * (v3' - v3) + binary_not(f_scw') * v3' = 0 when is_constrained_value[3]; +} + +########################################################################################## +# HELPER FUNCTIONS +########################################################################################## + +# Memory chiplet active flag +fn flag_all_rows(s0: felt, s1: felt, s2: felt) -> felt { + return s0 * s1 * binary_not(s2); +} + +# Memory chiplet active flag for next row +fn flag_all_rows_next(s0: felt, s1: felt, s2: felt) -> felt { + return s0' * s1' * binary_not(s2'); +} + +# Memory chiplet active in current row and row is not the last one of the memory chiplet +fn flag_memory_active_not_last_row(s0: felt, s1: felt, s2: felt) -> felt { + return s0 * s1 * binary_not(s2'); +} + +# First row of memory chiplet (transitioning from bitwise to memory) +fn flag_next_row_first_row_memory(s0: felt, s1: felt, s2: felt) -> felt { + return (1 - s1) * flag_all_rows_next(s0, s1, s2); +} + +# Computes constraint flag: 1 if value needs to be constrained, 0 otherwise +fn compute_constrained_values(is_accessed_i: felt, is_read_next: felt, is_word_access_next: felt) -> felt { + let z_i = binary_not(is_word_access_next) * binary_not(is_accessed_i); + return is_read_next + binary_not(is_read_next) * z_i; +} + +# Computes which memory elements need to be constrained based on access pattern +# Returns array of 4 flags indicating whether each element (v0-v3) needs constraining +# +# is_constrainted_value_i is set to 1 when `v'[i]` is not written to, and 0 otherwise. +# +# In other words, is_constrainted_value_i is set to 1 when `v'[i]` needs to be constrained (to either 0 or `v[i]`). +# +# Note that `is_constrainted_value_i` only uses values in the "next" row. This is because it must be used to +# constrain the first row of the memory chiplet, where that row sits in the "next" position of +# the frame, and the "current" row belongs to the previous chiplet (and hence the "current" row +# must not be accessed). +# +# As a result, `is_constrainted_value_i` does not include the constraint of being in the memory chiplet, or in the +# same context and word - these must be enforced separately. +fn compute_element_access_flags(idx0_next: felt, idx1_next: felt, is_read_next: felt, is_word_access_next: felt) -> felt[4] { + # Element selection flags: is_accessed[i] = 1 when element index (2*idx1 + idx0) equals i + let is_accessed_0 = binary_not(idx1_next) * binary_not(idx0_next); + let is_accessed_1 = binary_not(idx1_next) * idx0_next; + let is_accessed_2 = idx1_next * binary_not(idx0_next); + let is_accessed_3 = idx1_next * idx0_next; + + let is_accessed = [is_accessed_0, is_accessed_1, is_accessed_2, is_accessed_3]; + return [compute_constrained_values(is_accessed_i, is_read_next, is_word_access_next) for is_accessed_i in is_accessed]; +} diff --git a/constraints/miden-vm-old/bitwise.air b/constraints/miden-vm-old/bitwise.air new file mode 100644 index 000000000..8c186367f --- /dev/null +++ b/constraints/miden-vm-old/bitwise.air @@ -0,0 +1,114 @@ +mod BitwiseAir + +### Constants and periodic columns ################################################################ + +periodic_columns { + k0: [1, 0, 0, 0, 0, 0, 0, 0] + k1: [1, 1, 1, 1, 1, 1, 1, 0] +} + + +### Helper functions ############################################################################## + +# Returns value aggregated from limbs. +fn aggregate(limb: vector[4]) -> scalar: + return sum([2^i * a for (i, a) in (0..4, limb)]) + + +### Helper evaluators ############################################################################# + +# Enforces that column must be binary. +# +# Constraint degree: 2 +ev is_binary([a]) { + enf a^2 = a +} + + +# Enforces that the bitwise selector is valid. +# +# Max constraint degree: 2 +ev bitwise_selector([s]) { + # Enforce that selector must be binary. + # Constraint degree: 2 + enf is_binary([s]) + + # Enforce that selector should stay the same throughout the cycle. + # Constraint degree: 2 + enf s' = s when k1 +} + + +# Enforces that the input to the bitwise chiplet is decomposed into limbs correctly. +# +# Max constraint degree: 2 +ev input_decomposition([a, b, a_limb[4], b_limb[4]]) { + # Enforce that the input is decomposed into valid bits. + # Constraints degree: 2 + enf is_binary([a]) for a in a_limb + enf is_binary([b]) for b in b_limb + + # Enforce that the value in the first row of column `a` of the current 8-row cycle should be + # the aggregation of the decomposed bit columns `a_limb`. + let a_aggr = aggregate(a_limb) + # Constraint degree: 2 + enf a = a_aggr when k0 + + # Enforce that the value in the first row of column `b` of the current 8-row cycle should be + # the aggregation of the decomposed bit columns `b_limb`. + let b_aggr = aggregate(b_limb) + # Constraint degree: 2 + enf b = b_aggr when k0 + + # Enforce that for all rows in an 8-row cycle, except for the last one, the values in a and b + # columns are increased by the values contained in the individual bit columns a_limb and + # b_limb. + # Constraints degree: 2 + enf a' = a * 16 + a_aggr when k1 + enf b' = b * 16 + b_aggr when k1 +} + + +# Enforces that the output of the bitwise operation is aggregated correctly from the decomposed +# limbs. +# +# Max constraint degree: 3 +ev output_aggregation([s, a, b, a_limb[4], b_limb[4], zp, z]) { + # Enforce that in the first row, the aggregated output value of the previous row should be 0. + # Constraint degree: 2 + enf zp = 0 when k0 + + # Enforce that for each row except the last, the aggregated output value must equal the + # previous aggregated output value in the next row. + # Constraint degree: 2 + enf zp' = z when k1 + + # Enforce that for all rows the value in the z column is computed by multiplying the previous + # output value (from the zp column in the current row) by 16 and then adding it to the bitwise + # operation applied to the row's set of bits of a_limb and b_limb. The entire constraint must + # also be multiplied by the operation selector flag to ensure it is only applied for the + # appropriate operation. The constraint for AND is enforced when s = 0 and the constraint for + # XOR is enforced when s = 1. Because the selectors for the AND and XOR operations are mutually + # exclusive, the constraints for different operations can be aggregated into the same result + # indices. + # Constraints degree: 3 + let a_and_b = sum([2^i * a * b for (i, a, b) in (0..4, a_limb, b_limb)]) + let a_xor_b = sum([2^i * (a + b - 2 * a * b) for (i, a, b) in (0..4, a_limb, b_limb)]) + match enf: + z = zp * 16 + a_xor_b when s + z = zp * 16 + a_and_b when !s +} + + +### Bitwise Chiplet Air Constraints ############################################################### + +# Enforces the constraints on the bitwise chiplet, given the columns of the bitwise execution +# trace. +# +# Max constraint degree: 4 +ev bitwise_chiplet([s, a, b, a_limb[4], b_limb[4], zp, z]) { + enf bitwise_selector([s]) + enf input_decomposition([a, b, a_limb, b_limb]) + enf output_aggregation([s, a, b, a_limb, b_limb, zp, z]) + # Bus constraint is implemented in a separate file +} diff --git a/constraints/miden-vm-old/chiplets.air b/constraints/miden-vm-old/chiplets.air new file mode 100644 index 000000000..2cc397ed2 --- /dev/null +++ b/constraints/miden-vm-old/chiplets.air @@ -0,0 +1,38 @@ +mod ChipletsConstraintsAir + +use bitwise::bitwise_chiplet +use hash::hash_chiplet +use memory::memory_chiplet + +### Helper evaluators ############################################################################# + +# Enforces that the provided columns must be binary. +ev is_binary([a]) { + enf a^2 = a +} + +# Enforces that the chiplet selector columns are set correctly. +ev chiplet_selectors([s[3]]) { + # Enforce that selectors are binary. + enf is_binary([s[0]]) + enf is_binary([s[1]]) when s[0] + enf is_binary([s[2]]) when s[0] & s[1] + + # Enforce that the chiplets are stacked correctly by restricting selector values so they can + # only change from 0 to 1. + enf s[0]' = s[0] when s[0] + enf s[1]' = s[1] when s[0] & s[1] + enf s[2]' = s[2] when s[0] & s[1] & s[2] +} + +### Chiplets Constraints ########################################################################## + +# Enforce the constraints on the hash, bitwise or memory chiplet, given the columns of the chiplet +# module trace. +ev chiplets([s[3], chiplet_columns[15]]) { + enf chiplet_selectors([s]) + match enf: + hash_chiplet([s[1], s[2], chiplet_columns]) when !s[0] + bitwise_chiplet([s[2], chiplet_columns]) when s[0] & !s[1] + memory_chiplet([chiplet_columns]) when s[0] & s[1] & !s[2]' +} \ No newline at end of file diff --git a/constraints/miden-vm-old/decoder.air b/constraints/miden-vm-old/decoder.air new file mode 100644 index 000000000..cdab70682 --- /dev/null +++ b/constraints/miden-vm-old/decoder.air @@ -0,0 +1,654 @@ +mod DecoderAir + +### Constants and periodic columns ################################################################ + +const HASHER_LINEAR_HASH = 3 +const HASHER_RETURN_HASH = 1 + +periodic_columns { + cycle_row_0: [1, 0, 0, 0, 0, 0, 0, 0] + cycle_row_7: [0, 0, 0, 0, 0, 0, 0, 1] +} + +### Helper functions ############################################################################## + +# Returns the f_join operation flag which is set when JOIN control operation is executed. +# +# Flag degree: 6 +fn get_f_join(b: vector[7]) -> scalar: + return b[6] & !b[5] & b[4] & b[3] & !b[2] & b[1] + + +# Returns the f_split operation flag which is set when SPLIT control operation is executed. +# +# Flag degree: 6 +fn get_f_split(b: vector[7]) -> scalar: + return b[6] & !b[5] & b[4] & b[3] & b[2] & !b[1] + + +# Returns the f_loop operation flag which is set when LOOP control operation is executed. +# +# Flag degree: 6 +fn get_f_loop(b: vector[7]) -> scalar: + return b[6] & !b[5] & b[4] & b[3] & b[2] & b[1] + + +# Returns the f_repeat operation flag which is set when REPEAT operation is executed. +# +# Flag degree: 4 +fn get_f_repeat(b: vector[7], extra: scalar) -> scalar: + return extra & b[4] & !b[3] & b[2] + + +# Returns the f_span operation flag which is set when SPAN operation is executed. +# +# Flag degree: 6 +fn get_f_span(b: vector[7]) -> scalar: + return b[6] & !b[5] & b[4] & b[3] & !b[2] & !b[1] + + +# Returns the f_respan operation flag which is set when RESPAN operation is executed. +# +# Flag degree: 4 +fn get_f_respan(b: vector[7], extra: scalar) -> scalar: + return extra & b[4] & b[3] & !b[2] + + +# Returns the f_call operation flag which is set when CALL control operation is executed. +# +# Flag degree: 4 +fn get_f_call(b: vector[7], extra: scalar) -> scalar: + return extra & !b[4] & b[3] & b[2] + + +# Returns the f_syscall operation flag which is set when SYSCALL control operation is executed. +# +# Flag degree: 4 +fn get_f_syscall(b: vector[7], extra: scalar) -> scalar: + return extra & !b[4] & b[3] & !b[2] + + +# Returns the f_end operation flag which is set when END operation is executed. +# +# Flag degree: 4 +fn get_f_end(b: vector[7], extra: scalar) -> scalar: + return extra & b[4] & !b[3] & !b[2] + + +# Returns the f_halt operation flag which is set when HALT operation is executed. +# +# Flag degree: 4 +fn get_f_halt(b: vector[7], extra: scalar) -> scalar: + return extra & b[4] & b[3] & b[2] + + +# Returns the f_push operation flag which is set when PUSH operation is executed. +# +# Flag degree: 4 +fn get_f_push(b: vector[7], extra: scalar) -> scalar: + return extra & !b[4] & !b[3] & b[2] + + +# Returns the f_ctrl flag which is set when any one of the control flow operations (JOIN, SPLIT, +# LOOP, REPEAT, SPAN, RESPAN, CALL, SYSCALL, END, HALT) is being executed. +# +# Flag degree: 4 +fn get_f_ctrl(b: vector[7], extra: scalar) -> scalar: + # flag for SPAN, JOIN, SPLIT, LOOP + let f_sjsl = b[6] & !b[5] & b[4] & b[3] + + # flag for END, REPEAT, RESPAN, HALT + let f_errh = b[6] & b[5] & b[4] + + return f_sjsl + f_errh + get_f_call(b, extra) + get_f_syscall(b, extra) + + +# Returns f_ctrli flag which is set to 1 when a control flow operation that signifies the +# initialization of a control block (JOIN, SPLIT, LOOP, CALL, SYSCALL) is being executed on the VM. +# +# Flag degree: 6 +fn get_f_ctrli(b: vector[7], extra: scalar) -> scalar: + return get_f_join(b) + get_f_split(b) + get_f_loop(b) + get_f_call(b, extra) + get_f_syscall(b, extra) + + +# Returns transition label, composed of the operation label and the periodic columns that uniquely +# identify each transition function. +fn get_transition_label(op_label: scalar) -> scalar: + return op_label + 2^4 * cycle_row_7 + 2^5 * cycle_row_0 + + +# Returns f_g8 flag which is set to 1 if there are 8 operation groups in the batch. +fn get_f_g8(op_batch_flags: vector[3]) -> scalar: + return op_batch_flags[0] + + +# Returns f_g4 flag which is set to 1 if there are 4 operation groups in the batch. +fn get_f_g4(op_batch_flags: vector[3]) -> scalar: + return !op_batch_flags[0] & op_batch_flags[1] & op_batch_flags[2] + + +# Returns f_g2 flag which is set to 1 if there are 2 operation groups in the batch. +fn get_f_g2(op_batch_flags: vector[3]) -> scalar: + return !op_batch_flags[0] & !op_batch_flags[1] & op_batch_flags[2] + + +# Returns f_g1 flag which is set to 1 if there are 1 operation groups in the batch. +fn get_f_g1(op_batch_flags: vector[3]) -> scalar: + return !op_batch_flags[0] & op_batch_flags[1] & !op_batch_flags[2] + + +### Helper evaluators ############################################################################# + +# Enforces that column must be binary. +# Constraint degree: 2 +ev is_binary([a]) { + enf a^2 = a +} + + +# Enforces that value in column is copied over to the next row. +# Constraint degree: 1 +ev is_unchanged([column]) { + enf column' = column +} + + +# Enforces decoder general constraints. +# +# Max constraint degree: 9 +ev general([addr, op_bits[7], hasher[8], in_span, s0, extra]) { + # Get flags required for the general constraints + let f_repeat = get_f_repeat(op_bits, extra) + let f_end = get_f_end(op_bits, extra) + let f_halt = get_f_halt(op_bits, extra) + + # Enforce that `extra` column is set to 1 when op_bits[6] = 1 and op_bits[5] = 1 + # Constraint degree: 3 + enf extra = 1 when op_bits[6] & op_bits[5] + + # Enforce that when SPLIT or LOOP operation is executed, the top of the operand stack must + # contain a binary value. + # Constraint degree: 8 + enf is_binary([s0]) when get_f_split(op_bits) | get_f_loop(op_bits) + + # Enforce that When REPEAT operation is executed, the value at the top of the operand stack + # must be 1. + # Constraint degree: 5 + enf s0 = 1 when f_repeat + + # Enforce that when REPEAT operation is executed, the value in hasher[4] column (the + # is_loop_body flag), must be set to 1. This ensures that REPEAT operation can be executed only + # inside a loop. + # Constraint degree: 5 + enf hasher[4] = 1 when f_repeat + + # Enforce that when RESPAN operation is executed, we need to make sure that the block ID is + # incremented by 8. + # Constraint degree: 5 + enf addr' = addr + 8 when f_respan(op_bits, extra) + + # Enforce that when END operation is executed and we are exiting a loop block (i.e., is_loop, + # value which is stored in hasher[5], is 1), the value at the top of the operand stack must be + # 0. + # Constraint degree: 6 + enf s0 = 0 when f_end & hasher[5] + + # Enforce that when END operation is executed and the next operation is REPEAT, values in + # hasher[0], ..., hasher[4] (the hash of the current block and the is_loop_body flag) must be + # copied to the next row. + # Constraint degree: 9 + enf is_unchanged([hasher[i]]) for i in 0..5 when f_end & get_f_repeat(op_bits', extra') + + # Enforce that a HALT instruction can be followed only by another HALT instruction. + # Constraint degree: 8 + enf f_halt * !get_f_halt(op_bits', extra') = 0 + + # Enforce that when a HALT operation is executed, block address column (addr) must be 0. + # Constraint degree: 5 + enf addr = 0 when f_halt + + # Enforce that values in op_bits columns must be binary. + # Constraint degree: 2 + enf is_binary([b]) for b in op_bits + + # Enforce that when the value in in_span column is set to 1, control flow operations cannot be + # executed on the VM, but when in_span flag is 0, only control flow operations can be executed + # on the VM. + # Constraint degree: 4 + enf 1 - in_span - get_f_ctrl(op_bits, extra) = 0 +} + + +# Enforces the constraint for computing block hashes. +# +# Max constraint degree: 8 +ev block_hash_computation([addr, op_bits[7], hasher[8], extra], [p[4]]) { + # Get flags required for the block hash computation constraint + let f_ctrli = get_f_ctrli(op_bits, extra) + let f_span = get_f_span(op_bits) + let f_respan = get_f_respan(op_bits, extra) + let f_end = get_f_end(op_bits, extra) + + # Label specifying that we are starting a new hash computation. + let m_bp = get_transition_label(HASHER_LINEAR_HASH) + + # Label specifying that we are absorbing the next sequence of 8 elements into an ongoing hash + # computation. + let m_abp = get_transition_label(HASHER_LINEAR_HASH) + + # Label specifying that we are reading the result of a hash computation. + let m_hout = get_transition_label(HASHER_RETURN_HASH) + + # `alpha` is the global random values array. + let rate_sum = sum([$alpha[i + 8] * hasher[i] for i in 0..8]) + let digest_sum = sum([$alpha[i + 8] * hasher[i] for i in 0..4]) + + # Variable for initiating a hasher with address addr' and absorbing 8 elements from the hasher + # state (hasher[0], ..., hasher[7]) into it. + let h_init = $alpha[0] + $alpha[1] * m_bp + $alpha[2] * addr' + rate_sum + + # Variable for the absorption. + let h_abp = $alpha[0] + $alpha[1] * m_abp + $alpha[2] * addr' + rate_sum + + # Variable for the result. + let h_res = $alpha[0] + $alpha[1] * m_hout + $alpha[2] * (addr + 7) + digest_sum + + # Opcode value of the opcode being executed on the virtual machine. + let opcode_value = sum([op_bits[i] * 2^i for i in 0..7]) + + # When a control block initializer operation (JOIN, SPLIT, LOOP, CALL, SYSCALL) is executed, a + # new hasher is initialized and the contents of hasher[0], ..., hasher[7] are absorbed into the + # hasher. + # + # Value degree: 7 + let u_ctrli = f_ctrli * (h_init + $alpha[5] * opcode_value) + + # When SPAN operation is executed, a new hasher is initialized and contents of + # hasher[0], ..., hasher[7] are absorbed into the hasher. + # + # Value degree: 7 + let u_span = f_span * h_init + + # When RESPAN operation is executed, contents of hasher[0], ..., hasher[7] (which contain the + # new operation batch) are absorbed into the hasher. + # + # Value degree: 5 + let u_respan = f_respan * h_abp + + # When END operation is executed, the hash result is copied into registers + # hasher[0], ..., hasher[3]. + # + # Value degree: 5 + let u_end = f_end * h_res + + # Enforce the block hash computation constraint. We need to add 1 and subtract the sum of the + # relevant operation flags to ensure that when none of the flags is set to 1, the above + # constraint reduces to p[0]' = p[0]. + # Constraint degree: 8 + enf p[0]' * (u_ctrli + u_span + u_respan + u_end + 1 - + (f_ctrli + f_span + f_respan + f_end)) = p[0] +} + + +# Enforces the constraint for updating the block stack table. +# +# Max constraint degree: 8 +ev block_stack_table([addr, op_bits[7], hasher[8], s0, extra], [p[4]]) { + # Get flags required for the block stack table constraint + let f_join = get_f_join(op_bits) + let f_split = get_f_split(op_bits) + let f_loop = get_f_loop(op_bits) + let f_span = get_f_span(op_bits) + let f_respan = get_f_respan(op_bits, extra) + let f_end = get_f_end(op_bits, extra) + + # When JOIN operation is executed, row (addr', addr, 0) is added to the block stack table. + # Value degree: 7 + let v_join = f_join * ($alpha[0] + $alpha[1] * addr' + $alpha[2] * addr) + + # When SPLIT operation is executed, row (addr', addr, 0) added to the block stack table. + # Value degree: 7 + let v_split = f_split * ($alpha[0] + $alpha[1] * addr' + $alpha[2] * addr) + + # When LOOP operation is executed, row (addr', addr, 1) is added to the block stack table if + # the value at the top of the operand stack is 1, and row (addr', addr, 0) is added to the + # block stack table if the value at the top of the operand stack is 0. + # Value degree: 7 + let v_loop = f_loop * ($alpha[0] + $alpha[1] * addr' + $alpha[2] * addr + $alpha[3] * s0) + + # When SPAN operation is executed, row (addr', addr, 0) is added to the block stack table. + # Value degree: 7 + let v_span = f_span * ($alpha[0] + $alpha[1] * addr' + $alpha[2] * addr) + + # When RESPAN operation is executed, row (addr, hasher[1]', 0) is removed from the block stack + # table, and row (addr', hasher[1]', 0) is added to the table. The prover sets the value of + # register hasher[1] at the next row to the ID of the parent block. + # Value degree: 5 + let u_respan = f_respan * ($alpha[0] + $alpha[1] * addr + $alpha[2] * hasher[1]') + # Value degree: 5 + let v_respan = f_respan * ($alpha[0] + $alpha[1] * addr' + $alpha[2] * hasher[1]') + + # When END operation is executed, row (addr, addr', hasher[5]) is removed from the block span + # table. Register hasher[5] contains the is_loop flag. + # Value degree: 5 + let u_end = f_end * + ($alpha[0] + $alpha[1] * addr + $alpha[2] * addr' + $alpha[3] * hasher[5]) + + # Enforce the block stack table constraint. We need to add 1 and subtract the sum of the + # relevant operation flags from each side to ensure that when none of the flags is set to 1, + # the above constraint reduces to p[1]' = p[1] + # Constraint degree: 8 + enf p[1]' * (u_end + u_respan + 1 - (f_end + f_respan)) = + p[1] * (v_join + v_split + v_loop + v_span + v_respan + 1 - + (f_join + f_split + f_loop + f_span + f_respan)) +} + + +# Enforces the constraint for updating the block hash table. +# +# Max constraint degree: 9 +ev block_hash_table([addr, op_bits[7], hasher[8], s0, extra], [p[4]]) { + # Get flags required for the block hash table constraint + let f_join = get_f_join(op_bits) + let f_split = get_f_split(op_bits) + let f_loop = get_f_loop(op_bits) + let f_end = get_f_end(op_bits, extra) + let f_repeat = get_f_repeat(op_bits, extra) + + # Values representing left and right children of a block. + # Value degree: 1 + let ch1 = $alpha[0] + $alpha[1] * addr' + sum([$alpha[i + 2] * hasher[i] for i in 0..4]) + # Value degree: 1 + let ch2 = $alpha[0] + $alpha[1] * addr' + sum([$alpha[i + 2] * hasher[i + 4] for i in 0..4]) + + # Value representing the result of hash computation. + # Value degree: 1 + let bh = $alpha[0] + $alpha[1] * addr + sum([$alpha[i + 2] * hasher[i]]) + $alpha[7] * hasher[4] + + # When JOIN operation is executed, hashes of both child nodes are added to the block hash + # table. We add alpha[6] term to the first child value to differentiate it from the second + # child (i.e., this sets is_first_child to 1). + # Value degree: 8 + let v_join = f_join * (ch1 + $alpha[6]) * ch2 + + # When SPLIT operation is executed and the top of the stack is 1, hash of the true branch is + # added to the block hash table, but when the top of the stack is 0, hash of the false branch + # is added to the block hash table. + # Value degree: 8 + let v_split = f_split * (s0 * ch1 + (1 - s0) * ch2) + + # When LOOP operation is executed and the top of the stack is 1, hash of loop body is added to + # the block hash table. We add alpha[7] term to indicate that the child is a body of a loop. + # The below also means that if the top of the stack is 0, nothing is added to the block hash + # table as the expression evaluates to 0. + # Value degree: 8 + let v_loop = f_loop * s0 * (ch1 + $alpha[7]) + + # When REPEAT operation is executed, hash of loop body is added to the block hash table. We add + # alpha[7] term to indicate that the child is a body of a loop. + # Value degree: 5 + let v_repeat = f_repeat * (ch1 + $alpha[7]) + + # When END operation is executed, hash of the completed block is removed from the block hash + # table. However, we also need to differentiate between removing the first and the second child + # of a join block. We do this by looking at the next operation. Specifically, if the next + # operation is neither END nor REPEAT we know that another block is about to be executed, and + # thus, we have just finished executing the first child of a join block. Thus, if the next + # operation is neither END nor REPEAT we need to set the term for alpha[6] coefficient to 1 as + # shown below. + # Value degree: 8 + let u_end = f_end * + (bh + $alpha[6] * (1 - (get_f_end(op_bits', extra') + get_f_repeat(op_bits', extra')))) + + # Enforce the block hash table constraint. We need to add 1 and subtract the sum of the + # relevant operation flags from each side to ensure that when none of the flags is set to 1, + # the above constraint reduces to p[2]' = p[2] + # Constraint degree: 9 + enf p[2]' * (u_end + 1 - f_end) = + p[2] * (v_join + v_split + v_loop + v_repeat + 1 - (f_join + f_split + f_loop + f_repeat)) + + # TODO: add boundary constraints to the p[2] column: + # 1. The first value in the column represents a row for the entire program. Specifically, the + # row tuple would be (0, program_hash, 0, 0). This row should be removed from the table + # when the last END operation is executed. + # 2. The last value in the column is 1 - i.e., the block hash table is empty. +} + + +# Enforce that values in in_span column, which is used to identify rows which execute non-control +# flow operations, are set correctly. +# +# Constraint degree: 7 +ev in_span_column([op_bits[7], in_span, extra]) { + # Get flags required for the inspan column constraint + let f_span = get_f_span(op_bits) + let f_respan = get_f_respan(op_bits, extra) + let f_respan_next = get_f_respan(op_bits', extra') + let f_end_next = get_f_end(op_bits', extra') + + # Enforce that when executing SPAN or RESPAN operation, the next value in in_span column must + # be set to 1. + # Constraint degree: 7 + enf in_span' = 1 when f_span | f_respan + + # Enforce that when the next operation is END or RESPAN, the next value in in_span column must + # be set to 0. + # Constraint degree: 5 + enf in_span' = 0 when f_end_next | f_respan_next + + # Enforce that in all other cases, the value in in_span column must be copied over to the next + # row. + # Constraint degree: 7 + enf is_unchanged(in_span) when !f_span & !f_respan & !f_end_next & !f_respan_next + + # TODO: add boundary constraint for in_span column: in_span.first = 0 +} + +# Enforce that when we are inside a span block, values in the block address column (denoted as addr) +# must remain the same. +# +# Constraint degree: 2 +ev block_address([addr, in_span]) { + enf is_unchanged(addr) when in_span +} + +# Enforce that values in group_count column, which is used to keep track of the number of operation +# groups which remains to be executed in a span block, are set correctly. +# +# Max constraint degree: 7 +ev group_count([op_bits[7], hasher[8], in_span, group_count, extra]) { + # Get value of the f_push flag + let f_push = get_f_push(op_bits, extra) + + # Enforce that inside a span block, group count can either stay the same or decrease by one. + # Constraint degree: 3 + enf (group_count' - group_count) * (group_count' - group_count - 1) = 0 when in_span + + # Enforce that when group count is decremented inside a span block, either hasher[0] must be 0 + # (we consumed all operations in a group) or we must be executing PUSH operation. + # Constraint degree: 7 + enf (1 - f_push) * hasher[0] = 0 when in_span & (group_count' - group_count) + + # Enforce that when executing a SPAN, a RESPAN, or a PUSH operation, group count must be + # decremented by 1. + # Constraint degree: 7 + enf group_count' - group_count = 1 when f_span(op_bits) | get_f_respan(op_bits, extra) | f_push + + # Enforce that if the next operation is either an END or a RESPAN, group count must remain the + # same. + # Constraint degree: 5 + enf is_unchanged(group_count) when get_f_end(op_bits', extra') | get_f_respan(op_bits', extra') + + # Enforce that when an END operation is executed, group count must be 0. + # Constraint degree: 5 + enf group_count = 0 when get_f_end(op_bits, extra) +} + +# Enforce that register hasher[0], which is used to keep track of operations to be executed in the +# current operation group, is set correctly. +# +# Max constraint degree: 7 +ev op_group_decoding([op_bits[7], in_span, group_count, extra]) { + # opcode value for the next row. + let op_next = sum([op_bits[i]' * 2^i for i in 0..7]) + + # Flag which is set to 1 when the group count within a span block does not change. We multiply + # it by sp' to make sure the flag is 0 when we are about to end decoding of an operation batch. + let f_sgc = in_span * in_span' * (1 - group_count' + group_count) + + # Enforce that when a SPAN, a RESPAN, or a PUSH operation is executed or when the group count + # does not change, the value in hasher[0] should be decremented by the value of the opcode in + # the next row. + # Constraint degree: 7 + enf hasher[0] - hasher[0]' * 2^7 - op_next = 0 + when f_span(op_bits) | get_f_respan(op_bits, extra) | get_f_push(op_bits, extra) | f_sgc + + # Enforce that when we are in a span block and the next operation is END or RESPAN, the current + # value in hasher[0] column must be 0. + # Constraint degree: 6 + enf (get_f_end(op_bits', extra') + get_f_respan(op_bits', extra')) * hasher[0] = 0 when in_span +} + +# Enforce that the values in op_index column, which tracks index of an operation within its +# operation group, are set correctly. +# +# Max constraint degree: 9 +ev op_index([op_bits[7], in_span, group_count, op_index, extra]) { + # ng is set to 1 when we are about to start executing a new operation group (i.e., group count + # is decremented but we did not execute a PUSH operation). + let ng = group_count' - group_count - get_f_push(op_bits, extra) + + # Enforce that when executing SPAN or RESPAN operations the next value of op_index must be set + # to 0. + # Constraint degree: 7 + enf op_index' = 0 when f_span(op_bits) | get_f_respan(op_bits, extra) + + # Enforce that when starting a new operation group inside a span block, the next value of + # op_index must be set to 0. + # Constraint degree: 6 + enf op_index' = 0 when in_span & ng + + # Enforce that when inside a span block but not starting a new operation group, op_index must + # be incremented by 1. + # Constraint degree: 7 + enf op_index' - op_index = 1 when in_span & in_span' & !ng + + # Enforce that values of op_index must be in the range [0, 8]. + # Constraint degree: 9 + enf prod([op_index - i for i in 0..9]) = 0 +} + +# Enforce that values in operation batch flag columns (denoted op_batch_flags[]), which are used to +# specify how many operation groups are present in an operation batch, are set correctly. +# +# Max constraint degree: 6 +ev op_batch_flags([op_bits[7], hasher[8], op_batch_flags[3], extra]) { + # Get flags required for the op batch flag constraints + let f_g1 = get_f_g1(op_batch_flags) + let f_g2 = get_f_g2(op_batch_flags) + let f_g4 = get_f_g4(op_batch_flags) + let f_g8 = get_f_g8(op_batch_flags) + + # Enforce that all batch flags are binary. + # Constraint degree: 2 + enf is_binary(bc) for bc in op_batch_flags + + # Enforce that when SPAN or RESPAN operations is executed, one of the batch flags must be set + # to 1. + # Constraint degree: 6 + enf f_g1 + f_g2 + f_g4 + f_g8 = 1 when f_span(op_bits) | get_f_respan(op_bits, extra) + + # Enforce that when we have at most 4 groups in a batch, registers h[4], ..., h[7] should be + # set to 0's. + # Constraint degree: 4 + enf hasher[i] = 0 for i in 4..8 when f_g1 | f_g2 | f_g4 + + # Enforce that When we have at most 2 groups in a batch, registers h[2] and h[3] should also be + # set to 0's. + # Constraint degree: 4 + enf hasher[i] = 0 for i in 2..4 when f_g1 | f_g2 + + # Enforce that when we have at most 1 group in a batch, register h[1] should also be set to 0. + # Constraint degree: 4 + enf hasher[1] = 0 when f_g1 +} + +# Enforce that all operation groups in a given batch are consumed before a new batch is started +# (i.e., via a RESPAN operation) or the execution of a span block is complete (i.e., via an END +# operation). +# +# Max constraint degree: 9 +ev op_group_table([addr, op_bits[7], hasher[8], in_span, group_count, op_index, op_batch_flags[3], s0, extra], [p[4]]) { + # Get value of the f_push flag + let f_push = get_f_push(op_bits, extra) + + # opcode value for the next row. + let op_next = sum([op_bits[i]' * 2^i for i in 0..7]) + + # Row value for group in hasher[1] to be added to the op group table when a SPAN or a RESPAN + # operation is executed. + # Value degree: 1 + let v_1 = $alpha[0] + $alpha[1] * addr' + $alpha[2] * (group_count - 1) + $alpha[3] * hasher[1] + + # Value degree: 1 + let prod_v_3 = prod([$alpha[0] + + $alpha[1] * addr' + + $alpha[2] * (group_count - i) + + $alpha[3] * hasher[i] for i in 1..4]) + + # Value degree: 1 + let prod_v_7 = prod([$alpha[0] + + $alpha[1] * addr' + + $alpha[2] * (group_count - i) + + $alpha[3] * hasher[i] for i in 1..8]) + + # The value of the row to be removed from the op group table. + # Value degree: 5 + let u = $alpha[0] + $alpha[1] * addr + $alpha[2] * group_count + $alpha[3] * + ((hasher[0]' * 2^7 + op_next) * (1 - f_push) + s0' * f_push) = 0 + + # A flag which is set to 1 when a group needs to be removed from the op group table. + let f_dg = in_span * (group_count' - group_count) + + # Enforce the constraint for updating op group table. The constraint specifies that when SPAN + # or RESPAN operations are executed, we add between 1 and 7 groups to the op group table, and + # when group count is decremented inside a span block, we remove a group from the op group + # table. + # Constraint degree: 9 + enf p[3]' * (f_dg * u + 1 - f_dg) = p[3] * (get_f_g2(op_batch_flags) * v_1 + + get_f_g4(op_batch_flags) * prod_v_3 + + get_f_g8(op_batch_flags) * prod_v_7 - 1 + + (f_span(op_bits) + get_f_respan(op_bits, extra))) +} + +# Enforce proper decoding of span blocks. +# +# Max constraint degree: 9 +ev span_block([addr, op_bits[7], hasher[8], in_span, group_count, op_index, op_batch_flags[3], s0, extra], [p[4]]) { + enf in_span_column([op_bits, in_span, extra]) + enf block_address([addr, in_span]) + enf group_count([op_bits, hasher, in_span, group_count, extra]) + enf op_group_decoding([op_bits, in_span, group_count, extra]) + enf op_index([op_bits, in_span, group_count, op_index, extra]) + enf op_batch_flags([op_bits, hasher, op_batch_flags, extra]) + enf op_group_table([addr, op_bits, hasher, in_span, group_count, op_index, op_batch_flags, s0, extra], [p]) +} + +### Decoder Air Constraints ####################################################################### + +# Enforces the constraints on the decoder. The register `s0` denotes the value at the top of the +# stack. `extra` denotes the register for degree reduction during flag computations, and p[4] +# columns denote multiset check columns. +# +# Max constraint degree: 9 +ev decoder_constraints([addr, op_bits[7], hasher[8], in_span, group_count, op_index, op_batch_flags[3], s0, extra], [p[4]]) { + enf general([addr, op_bits[7], hasher[8], in_span, s0, extra]) + + enf block_hash_computation([addr, op_bits[7], hasher[8], extra], [p[4]]) + + enf block_stack_table([addr, op_bits[7], hasher[8], s0, extra], [p[4]]) + + enf block_hash_table([addr, op_bits, hasher, s0, extra], [p[4]]) + + enf span_block([addr, op_bits[7], hasher[8], in_span, group_count, op_index, op_batch_flags[3], s0, extra], [p[4]]) +} \ No newline at end of file diff --git a/constraints/miden-vm-old/hash.air b/constraints/miden-vm-old/hash.air new file mode 100644 index 000000000..55165cd19 --- /dev/null +++ b/constraints/miden-vm-old/hash.air @@ -0,0 +1,208 @@ +mod HashChipletAir + +### Constants and periodic columns ################################################################ + +periodic_columns { + cycle_row_0: [1, 0, 0, 0, 0, 0, 0, 0] + cycle_row_6: [0, 0, 0, 0, 0, 0, 1, 0] + cycle_row_7: [0, 0, 0, 0, 0, 0, 0, 1] +} + +### Helper functions ############################################################################## + +# Returns binary negation of the value. +fn binary_not(value: scalar) -> scalar: + return 1 - value + + +# Set to 1 when selector flags are (1,0,1) on rows which are multiples of 8. This is flag of +# the instruction that initiates Merkle path verification computation. +fn get_f_mp(s: vector[3]) -> scalar: + return cycle_row_0 & s[0] & binary_not(s[1]) & s[2] + + +# Set to 1 when selector flags are (1,1,0) on rows which are multiples of 8. This is flag of +# the instruction that initiates Merkle path verification for the "old" node value during +# Merkle root update computation. +fn get_f_mv(s: vector[3]) -> scalar: + return cycle_row_0 & s[0] & s[1] & binary_not(s[2]) + + +# Set to 1 when selector flags are (1,1,1) on rows which are multiples of 8. This is flag of +# the instruction that initiates Merkle path verification for the "new" node value during +# Merkle root update computation. +fn get_f_mu(s: vector[3]) -> scalar: + return cycle_row_0 & s[0] & s[1] & s[2] + + +# Set to 1 when selector flags are (1,0,0) on rows which are 1 less than a multiple of 8. This +# is flag of the instruction that absorbs a new set of elements into the hasher state when +# computing a linear hash of many elements. +fn get_f_abp(s: vector[3]) -> scalar: + return cycle_row_7 & s[0] & binary_not(s[1]) & binary_not(s[2]) + + +# Set to 1 when selector flags are (1,0,1) on rows which are 1 less than a multiple of 8. This +# is flag of the instruction that absorbs the next Merkle path node into the hasher state +# during Merkle path verification computation. +fn get_f_mpa(s: vector[3]) -> scalar: + return cycle_row_7 & s[0] & binary_not(s[1]) & s[2] + + +# Set to 1 when selector flags are (1,1,0) on rows which are 1 less than a multiple of 8. This +# is flag of the instruction that absorbs the next Merkle path node into the hasher state +# during Merkle path verification for the "old" node value during Merkle root update +# computation. +fn get_f_mva(s: vector[3]) -> scalar: + return cycle_row_7 & s[0] & s[1] & binary_not(s[2]) + + +# Set to 1 when selector flags are (1,1,1) on rows which are 1 less than a multiple of 8. This +# is flag of the instruction that absorbs the next Merkle path node into the hasher state +# during Merkle path verification for the "new" node value during Merkle root update +# computation. +fn get_f_mua(s: vector[3]) -> scalar: + return cycle_row_7 & s[0] & s[1] & s[2] + + +# We can define two flags: +# 1. Flag f_hout = cycle_row_7 & binary_not(s[0]) & binary_not(s[1]) & binary_not(s[2]), +# which is set to 1 when selector flags are (0,0,0) on rows which are 1 less than a multiple +# of 8. This is flag of the instruction that returns the result of the currently running +# computation. +# 2. Flag f_sout = cycle_row_7 & binary_not(s[0]) & binary_not(s[1]) & s[2], which is set to 1 +# when selector flags are (0,0,1) on rows which are 1 less than a multiple of 8. This is flag +# of the instruction that returns the whole hasher state. +# +# Flag f_out is set to 1 when either f_hout = 1 or f_sout = 1 in the current row. +fn get_f_out(s: vector[3]) -> scalar: + return cycle_row_7 & binary_not(s[0]) & binary_not(s[1]) + + +# Flag f_out_next is set to 1 when either f_hout = 1 or f_sout = 1 in the next row. +fn get_f_out_next(s: vector[3]) -> scalar: + return cycle_row_6 & binary_not(s[0]') & binary_not(s[1]') + + +### Helper evaluators ############################################################################# + +# Enforces that column must be binary. +ev is_binary(main: [a]) { + enf a^2 = a +} + + +# Enforces that value in column is copied over to the next row. +ev is_unchanged(main: [column]) { + ev column' = column +} + + +# Enforce selector columns constraints +ev selector_columns(main: [s[3]]) { + let f_out = get_f_out(s) + let f_out_next = get_f_out_next(s) + let f_abp = get_f_abp(s) + let f_mpa = get_f_mpa(s) + let f_mva = get_f_mva(s) + let f_mua = get_f_mua(s) + + # Flag that is true when the performed operation is one of the represented by flags f_abp, + # f_mpa, f_mva or f_mua + let f_comp = f_abp + f_mpa + f_mva + f_mua + + # Enforce that selector columns are binary. + enf is_binary([selector]) for selector in s + + # Enforce that unless f_out = 1 or f_out' = 1, the values in columns s[1] and s[2] are copied + # over to the nex row. + enf is_unchanged([s[1]]) when !f_out & !f_out_next + enf is_unchanged([s[2]]) when !f_out & !f_out_next + + # Enforce that if any of f_abp, f_mpa, f_mva, f_mua flags is set to 1, the next value of s[0] + # is 0. + enf s[0]' * f_comp = 0 + + # Enforce that no invalid combinations of flags are allowed. + enf cycle_row_7 * binary_not(s[0]) * s[1] = 0 +} + +# Enforce node index constraints +ev node_index(main: [s[3], i]) { + let f_out = get_f_out(s) + let f_mp = get_f_mp(s) + let f_mv = get_f_mv(s) + let f_mu = get_f_mu(s) + let f_mpa = get_f_mpa(s) + let f_mva = get_f_mva(s) + let f_mua = get_f_mua(s) + + # b is the value of the bit which is discarded during shift by one bit to the right. + let b = i - 2 * i' + + # Flag that allows to enforce constraint that b is binary only when a new node is absorbed into + # the hasher state (when the hash operation is one of Merkle path verification operations or + # next Merkle path node absorption operations) + let f_an = f_mp + f_mv + f_mu + f_mpa + f_mva + f_mua + + # Enforce that b is binary only when a new node is absorbed into the hasher state. + enf f_an * (b^2 - b) = 0 + + # Enforce that when a computation is finished i = 0. + enf f_out * i = 0 + + # Enforce that the value in i is copied over to the next row unless we are absorbing a new row + # or the computation is finished. + let absorbing_or_comp_finished = 1 - f_an - f_out + enf is_unchanged([i]) when absorbing_or_comp_finished +} + +# Enforce hasher state constraints +ev hasher_state(main: [s[3], h[12], i]) { + let f_mp = get_f_mp(s) + let f_mv = get_f_mv(s) + let f_mu = get_f_mu(s) + let f_abp = get_f_abp(s) + + # Flag that is true when the performed operation includes absorbing the next node during Merkle + # path computation. + let f_absorb_node = f_mp + f_mv + f_mu + + # b is the value of the bit which is discarded during shift by one bit to the right. + let b = i - 2 * i' + + # Enforce that when absorbing the next set of elements into the state during linear hash + # computation (i.e. f_abp = 1) the first 4 elements (the capacity portion) are carried over to + # the next row. + enf f_abp * (h[j]' - h[j]) = 0 for j in 0..4 + + # Enforce that when absorbing the next node during Merkle path computation + # (i.e. f_mp + f_mv + f_mu = 1), the result of the previous hash (h[4], ..., h[7]) are copied + # over either to (h[4]', ..., h[7]') or to (h[8]', ..., h[11]') depending on the value of b. + match enf: + is_unchanged(h[j + 4]) for j in 0..4 when !b & f_absorb_node + h[j + 8]' = h[j + 4] for j in 0..4 when b & f_absorb_node +} + +### Hash Chiplet Air Constraints ################################################################## + +# Enforces the constraints on the hash chiplet, given the columns of the hash execution trace. +ev hash_chiplet(main: [s[3], r, h[12], i]) { + ## Row address constraint ## + # TODO: Apply row address constraints: + # 1. Boundary constraint `enf r.first = 1` + # 2. Transition constraint. It requires chiplets module's selector flag s0. + + ## Selector columns constraints ## + enf selector_columns([s]) + + ## Node index constraints ## + enf node_index([s, i]) + + ## Hasher state constraints ## + # TODO: apply RPO constraints to the hasher state + enf hasher_state([s, h, i]) + + # Multiset check constraints + # TODO: Apply multiset check constraints +} \ No newline at end of file diff --git a/constraints/miden-vm-old/memory.air b/constraints/miden-vm-old/memory.air new file mode 100644 index 000000000..17da938a5 --- /dev/null +++ b/constraints/miden-vm-old/memory.air @@ -0,0 +1,120 @@ +mod MemoryChipletAir + +### Helper functions ############################################################################## + +# Returns the n0 flag which is set to 1 when context changes and 0 otherwise. +fn get_n0(ctx: scalar, ctx_next: scalar, t_next: scalar) -> scalar: + return (ctx_next - ctx) * t_next + + +# Returns the n1 flag. If context remains the same, n1 = 1 when address changes and 0 otherwise. +fn get_n1(addr: scalar, addr_next: scalar, t_next: scalar) -> scalar: + return (addr_next - addr) * t_next + + +### Helper evaluators ############################################################################# + +# Enforces that column must be binary. +# Constraint degree: 2 +ev is_binary([a]) { + enf a^2 = a +} + + +# Enforces that value in column is copied over to the next row. +# Constraint degree: 1 +ev is_unchanged([column]) { + enf column' = column +} + + +# Enforces that the provided columns must be zero. +ev is_zero([column]) { + enf column = 0 +} + + +# Enforces that created flags have valid values during the program execution. +ev flags_validity([ctx, addr, t]) { + # n0 = 1 when context changes and 0 otherwise. + let n0 = get_n0(ctx, ctx', t') + + # if context remains the same, n1 = 1 when address changes and 0 otherwise. + let n1 = get_n1(addr, addr', t') + + # Enforce that n0 must be binary. + enf n0^2 = n0 + + # Enforce that when context changes, n0 = 1 (or when n0 = 0, context remains the same). + enf ctx' = ctx when !n0 + + # Enforce that n1 must be binary. An additional condition ensures that the check of n1 + # occurs only if the context does not change (n0 = 0). + enf n1^2 = n1 when !n0 + + # Enforce that if context remains the same, n1 = 1 when address changes and 0 otherwise. + enf addr' = addr when !n0 & !n1 +} + +# Enforces that selectors take the correct values under certain conditions. +ev enforce_selectors([s[2], ctx, addr, t]) { + # Enforce that values in the selectior columns must be binary. + # s[0] is set to 0 for write operations and to 1 for read operations. + enf is_binary([selector]) for selector in s + + # n0 = 1 when context changes and 0 otherwise. + let n0 = get_n0(ctx, ctx', t') + + # if context remains the same, n1 = 1 when address changes and 0 otherwise. + let n1 = get_n1(addr, addr', t') + + # Enforce that s[1]' = 1 when the operation is a read and `ctx` and `addr` columns are both + # unchanged. + enf s[1]' = 1 when !n0 & !n1 & s[0]' + + # Enforce that s[1]' = 0 when either the context changed, the address changed, or the operation + # is a write. + enf s[1]' = 0 when n0 | n1 | !s[0]' +} + +# Enforces that the delta between two consecutive contexts, addresses, or clock cycles is updated +# and decomposed into the `d1` and `d0` columns correctly. +ev enforce_delta([ctx, addr, clk, d[2], t]) { + # n0 = 1 when context changes and 0 otherwise. + let n0 = get_n0(ctx, ctx', t') + + # if context remains the same, n1 = 1 when address changes and 0 otherwise. + let n1 = get_n1(addr, addr', t') + + let d_next_agg = 2^16 * d[1]' + d[0]' + + # Enforce that values of context (`ctx`), address (`addr`), and clock cycle (`clk`) grow + # monotonically + match enf: + d_next_agg = ctx' - ctx when n0 + d_next_agg = addr' - addr when !n0 & n1 + d_next_agg = clk' - clk - 1 when !n0 & !n1 +} + +# Enforces that memory is initialized to zero when it is read before being written and that when +# existing memory values are read they remain unchanged. +ev enforce_values([s[2], v[4]]) { + # Enforce that values at a given memory address are always initialized to 0. + enf is_zero([v_i]) for v_i in v when s[0] & !s[1] + + # Enforce that for the same context/address combination, the v columns of the current row are + # equal to the corresponding v columns of the next row + enf is_unchanged([v_i]) for v_i in v when s[1] +} + +### Memory Chiplet Air Constraints ################################################################ + +# Enforces the constraints on the memory chiplet, given the columns of the memory execution trace. +ev memory_chiplet([s[2], ctx, addr, clk, v[4], d[2], t]) { + enf flags_validity([ctx, addr, t]) + enf enforce_selectors([s, ctx, addr, t]) + enf enforce_delta([ctx, addr, clk, d, t]) + # TODO: perform range checks for values in columns d[0] and d[1] + enf enforce_values([s, v]) + # Bus constraint is implemented in a separate file +} \ No newline at end of file diff --git a/constraints/miden-vm-old/range_checker.air b/constraints/miden-vm-old/range_checker.air new file mode 100644 index 000000000..f3581de8c --- /dev/null +++ b/constraints/miden-vm-old/range_checker.air @@ -0,0 +1,62 @@ +mod RangeCheckerAir + +### Helper functions ############################################################################## + +# Returns array of mutually exclusive multiplicity flags. +# f[0] set to 1 when we don't include the value into the running product. +# f[1] set to 1 when we include the value into the running product. +# f[2] set to 1 when we include two copies of the value into the running product. +# f[3] set to 1 when we include four copies of the value into the running product. +fn get_multiplicity_flags(s0: scalar, s1: scalar) -> vector[4]: + return [!s0 & !s1, s0 & !s1, !s0 & s1, s0 & s1] + + +### Helper evaluators ############################################################################# + +# Enforces that column must be binary. +ev is_binary([v]) { + enf v^2 = v +} + +# Enforces correct transition from 8-bit to 16-bit section of the table. +ev transition_8_to_16_bit([t, v]) { + # Ensure that values in column t can flip from 0 to 1 only once + enf t * !t' = 0 + + # Ensure that when column t flips, column v must equal 255 + enf v = 255 when t' & !t + + # Ensure that when column t flips, v' must be reset to 0 + enf v' = 0 when t' & !t +} + +# The virtual table enforces an 8-bit range check for each row transition in the 16-bit section of +# the range checker, which enforces its internal correctness. +ev virtual_table([t, s0, s1, v], [p0]) { + let val = $alpha[0] + v + let f = get_multiplicity_flags(s0, s1) + + # z represents how a row in the execution trace is reduced to a single value. + let z = val^4 * f[3] + val^2 * f[2] + val * f[1] + f[0] + enf p0' * (($alpha[0] + v' - v) * t - t + 1) = p0 * (z - z * t + t) + + # TODO: add boundary constraints p0.first = 1 and p0.last = 1 +} + +### Range checker Air Constraints ################################################################# + +ev range_checker([t, s0, s1, v], [p0]) { + # Check selector flags are binary. + let selectors = [t, s0, s1] + enf is_binary([s]) for s in selectors + + # Constrain the row transitions in the 8-bit section of the table so that as we move from one + # row to the next the value either stays the same or increases by 1. + enf (v' - v) * (v' - v - 1) = 0 when !t' + + # Constrain the transition from 8-bit to 16-bit section of the table. + enf transition_8_to_16_bit([t, v]) + + # Constrain the row transitions in the 16-bit section of the table. + enf virtual_table([t, s0, s1, v], [p0]) +} \ No newline at end of file diff --git a/constraints/miden_vm.air b/constraints/miden_vm.air new file mode 100644 index 000000000..c9a324409 --- /dev/null +++ b/constraints/miden_vm.air @@ -0,0 +1,184 @@ +########################################################################################## +# MIDEN VM - ALGEBRAIC INTERMEDIATE REPRESENTATION (AIR) CONSTRAINTS +########################################################################################## +# +# The Miden Virtual Machine is a STARK-based zero-knowledge virtual machine designed for +# efficient execution and proving of arbitrary computations. This AIR specification defines +# the arithmetic constraints that govern the VM's execution. +# +# STATUS: Not fully implemented +# +# REFERENCES: +# - Miden VM implementation: https://0xmiden.github.io/miden-vm/ +# - Miden VM documentation : https://0xmiden.github.io/miden-vm/intro/main.html +########################################################################################## + +def MidenVM + +use chiplets::chiplets_constraints; +use bitwise::*; +use hasher::*; +use range_checker::*; +use system::*; +use utils::*; + +########################################################################################## +# EXECUTION TRACE LAYOUT +########################################################################################## +# +# The execution trace captures the state of the VM at each cycle. The main trace consists +# of 80 columns organized into logical segments. +# +########################################################################################## + +trace_columns { + main: [system[8], decoder[24], stack[19], range_checker[2], chiplets[20], padding[7]], +} + +########################################################################################## +# PUBLIC INPUTS +########################################################################################## +# +# Public inputs define the interface between the prover and verifier, establishing +# the computation's inputs and expected outputs that must be verified. +# +# INPUT STRUCTURE: +# ┌───────────────┬─────────┬───────────────────────────────────────────────────┐ +# │ Field │ Size │ Description │ +# ├───────────────┼─────────┼───────────────────────────────────────────────────┤ +# │ stack_inputs │ 16 │ Initial operand stack state (top 16 elements) │ +# │ stack_outputs │ 16 │ Final operand stack state (top 16 elements) │ +# │program_digest │ 4 │ MAST root hash identifying the executed program │ +# │kernel_digests │ [[5]] │ Hashes of kernel procedures (dynamic array) │ +# └───────────────┴─────────┴───────────────────────────────────────────────────┘ +# +########################################################################################## + +public_inputs { + stack_inputs: [16], # Initial operand stack state (16 field elements) + stack_outputs: [16], # Final operand stack state (16 field elements) + program_digest: [4], # MAST root hash (4 field elements) + kernel_digests: [[5]], # Kernel procedure hashes plus an op label +} + +########################################################################################## +# BUS ARCHITECTURE +########################################################################################## +# +# Buses enable efficient communication between VM components using cryptographic +# protocols like multiset checks and LogUp. They ensure data integrity and consistency +# across different execution units. +# +########################################################################################## + +buses { + # Decoder buses + multiset bus_0_decoder_p1, + multiset bus_1_decoder_p2, + multiset bus_2_decoder_p3, + + # Stack overflow table + multiset bus_3_stack_p1, + + # Range checker bus + logup bus_4_range_checker, + + # Chiplet buses + multiset bus_5_v_table, # Chiplets virtual table + multiset bus_6_chiplets_bus, # Main chiplets communication bus + + # Wiring bus + logup bus_7_wiring_bus, # ACE (Algebraic Circuit Elements) wiring +} + +########################################################################################## +# BOUNDARY CONSTRAINTS +########################################################################################## +# +# Boundary constraints establish the initial and final states of the VM execution. +# They ensure proper initialization and define the expected state at program completion. +# +########################################################################################## + +boundary_constraints { + #################################################################################### + # MAIN TRACE BOUNDARIES + #################################################################################### + + # System clock must start at 0 (VM begins execution at cycle 0) + enf system[0].first = 0; + + #################################################################################### + # AUXILIARY COLUMN BOUNDARIES + #################################################################################### + + # Decoder buses + enf bus_0_decoder_p1.first = unconstrained; + enf bus_0_decoder_p1.last = unconstrained; + enf bus_1_decoder_p2.first = unconstrained; + enf bus_1_decoder_p2.last = unconstrained; + enf bus_2_decoder_p3.first = unconstrained; + enf bus_2_decoder_p3.last = unconstrained; + + # Stack overflow table + enf bus_3_stack_p1.first = unconstrained; + enf bus_3_stack_p1.last = unconstrained; + + # Range checker bus - LogUp protocol requires null initialization/finalization + enf bus_4_range_checker.first = null; + enf bus_4_range_checker.last = null; + + # Chiplets virtual table + enf bus_5_v_table.first = unconstrained; + enf bus_5_v_table.last = unconstrained; + + # Chiplets communication bus + # Initial state contains kernel procedure digests against which the program was compiled + enf bus_6_chiplets_bus.first = kernel_digests; + enf bus_6_chiplets_bus.last = unconstrained; + + # ACE (Algebraic Circuit Elements) wiring bus + enf bus_7_wiring_bus.first = unconstrained; + enf bus_7_wiring_bus.last = unconstrained; +} + +########################################################################################## +# INTEGRITY CONSTRAINTS +########################################################################################## +# +# Integrity constraints define the state transition rules that govern VM execution. +# These constraints ensure that each step of program execution is valid according +# to the Miden VM instruction set architecture. +# +########################################################################################## + +integrity_constraints { + #################################################################################### + # MAIN TRACE CONSTRAINTS + #################################################################################### + + # System state transitions + enf system_transition([system[0..8]]); + + # TODO: Decoder constraints - Program execution and MAST verification + # enf decoder_constraints([decoder]); + + # TODO: Stack constraints - Operand stack operations and overflow handling + # enf stack_constraints([stack]); + + # TODO: Chiplet constraints - Hash, bitwise, ACE, memory operations, and kernel ROM + enf chiplets_constraints([chiplets[0..20]]); + + #################################################################################### + # BUS CONSTRAINTS + #################################################################################### + + # Range checker bus protocol - Connects stack and chiplets for 16-bit range checks + enf range_checker_constraints([decoder[0..24], range_checker[0..2], chiplets[0..20]]); + + # TODO: Additional bus constraints for complete VM verification + # enf decoder_bus_constraints([decoder]); + # enf stack_bus_constraints([stack]); + # enf chiplet_bus_constraints([chiplets]); + # enf ace_bus_constraints([chiplets]) +} diff --git a/constraints/range_checker.air b/constraints/range_checker.air new file mode 100644 index 000000000..e18b2965b --- /dev/null +++ b/constraints/range_checker.air @@ -0,0 +1,86 @@ +########################################################################################## +# RANGE CHECKER CONSTRAINTS MODULE +########################################################################################## +# +# The Range Checker provides efficient 16-bit range checking for other VM components +# using the LogUp protocol with optimized gap handling to minimize the minimal +# trace length while supporting (practically) unlimited range checks. +# +# STATUS: Not fully implemented +# +# REFERENCES: +# - Range Checker Spec: https://0xmiden.github.io/miden-vm/design/range.html +# - LogUp Protocol: https://0xmiden.github.io/miden-vm/design/lookups/main.html +########################################################################################## + +mod range_checker + +use chiplets::*; +use utils::*; + +########################################################################################## +# RANGE CHECKER BUS CONSTRAINTS +########################################################################################## + + +ev range_checker_constraints([decoder[24], range_checker[2], chiplets[20]]) { + + #################################################################################### + # Components requesting range checks + #################################################################################### + + # Stack values requiring 16-bit range checks (from decoder trace) + # These correspond to the top 4 operand stack elements during u32 operations + let sv0 = decoder[10]; # Stack value 0 + let sv1 = decoder[11]; # Stack value 1 + let sv2 = decoder[12]; # Stack value 2 + let sv3 = decoder[13]; # Stack value 3 + + # Memory chiplet values requiring 16-bit range checks + let mv0 = chiplets[14]; # Memory value 0 + let mv1 = chiplets[15]; # Memory value 1 + + #################################################################################### + # RANGE CHECKER STATE + #################################################################################### + + # Core range checker table columns + let value = range_checker[1]; # v: Current 16-bit value [0, 65535] + let multiplicity = range_checker[0]; # m: Usage count (how many times v is range-checked) + + #################################################################################### + # OPERATION FLAGS - Determine when range checks are needed + #################################################################################### + + # U32 range check operation flag + # Identifies stack operations that require 16-bit range checking + let not_4 = binary_not(decoder[5]); + let not_5 = binary_not(decoder[6]); + let u32_rc_op = decoder[7] * not_4 * not_5; + + # Memory chiplet operation flag + # Identifies memory operations that require range checking + let s_0 = chiplets[0]; + let s_1 = chiplets[1]; + let s_2 = chiplets[2]; + let chiplets_memory_flag = memory_chiplet_flag(s_0, s_1, s_2); + + #################################################################################### + # RANGE CHECKER INTERACTIONS + #################################################################################### + + # Each cycle, we insert the current value with its multiplicity + bus_4_range_checker.insert(value) with multiplicity; + + # Memory-related range checks + # When memory operations occur, verify that memory values are 16-bit + bus_4_range_checker.remove(mv0) when chiplets_memory_flag; + bus_4_range_checker.remove(mv1) when chiplets_memory_flag; + + # Stack-related range checks + # When u32 operations occur, verify that all operand components are 16-bit + bus_4_range_checker.remove(sv0) when u32_rc_op; + bus_4_range_checker.remove(sv1) when u32_rc_op; + bus_4_range_checker.remove(sv2) when u32_rc_op; + bus_4_range_checker.remove(sv3) when u32_rc_op; +} diff --git a/constraints/rpo.air b/constraints/rpo.air new file mode 100644 index 000000000..78d6b9454 --- /dev/null +++ b/constraints/rpo.air @@ -0,0 +1,97 @@ + +mod rpo + +########################################################################################## +# RPO PERMUTATION ROUND CONSTRAINTS +########################################################################################## + +ev enforce_rpo_round([h[12]]){ + let ark1 = [ark1_0, ark1_1, ark1_2, ark1_3, ark1_4, ark1_5, ark1_6, ark1_7, ark1_8, ark1_9, + ark1_10, ark1_11]; + + let ark2 = [ark2_0, ark2_1, ark2_2, ark2_3, ark2_4, ark2_5, ark2_6, ark2_7, ark2_8, ark2_9, + ark2_10, ark2_11]; + + # Compute the state that should result from applying the first 5 steps of an RPO round to + # the current hasher state. + + # 1. Apply mds + let step1_initial = apply_mds(h); + + # 2. Add constants + let step1_with_constants = [s + k for (s, k) in (step1_initial, ark1)]; + + # 3. Apply sbox + let step1_with_sbox = [s^7 for s in step1_with_constants]; + + # 4. Apply mds + let step1_with_mds = apply_mds(step1_with_sbox); + + # 5. Add constants + let step1 = [s + k for (s, k) in (step1_with_mds, ark2)]; + + # Compute the state that should result from applying the inverse of the last operation of the + # RPO round to the next step of the computation. + let step2 = [s'^7 for s in h]; + + # Make sure that the results are equal. + enf s1 = s2 for (s1, s2) in (step1, step2); +} + +########################################################################################## +# HELPER FUNCTIONS +########################################################################################## + +fn apply_mds(state: felt[12]) -> felt[12]{ + return [sum([s * m for (s, m) in (state, mds_row)]) for mds_row in MDS]; +} + +########################################################################################## +# CONSTANTS AND PERIODIC COLUMNS +########################################################################################## + +# MDS matrix used for computing the linear layer in a RPO round +const MDS = [ + [7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8], + [8, 7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21], + [21, 8, 7, 23, 8, 26, 13, 10, 9, 7, 6, 22], + [22, 21, 8, 7, 23, 8, 26, 13, 10, 9, 7, 6], + [6, 22, 21, 8, 7, 23, 8, 26, 13, 10, 9, 7], + [7, 6, 22, 21, 8, 7, 23, 8, 26, 13, 10, 9], + [9, 7, 6, 22, 21, 8, 7, 23, 8, 26, 13, 10], + [10, 9, 7, 6, 22, 21, 8, 7, 23, 8, 26, 13], + [13, 10, 9, 7, 6, 22, 21, 8, 7, 23, 8, 26], + [26, 13, 10, 9, 7, 6, 22, 21, 8, 7, 23, 8], + [8, 26, 13, 10, 9, 7, 6, 22, 21, 8, 7, 23], + [23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8, 7] +]; + +periodic_columns{ + # Round constants added to the hasher state in the first half of the RPO round + ark1_0: [5789762306288267264, 12987190162843097088, 18072785500942327808, 5674685213610122240, 4887609836208846848, 16308865189192448000, 7123075680859040768, 0], + ark1_1: [6522564764413702144, 653957632802705280, 6200974112677013504, 5759084860419474432, 3027115137917284352, 11977192855656443904, 1034205548717903104, 0], + ark1_2: [17809893479458207744, 4441654670647621120, 17682092219085883392, 13943282657648898048, 9595098600469471232, 12532242556065779712, 7717824418247931904, 0], + ark1_3: [107145243989736512, 4038207883745915904, 10599526828986757120, 1352748651966375424, 10528569829048483840, 14594890931430969344, 3019070937878604288, 0], + ark1_4: [6388978042437517312, 5613464648874829824, 975003873302957312, 17110913224029904896, 7864689113198940160, 7291784239689209856, 11403792746066868224, 0], + ark1_5: [15844067734406017024, 13222989726778339328, 8264241093196931072, 1003883795902368384, 17533723827845969920, 5514718540551361536, 10280580802233112576, 0], + ark1_6: [9975000513555218432, 3037761201230264320, 10065763900435474432, 4141870621881018368, 5781638039037711360, 10025733853830934528, 337153209462421248, 0], + ark1_7: [3344984123768313344, 16683759727265179648, 2181131744534710272, 8121410972417424384, 17024078752430718976, 7293794580341021696, 13333398568519923712, 0], + ark1_8: [9959189626657347584, 8337364536491240448, 6317303992309419008, 14300518605864919040, 109659393484013504, 6728552937464861696, 3596153696935337472, 0], + ark1_9: [12960773468763564032, 3227397518293416448, 1401440938888741632, 13712227150607669248, 7158933660534805504, 6332385040983343104, 8104208463525993472, 0], + ark1_10: [9602914297752487936, 8110510111539675136, 8884468225181997056, 17021852944633065472, 2955076958026921984, 13277683694236792832, 14345062289456084992, 0], + ark1_11: [16657542370200465408, 2872078294163232256, 13066900325715521536, 6252096473787587584, 7433723648458773504, 2600778905124452864, 17036731477169661952, 0], + + # Round constants added to the hasher state in the second half of the RPO round + ark2_0: [6077062762357203968, 6202948458916100096, 8023374565629191168, 18389244934624493568, 6982293561042363392, 3736792340494631424, 17130398059294019584, 0], + ark2_1: [15277620170502010880, 17690140365333231616, 15013690343205953536, 16731736864863924224, 14065426295947720704, 577852220195055360, 519782857322262016, 0], + ark2_2: [5358738125714196480, 3595001575307484672, 4485500052507913216, 4440209734760478208, 16451845770444974080, 6689998335515780096, 9625384390925084672, 0], + ark2_3: [14233283787297595392, 373995945117666496, 12489737547229155328, 17208448209698889728, 7139138592091307008, 13886063479078012928, 1664893052631119104, 0], + ark2_4: [13792579614346651648, 1235734395091296000, 9500452585969031168, 8739495587021565952, 9012006439959783424, 14358505101923203072, 7629576092524553216, 0], + ark2_5: [11614812331536766976, 14172757457833930752, 2054001340201038848, 17000774922218162176, 14619614108529063936, 7744142531772273664, 3485239601103661568, 0], + ark2_6: [14871063686742261760, 707573103686350208, 12420704059284934656, 13533282547195531264, 1394813199588124416, 16135070735728404480, 9755891797164034048, 0], + ark2_7: [10148237148793042944, 15453217512188186624, 355990932618543744, 525402848358706240, 4635111139507788800, 12290902521256030208, 15218148195153268736, 0], + ark2_8: [4457428952329675776, 219777875004506016, 9071225051243524096, 16987541523062161408, 16217473952264204288, 12059913662657710080, 16460604813734957056, 0], + ark2_9: [15590786458219171840, 17876696346199468032, 12766199826003447808, 5466806524462796800, 10782018226466330624, 16456018495793752064, 9643968136937730048, 0], + ark2_10: [10063319113072093184, 17731621626449383424, 9045979173463557120, 14512769585918244864, 6844229992533661696, 4571485474751953408, 3611348709641382912, 0], + ark2_11: [14200078843431360512, 2897136237748376064, 12934431667190679552, 10973956031244050432, 7446486531695178752, 17200392109565784064, 18256379591337758720, 0], +} diff --git a/constraints/stack.air b/constraints/stack.air new file mode 100644 index 000000000..f2b0898d9 --- /dev/null +++ b/constraints/stack.air @@ -0,0 +1,15 @@ +########################################################################################## +# STACK CONSTRAINTS MODULE +########################################################################################## +# +# The Stack module manages Miden VM's operand stack which is practically of unlimited depth +# (up to 2³² items), though only the top 16 stack items are directly accessible, with deeper +# items stored in an overflow virtual table. +# +# STATUS: Not implemented +# +# REFERENCES: +# - Stack Design: https://0xmiden.github.io/miden-vm/design/stack/main.html +########################################################################################## + +mod stack diff --git a/constraints/system.air b/constraints/system.air new file mode 100644 index 000000000..2495ab34d --- /dev/null +++ b/constraints/system.air @@ -0,0 +1,56 @@ +########################################################################################## +# SYSTEM CONSTRAINTS MODULE +########################################################################################## +# +# System module is responsible for managing system data, including the current VM cycle +# (clk), the free memory pointer (fmp) used for specifying the region of memory available +# to procedure locals, and the current and parent execution contexts. +# +# SYSTEM COLUMN LAYOUT (8 columns): +# ┌─────────┬──────────────────────────────────────────────────────────────────────┐ +# │ Column │ Purpose │ +# ├─────────┼──────────────────────────────────────────────────────────────────────┤ +# │ 0 │ clk - VM execution clock (increments each cycle) │ +# │ 1 │ fmp - Free memory pointer (procedure local memory region) │ +# │ 2 │ ctx - Current execution context │ +# │ 3 │ in_syscall - System call execution flag │ +# │ 4-7 │ Function hash - Current/parent function digest (4 elements) │ +# └─────────┴──────────────────────────────────────────────────────────────────────┘ +# +# +# STATUS: Not fully implemented +# +# REFERENCES: +# - System Design: https://0xmiden.github.io/miden-vm/design/main.html +########################################################################################## + +mod system + +########################################################################################## +# SYSTEM CONSTRAINT IMPLEMENTATION +########################################################################################## + +# Main system constraint evaluator - ensures proper system state transitions +ev system_transition([system[8]]) { + # Clock progression constraint + enf system_clock_transition([system[0]]); + + # TODO: Free memory pointer constraints + # TODO: Execution context constraints + # TODO: System call flag constraints + # TODO: Function hash management constraints +} + +########################################################################################## +# CLOCK CONSTRAINT +########################################################################################## + +# Clock increment by 1 constraint +# +# +# CONSTRAINT DEGREE: 1 (linear) +# +# PURPOSE: Ensures VM execution cycles increase monotonically +ev system_clock_transition([clk]) { + enf clk' = clk + 1; +} diff --git a/constraints/tests/miden_vm.rs b/constraints/tests/miden_vm.rs new file mode 100644 index 000000000..8509937d1 --- /dev/null +++ b/constraints/tests/miden_vm.rs @@ -0,0 +1,45 @@ +use std::sync::Arc; + +use air_codegen_ace::{build_ace_circuit, AceCircuit, AceNode}; +use air_ir::{compile, Air}; +use miden_diagnostics::{ + term::termcolor::ColorChoice, CodeMap, DefaultEmitter, DiagnosticsHandler, +}; +use winter_math::FieldElement; + +fn generate_circuit(source: &str) -> (Air, AceCircuit, AceNode) { + let code_map = Arc::new(CodeMap::new()); + let emitter = Arc::new(DefaultEmitter::new(ColorChoice::Auto)); + let diagnostics = DiagnosticsHandler::new(Default::default(), code_map.clone(), emitter); + + let air = air_parser::parse(&diagnostics, code_map, source) + .map_err(air_ir::CompileError::Parse) + .and_then(|program| compile(&diagnostics, program)) + .expect("lowering failed"); + + let (root, circuit) = build_ace_circuit(&air).expect("codegen failed"); + + (air, circuit, root) +} + +/// Loads the MidenVM AIR example +pub fn load_miden_vm_air() -> std::io::Result { + let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let path = format!("{}/miden_vm.air", crate_dir); + let content = std::fs::read_to_string(path)?; + + Ok(content) +} + +#[test] +fn test_miden_vm_updated_air_randomized() { + let air_string = load_miden_vm_air().expect("unable to read MidenVM AIR"); + + let (_air, circuit, root_node) = generate_circuit(&air_string); + + // Provide dummy variable assignments since we are not generating valid ACE vars here + let dummy_inputs = vec![Default::default(); circuit.layout.num_inputs]; + let eval = circuit.eval(root_node, &dummy_inputs); + + assert_eq!(eval, <_ as FieldElement>::ZERO); +} diff --git a/constraints/utils.air b/constraints/utils.air new file mode 100644 index 000000000..5af593a2c --- /dev/null +++ b/constraints/utils.air @@ -0,0 +1,142 @@ +########################################################################################## +# UTILITY CONSTRAINTS MODULE +########################################################################################## +# +# The Utils module provides fundamental constraint patterns and helper functions used +# throughout the Miden VM constraint system. These utilities ensure consistency and +# reusability across all VM components. +# +########################################################################################## + +mod utils + +########################################################################################## +# BINARY CONSTRAINT EVALUATORS +########################################################################################## + +# Ensures value is either 0 or 1 +# +# CONSTRAINT DEGREE: 2 +# +# USAGE PATTERN: +# ```air +# let flag = column[i]; +# enf is_binary([flag]); // Ensures flag ∈ {0, 1} +# ``` +ev is_binary([a]) { + enf a^2 = a; +} + +########################################################################################## +# STATE CONSISTENCY EVALUATORS +########################################################################################## + +# State persistence constraint - ensures values remain constant across cycles +# +# INTUITION: +# +# Many VM components require certain values to remain stable during specific +# operations or phases. This constraint enforces immutability when needed. +# +# CONSTRAINT DEGREE: 1 +# +# USAGE PATTERN: +# ```air +# let stable_value = column[i]; +# enf is_unchanged([stable_value]) when operation_active; +# ``` +ev is_unchanged([column]) { + enf column' = column; +} + +########################################################################################## +# LOGICAL OPERATION HELPERS +########################################################################################## + +# Binary negation function - computes logical NOT in finite field +# +# This is usually combined with another constraint to ensure booleaness +# +# CONSTRAINT DEGREE: 1 +# +# USAGE EXAMPLES: +# ```air +# let not_flag = binary_not(flag); // Simple negation +# let condition = flag * binary_not(other); // AND NOT pattern +# ``` +fn binary_not(a: felt) -> felt { + return 1 - a; +} + +# Binary constraint helper - returns 0 if input is binary (0 or 1), non-zero otherwise +# +# This function computes flag² - flag, which equals: +# - 0 when flag = 0 (since 0² - 0 = 0) +# - 0 when flag = 1 (since 1² - 1 = 0) +# - non-zero for any other value +# +# Usage: enf binary_constraint(x) = 0; // constrains x to be 0 or 1 +fn binary_constraint(flag: felt) -> felt { + return flag^2 - flag; +} + +########################################################################################## +# BOOLEAN ALGEBRA OPERATIONS +########################################################################################## + +# Logical AND: returns 1 if both a and b are 1, else 0 +# +# CONSTRAINT DEGREE: 2 +# +# USAGE EXAMPLES: +# ```air +# let result = binary_and(flag_a, flag_b); +# ``` +fn binary_and(a: felt, b: felt) -> felt { + return a * b; +} + +# Logical OR: returns 1 if either a or b is 1, else 0 +# +# Uses inclusion-exclusion principle: |A ∪ B| = |A| + |B| - |A ∩ B| +# +# CONSTRAINT DEGREE: 2 +# +# USAGE EXAMPLES: +# ```air +# let result = binary_or(flag_a, flag_b); +# ``` +fn binary_or(a: felt, b: felt) -> felt { + return a + b - a * b; +} + +# Logical XOR: returns 1 if exactly one of a or b is 1, else 0 +# +# CONSTRAINT DEGREE: 2 +# +# USAGE EXAMPLES: +# ```air +# let result = binary_xor(flag_a, flag_b); +# ``` +fn binary_xor(a: felt, b: felt) -> felt { + let ab = a * b; + return a + b - (ab + ab); +} + +########################################################################################## +# CONDITIONAL OPERATIONS +########################################################################################## + +# Conditional selection: returns if_true when condition=1, if_false when condition=0 +# +# This is a multiplexer function that selects between two values based on condition +# +# CONSTRAINT DEGREE: 2 +# +# USAGE EXAMPLES: +# ```air +# let result = select(condition, true_value, false_value); +# ``` +fn select(condition: felt, if_true: felt, if_false: felt) -> felt { + return condition * (if_true - if_false) + if_false; +} From 5798a02ad396210e6826c90b082eb591fc1d0a70 Mon Sep 17 00:00:00 2001 From: al <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 17:14:08 +0400 Subject: [PATCH 28/83] test: Add failing test for cross-module constant dependencies --- air-script/src/tests/mod.rs | 2 + air-script/tests/codegen/winterfell.rs | 379 ++++++++++++++++++ .../cross_module_constants/constants_lib.air | 21 + .../cross_module_constants.air | 21 + .../cross_module_constants.rs | 2 + .../tests/cross_module_constants/mod.rs | 1 + 6 files changed, 426 insertions(+) create mode 100644 air-script/tests/codegen/winterfell.rs create mode 100644 air-script/tests/cross_module_constants/constants_lib.air create mode 100644 air-script/tests/cross_module_constants/cross_module_constants.air create mode 100644 air-script/tests/cross_module_constants/cross_module_constants.rs create mode 100644 air-script/tests/cross_module_constants/mod.rs diff --git a/air-script/src/tests/mod.rs b/air-script/src/tests/mod.rs index a140647c8..ce052cc87 100644 --- a/air-script/src/tests/mod.rs +++ b/air-script/src/tests/mod.rs @@ -11,6 +11,8 @@ mod constant_in_range; #[allow(unused_variables, dead_code, unused_mut)] mod constants; #[allow(unused_variables, dead_code, unused_mut)] +mod cross_module_constants; +#[allow(unused_variables, dead_code, unused_mut)] mod constraint_comprehension; #[allow(unused_variables, dead_code, unused_mut)] mod evaluators; diff --git a/air-script/tests/codegen/winterfell.rs b/air-script/tests/codegen/winterfell.rs new file mode 100644 index 000000000..e2c9fc621 --- /dev/null +++ b/air-script/tests/codegen/winterfell.rs @@ -0,0 +1,379 @@ +use expect_test::expect_file; + +use super::helpers::{Target, Test}; + +#[test] +fn binary() { + let generated_air = Test::new("tests/binary/binary.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../binary/binary.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn bitwise() { + let generated_air = Test::new("tests/bitwise/bitwise.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../bitwise/bitwise.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_complex() { + let generated_air = Test::new("tests/buses/buses_complex.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../buses/buses_complex.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_simple() { + let generated_air = Test::new("tests/buses/buses_simple.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../buses/buses_simple.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_simple_with_evaluators() { + let generated_air = Test::new("tests/buses/buses_simple_with_evaluators.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../buses/buses_simple.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_varlen_boundary_both() { + let generated_air = Test::new("tests/buses/buses_varlen_boundary_both.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../buses/buses_varlen_boundary_both.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_varlen_boundary_first() { + let generated_air = Test::new("tests/buses/buses_varlen_boundary_first.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../buses/buses_varlen_boundary_first.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn buses_varlen_boundary_last() { + let generated_air = Test::new("tests/buses/buses_varlen_boundary_last.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../buses/buses_varlen_boundary_last.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn computed_indices_complex() { + let generated_air = + Test::new("tests/computed_indices/computed_indices_complex.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../computed_indices/computed_indices_complex.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn computed_indices_simple() { + let generated_air = Test::new("tests/computed_indices/computed_indices_simple.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../computed_indices/computed_indices_simple.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn constant_in_range() { + let generated_air = Test::new("tests/constant_in_range/constant_in_range.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../constant_in_range/constant_in_range.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn constants() { + let generated_air = Test::new("tests/constants/constants.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../constants/constants.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn constraint_comprehension() { + let generated_air = + Test::new("tests/constraint_comprehension/constraint_comprehension.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../constraint_comprehension/constraint_comprehension.rs"]; + expected.assert_eq(&generated_air); + + let generated_air = + Test::new("tests/constraint_comprehension/cc_with_evaluators.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../constraint_comprehension/constraint_comprehension.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn evaluators() { + let generated_air = Test::new("tests/evaluators/evaluators.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../evaluators/evaluators.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn evaluators_slice() { + let generated_air = Test::new("tests/evaluators/evaluators_slice.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../evaluators/evaluators_slice.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn evaluators_nested_slice_call() { + let generated_air = Test::new("tests/evaluators/evaluators_nested_slice_call.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../evaluators/evaluators_nested_slice_call.rs"]; + expected.assert_eq(&generated_air); +} + +// TODO: add support for nested slicing in general expressions. +// +// #[test] +// fn evaluators_slice_slicing() { +// let generated_air = Test::new("tests/evaluators/evaluators_slice_slicing.air".to_string()) +// .transpile(Target::Winterfell) +// .unwrap(); +// +// let expected = expect_file!["../evaluators/evaluators_slice_slicing.rs"]; +// expected.assert_eq(&generated_air); +// } + +#[test] +fn fibonacci() { + let generated_air = Test::new("tests/fibonacci/fibonacci.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../fibonacci/fibonacci.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn functions_complex() { + let generated_air = Test::new("tests/functions/functions_complex.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../functions/functions_complex.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn functions_simple() { + let generated_air = Test::new("tests/functions/functions_simple.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../functions/functions_simple.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn functions_simple_inlined() { + // make sure that the constraints generated using inlined functions are the same as the ones + // generated using regular functions + let generated_air = Test::new("tests/functions/inlined_functions_simple.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../functions/functions_simple.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn indexed_trace_access() { + let generated_air = + Test::new("tests/indexed_trace_access/indexed_trace_access.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../indexed_trace_access/indexed_trace_access.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn list_comprehension() { + let generated_air = Test::new("tests/list_comprehension/list_comprehension.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../list_comprehension/list_comprehension.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn list_comprehension_nested() { + let generated_air = + Test::new("tests/list_comprehension/list_comprehension_nested.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../list_comprehension/list_comprehension_nested.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn list_folding() { + let generated_air = Test::new("tests/list_folding/list_folding.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../list_folding/list_folding.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn periodic_columns() { + let generated_air = Test::new("tests/periodic_columns/periodic_columns.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../periodic_columns/periodic_columns.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn pub_inputs() { + let generated_air = Test::new("tests/pub_inputs/pub_inputs.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../pub_inputs/pub_inputs.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors() { + let generated_air = Test::new("tests/selectors/selectors.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../selectors/selectors.rs"]; + expected.assert_eq(&generated_air); + + let generated_air = Test::new("tests/selectors/selectors_with_evaluators.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../selectors/selectors_with_evaluators.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors_combine_simple() { + let generated_air = Test::new("tests/selectors/selectors_combine_simple.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../selectors/selectors_combine_simple.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors_combine_complex() { + let generated_air = Test::new("tests/selectors/selectors_combine_complex.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../selectors/selectors_combine_complex.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn selectors_combine_with_list_comprehensions() { + let generated_air = + Test::new("tests/selectors/selectors_combine_with_list_comprehensions.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../selectors/selectors_combine_with_list_comprehensions.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn system() { + let generated_air = Test::new("tests/system/system.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../system/system.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn trace_col_groups() { + let generated_air = Test::new("tests/trace_col_groups/trace_col_groups.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../trace_col_groups/trace_col_groups.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn variables() { + let generated_air = Test::new("tests/variables/variables.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../variables/variables.rs"]; + expected.assert_eq(&generated_air); +} + +#[test] +fn cross_module_constants() { + // Test that constants used in comprehension iterables work across module boundaries + let generated_air = + Test::new("tests/cross_module_constants/cross_module_constants.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../cross_module_constants/cross_module_constants.rs"]; + expected.assert_eq(&generated_air); +} diff --git a/air-script/tests/cross_module_constants/constants_lib.air b/air-script/tests/cross_module_constants/constants_lib.air new file mode 100644 index 000000000..25ef19270 --- /dev/null +++ b/air-script/tests/cross_module_constants/constants_lib.air @@ -0,0 +1,21 @@ +mod constants_lib + +# Constants used in comprehension iterables +const WEIGHTS = [1, 2, 3, 4]; + +# Pure function that uses constants in a comprehension +fn weighted_sum(values: felt[4]) -> felt { + return sum([v * w for (v, w) in (values, WEIGHTS)]); +} + +# Another function that calls the first +fn compute_result(a: felt, b: felt, c: felt, d: felt) -> felt { + let values = [a, b, c, d]; + return weighted_sum(values); +} + +# Evaluator that uses the chain of functions +ev apply_computation([cols[5]]) { + let result = compute_result(cols[0], cols[1], cols[2], cols[3]); + enf cols[4] = result; +} diff --git a/air-script/tests/cross_module_constants/cross_module_constants.air b/air-script/tests/cross_module_constants/cross_module_constants.air new file mode 100644 index 000000000..87315ecd6 --- /dev/null +++ b/air-script/tests/cross_module_constants/cross_module_constants.air @@ -0,0 +1,21 @@ +def CrossModuleConstantsTest + +# Import evaluator that uses a chain: apply_computation -> compute_result -> weighted_sum -> WEIGHTS constant +use constants_lib::apply_computation; + +trace_columns { + main: [a, b, c, d, result], +} + +public_inputs { + expected: [1], +} + +boundary_constraints { + enf a.first = 0; +} + +integrity_constraints { + # Use imported evaluator that internally uses constants in comprehensions + enf apply_computation([a, b, c, d, result]); +} diff --git a/air-script/tests/cross_module_constants/cross_module_constants.rs b/air-script/tests/cross_module_constants/cross_module_constants.rs new file mode 100644 index 000000000..44bfcdb23 --- /dev/null +++ b/air-script/tests/cross_module_constants/cross_module_constants.rs @@ -0,0 +1,2 @@ +// This file will be overwritten by the test with generated code +// It serves as the expected output for the cross_module_constants test diff --git a/air-script/tests/cross_module_constants/mod.rs b/air-script/tests/cross_module_constants/mod.rs new file mode 100644 index 000000000..fa516c053 --- /dev/null +++ b/air-script/tests/cross_module_constants/mod.rs @@ -0,0 +1 @@ +mod cross_module_constants; From 512e04bb0fe9e2c1244e99894c0e702bc09d986e Mon Sep 17 00:00:00 2001 From: al <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 17:15:11 +0400 Subject: [PATCH 29/83] fix: Handle cross-module constant dependencies in dependency graph and MIR translation --- .../cross_module_constants.rs | 99 ++++++++++++++++++- mir/src/passes/translate.rs | 4 + parser/src/sema/semantic_analysis.rs | 6 +- 3 files changed, 104 insertions(+), 5 deletions(-) diff --git a/air-script/tests/cross_module_constants/cross_module_constants.rs b/air-script/tests/cross_module_constants/cross_module_constants.rs index 44bfcdb23..5de652283 100644 --- a/air-script/tests/cross_module_constants/cross_module_constants.rs +++ b/air-script/tests/cross_module_constants/cross_module_constants.rs @@ -1,2 +1,97 @@ -// This file will be overwritten by the test with generated code -// It serves as the expected output for the cross_module_constants test +use winter_air::{Air, AirContext, Assertion, AuxRandElements, EvaluationFrame, ProofOptions as WinterProofOptions, TransitionConstraintDegree, TraceInfo}; +use winter_math::fields::f64::BaseElement as Felt; +use winter_math::{ExtensionOf, FieldElement, ToElements}; +use winter_utils::{ByteWriter, Serializable}; + +pub struct PublicInputs { + expected: [Felt; 1], +} + +impl PublicInputs { + pub fn new(expected: [Felt; 1]) -> Self { + Self { expected } + } +} + +impl Serializable for PublicInputs { + fn write_into(&self, target: &mut W) { + self.expected.write_into(target); + } +} + +impl ToElements for PublicInputs { + fn to_elements(&self) -> Vec { + let mut elements = Vec::new(); + elements.extend_from_slice(&self.expected); + elements + } +} + +pub struct CrossModuleConstantsTest { + context: AirContext, + expected: [Felt; 1], +} + +impl CrossModuleConstantsTest { + pub fn last_step(&self) -> usize { + self.trace_length() - self.context().num_transition_exemptions() + } +} + +impl Air for CrossModuleConstantsTest { + type BaseField = Felt; + type PublicInputs = PublicInputs; + + fn context(&self) -> &AirContext { + &self.context + } + + fn new(trace_info: TraceInfo, public_inputs: PublicInputs, options: WinterProofOptions) -> Self { + let main_degrees = vec![TransitionConstraintDegree::new(1)]; + let aux_degrees = vec![]; + let num_main_assertions = 1; + let num_aux_assertions = 0; + + let context = AirContext::new_multi_segment( + trace_info, + main_degrees, + aux_degrees, + num_main_assertions, + num_aux_assertions, + options, + ) + .set_num_transition_exemptions(2); + Self { context, expected: public_inputs.expected } + } + + fn get_periodic_column_values(&self) -> Vec> { + vec![] + } + + fn get_assertions(&self) -> Vec> { + let mut result = Vec::new(); + result.push(Assertion::single(0, 0, Felt::ZERO)); + result + } + + fn get_aux_assertions>(&self, aux_rand_elements: &AuxRandElements) -> Vec> { + let mut result = Vec::new(); + result + } + + fn evaluate_transition>(&self, frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E]) { + let main_current = frame.current(); + let main_next = frame.next(); + result[0] = main_current[4] - (main_current[0] + main_current[1] * E::from(Felt::new(2_u64)) + main_current[2] * E::from(Felt::new(3_u64)) + main_current[3] * E::from(Felt::new(4_u64))); + } + + fn evaluate_aux_transition(&self, main_frame: &EvaluationFrame, aux_frame: &EvaluationFrame, _periodic_values: &[F], aux_rand_elements: &AuxRandElements, result: &mut [E]) + where F: FieldElement, + E: FieldElement + ExtensionOf, + { + let main_current = main_frame.current(); + let main_next = main_frame.next(); + let aux_current = aux_frame.current(); + let aux_next = aux_frame.next(); + } +} \ No newline at end of file diff --git a/mir/src/passes/translate.rs b/mir/src/passes/translate.rs index db342ecb7..6a631fe50 100644 --- a/mir/src/passes/translate.rs +++ b/mir/src/passes/translate.rs @@ -714,6 +714,10 @@ impl<'a> MirBuilder<'a> { }) .build(); Ok(node) + } else if let Some(constant) = self.program.constants.get(qual_ident) { + // Handle qualified constant references that weren't inlined + // (e.g., constants used in comprehension iterables across modules) + self.translate_const(&constant.value, access.span()) } else { // This is a qualified reference that should have been eliminated // during inlining or constant propagation, but somehow slipped through. diff --git a/parser/src/sema/semantic_analysis.rs b/parser/src/sema/semantic_analysis.rs index 09d89426f..8bda93fc3 100644 --- a/parser/src/sema/semantic_analysis.rs +++ b/parser/src/sema/semantic_analysis.rs @@ -444,9 +444,9 @@ impl VisitMut for SemanticAnalysis<'_> { self.current_module.clone().unwrap(), NamespacedIdentifier::Function(function.name), ); - let current_item_node_index = self.deps_graph.add_node(current_item); - for (referenced_item, ref_type) in self.referenced.iter() { - let referenced_item_node_index = self.deps_graph.add_node(referenced_item.clone()); + let current_item_node_index = self.get_node_index_or_add(¤t_item); + for (referenced_item, ref_type) in self.referenced.clone().iter() { + let referenced_item_node_index = self.get_node_index_or_add(referenced_item); self.deps_graph.add_edge( current_item_node_index, referenced_item_node_index, From 778c36637af98bdf2f91b700c55a1690c5249e1c Mon Sep 17 00:00:00 2001 From: al <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:26:28 +0400 Subject: [PATCH 30/83] enable hasher constraints --- constraints/chiplets.air | 4 +- constraints/hasher.air | 101 ++++++++++++++++++++------------------- constraints/rpo.air | 45 ++++++++++------- 3 files changed, 83 insertions(+), 67 deletions(-) diff --git a/constraints/chiplets.air b/constraints/chiplets.air index cbb22b94f..77cec26d0 100644 --- a/constraints/chiplets.air +++ b/constraints/chiplets.air @@ -25,6 +25,8 @@ mod chiplets use ace::ace_chiplet_constraints_all_rows; use ace::ace_chiplet_constraints_first_row; +use hasher::hash_chiplet; + use bitwise::bitwise_chiplet_constraints; use memory::memory_chiplet_constraints_all_rows; @@ -63,7 +65,7 @@ ev chiplets_constraints([chiplets[20]]) { # Apply chiplet-specific constraints based on hierarchical selector state. enf match { - #case hash_active: hash_chiplet_constraints([chiplets[1..20]]), + case hash_active: hash_chiplet([chiplets[1..17]]), case bitwise_active: bitwise_chiplet_constraints([chiplets[2..15]]), case memory_active: memory_chiplet_constraints_all_rows([chiplets[3..18]]), case ace_active: ace_chiplet_constraints_all_rows([chiplets[3..20]]), diff --git a/constraints/hasher.air b/constraints/hasher.air index 599d4e25b..5d92137f4 100644 --- a/constraints/hasher.air +++ b/constraints/hasher.air @@ -46,15 +46,15 @@ ev selector_columns([s[3]]) { # Enforce that selector columns are binary. enf is_binary([selector]) for selector in s; - # Compute relevant flags - let f_abp = get_f_abp(s); - let f_mpa = get_f_mpa(s); - let f_mva = get_f_mva(s); - let f_mua = get_f_mua(s); - let f_out = get_f_out(s); - let f_out_next = get_f_out_next(s); - - # Enforce that unless f_out = 1 or f_out' = 1, the values in columns s[1] and s[2] are copied + # Compute relevant flags (passing periodic column values as parameters) + let f_abp = get_f_abp(s, cycle_row_7); + let f_mpa = get_f_mpa(s, cycle_row_7); + let f_mva = get_f_mva(s, cycle_row_7); + let f_mua = get_f_mua(s, cycle_row_7); + let f_out = get_f_out(s, cycle_row_7); + let f_out_next = get_f_out_next(s, cycle_row_6, s[0]', s[1]'); + + # Enforce that unless f_out = 1 or f_out' = 1, the values in columns s[1] and s[2] are copied # over to the next row. # This encodes the fact that we can change the op flags only at the end of a cycle in order # to output a result, or at the start of a new cycle to initiate a new operation. @@ -65,7 +65,7 @@ ev selector_columns([s[3]]) { # f_abp, f_mpa, f_mva or f_mua let f_comp = f_abp + f_mpa + f_mva + f_mua; - # Enforce that if any of f_abp, f_mpa, f_mva, f_mua flags is set to 1, the next value of s[0] + # Enforce that if any of f_abp, f_mpa, f_mva, f_mua flags is set to 1, the next value of s[0] # is 0. # This basically enforces the exclusion of all op which initiate a new op (at the start of a new # cycle). Note that f_comp is a flag that is set only on rows which are 1 less than a multiple of 8 @@ -73,23 +73,23 @@ ev selector_columns([s[3]]) { enf s[0]' = 0 when f_comp; # Enforce that no invalid combinations of flags are allowed. - # This enforces that if s[0] is 0 then the either f_hout or f_sout is set.§§ + # This enforces that if s[0] is 0 then the either f_hout or f_sout is set. enf s[1] = 0 when binary_and(cycle_row_7, binary_not(s[0])); } # Enforce node index constraints ev node_index([s[3], i]) { - # Compute relevant flags - let f_mp = get_f_mp(s); - let f_mv = get_f_mv(s); - let f_mu = get_f_mu(s); - let f_mpa = get_f_mpa(s); - let f_mva = get_f_mva(s); - let f_mua = get_f_mua(s); - let f_out = get_f_out(s); + # Compute relevant flags (passing periodic column values as parameters) + let f_mp = get_f_mp(s, cycle_row_0); + let f_mv = get_f_mv(s, cycle_row_0); + let f_mu = get_f_mu(s, cycle_row_0); + let f_mpa = get_f_mpa(s, cycle_row_7); + let f_mva = get_f_mva(s, cycle_row_7); + let f_mua = get_f_mua(s, cycle_row_7); + let f_out = get_f_out(s, cycle_row_7); # Flag indicating to enforce the constraint that b is binary only when a new node is absorbed into - # the hasher state (when the hash operation is either one of Merkle path verification or + # the hasher state (when the hash operation is either one of Merkle path verification or # Merkle root update) let f_an = f_mp + f_mv + f_mu + f_mpa + f_mva + f_mua; @@ -102,7 +102,7 @@ ev node_index([s[3], i]) { # Enforce that when a computation is finished i = 0. enf i = 0 when f_out; - # Enforce that the value in i is copied over to the next row unless we are absorbing a new row + # Enforce that the value in i is copied over to the next row unless we are absorbing a new row # or the computation is finished. let not_absorbing_nor_comp_finished = 1 - (f_an + f_out); enf is_unchanged([i]) when not_absorbing_nor_comp_finished; @@ -113,10 +113,11 @@ ev hasher_state([s[3], h[12], i]) { # Enforce the RPO permutation round constraints enf enforce_rpo_round([h]) when !cycle_row_7; - let f_mp = get_f_mp(s); - let f_mv = get_f_mv(s); - let f_mu = get_f_mu(s); - let f_abp = get_f_abp(s); + # Compute relevant flags (passing periodic column values as parameters) + let f_mp = get_f_mp(s, cycle_row_0); + let f_mv = get_f_mv(s, cycle_row_0); + let f_mu = get_f_mu(s, cycle_row_0); + let f_abp = get_f_abp(s, cycle_row_7); # Flag that is true when the performed operation includes absorbing the next node during Merkle # path computation. @@ -125,15 +126,15 @@ ev hasher_state([s[3], h[12], i]) { # b is the value of the bit which is discarded during shift by one bit to the right. let b = i - 2 * i'; - # Enforce that when absorbing the next set of elements into the state during linear hash - # computation (i.e. f_abp = 1) the first 4 elements (the capacity portion) are carried over to + # Enforce that when absorbing the next set of elements into the state during linear hash + # computation (i.e. f_abp = 1) the first 4 elements (the capacity portion) are carried over to # the next row. enf f_abp * (h' - h) = 0 for h in h; # TODO: Double check the following and fix both docs and VM if there is a typo # - # Enforce that when absorbing the next node during Merkle path computation - # (i.e. f_mp + f_mv + f_mu = 1), the result of the previous hash (h[4], ..., h[7]) are copied + # Enforce that when absorbing the next node during Merkle path computation + # (i.e. f_mp + f_mv + f_mu = 1), the result of the previous hash (h[4], ..., h[7]) are copied # over either to (h[4]', ..., h[7]') or to (h[8]', ..., h[11]') depending on the value of b. # # TODO: uncomment when computed indices are supported @@ -184,63 +185,63 @@ ev hasher_state([s[3], h[12], i]) { # f_mp: Merkle Path verification flag (1,0,1) on cycle_row_0 # Initiates standard Merkle path verification computation. -fn get_f_mp(s: felt[3]) -> felt { - return cycle_row_0 & s[0] & binary_not(s[1]) & s[2]; +fn get_f_mp(s: felt[3], row0: felt) -> felt { + return row0 & s[0] & binary_not(s[1]) & s[2]; } # f_mv: Merkle path Verification for "old" node flag (1,1,0) on cycle_row_0 # Begins verification for old leaf value during Merkle root update computation. -fn get_f_mv(s: felt[3]) -> felt { - return cycle_row_0 & s[0] & s[1] & binary_not(s[2]); +fn get_f_mv(s: felt[3], row0: felt) -> felt { + return row0 & s[0] & s[1] & binary_not(s[2]); } # f_mu: Merkle path verification for "new" node flag (1,1,1) on cycle_row_0 # Starts verification for new leaf value during Merkle root update computation. -fn get_f_mu(s: felt[3]) -> felt { - return cycle_row_0 & s[0] & s[1] & s[2]; +fn get_f_mu(s: felt[3], row0: felt) -> felt { + return row0 & s[0] & s[1] & s[2]; } # f_abp: Absorb elements for linear hash flag (1,0,0) on cycle_row_7 # Absorbs next set of elements into hasher state during linear hash computation. -fn get_f_abp(s: felt[3]) -> felt { - return cycle_row_7 & s[0] & binary_not(s[1]) & binary_not(s[2]); +fn get_f_abp(s: felt[3], row7: felt) -> felt { + return row7 & s[0] & binary_not(s[1]) & binary_not(s[2]); } # f_mpa: Merkle Path Absorb flag (1,0,1) on cycle_row_7 # Absorbs next Merkle path node during standard verification computation. -fn get_f_mpa(s: felt[3]) -> felt { - return cycle_row_7 & s[0] & binary_not(s[1]) & s[2]; +fn get_f_mpa(s: felt[3], row7: felt) -> felt { + return row7 & s[0] & binary_not(s[1]) & s[2]; } # f_mva: Merkle path absorb for "old" node flag (1,1,0) on cycle_row_7 # Absorbs next node during "old" leaf verification (Merkle root update computation). -fn get_f_mva(s: felt[3]) -> felt { - return cycle_row_7 & s[0] & s[1] & binary_not(s[2]); +fn get_f_mva(s: felt[3], row7: felt) -> felt { + return row7 & s[0] & s[1] & binary_not(s[2]); } # f_mua: Merkle path absorb for "new" node flag (1,1,1) on cycle_row_7 # Absorbs next node during "new" leaf verification (Merkle root update computation). -fn get_f_mua(s: felt[3]) -> felt { - return cycle_row_7 & s[0] & s[1] & s[2]; +fn get_f_mua(s: felt[3], row7: felt) -> felt { + return row7 & s[0] & s[1] & s[2]; } # We can define two flags: -# 1. Flag f_hout = cycle_row_7 & binary_not(s[0]) & binary_not(s[1]) & binary_not(s[2]), -# which is set to 1 when selector flags are (0,0,0) on rows which are 1 less than a multiple +# 1. Flag f_hout = cycle_row_7 & binary_not(s[0]) & binary_not(s[1]) & binary_not(s[2]), +# which is set to 1 when selector flags are (0,0,0) on rows which are 1 less than a multiple # of 8. This flag is for the instruction that returns the resulting digest of the currently # running computation. -# 2. Flag f_sout = cycle_row_7 & binary_not(s[0]) & binary_not(s[1]) & s[2], which is set to 1 +# 2. Flag f_sout = cycle_row_7 & binary_not(s[0]) & binary_not(s[1]) & s[2], which is set to 1 # when selector flags are (0,0,1) on rows which are 1 less than a multiple of 8. This flag is # for the instruction that returns the whole hasher state. # # Flag f_out is set to 1 when either f_hout = 1 or f_sout = 1 in the current row. -fn get_f_out(s: felt[3]) -> felt { - return cycle_row_7 & binary_not(s[0]) & binary_not(s[1]); +fn get_f_out(s: felt[3], row7: felt) -> felt { + return row7 & binary_not(s[0]) & binary_not(s[1]); } # Flag f_out_next is set to 1 when either f_hout = 1 or f_sout = 1 in the next row. -fn get_f_out_next(s: felt[3]) -> felt { - return cycle_row_6 & binary_not(s[0]') & binary_not(s[1]'); +fn get_f_out_next(s: felt[3], row6: felt, s0_next: felt, s1_next: felt) -> felt { + return row6 & binary_not(s0_next) & binary_not(s1_next); } ########################################################################################## diff --git a/constraints/rpo.air b/constraints/rpo.air index 78d6b9454..5061e8188 100644 --- a/constraints/rpo.air +++ b/constraints/rpo.air @@ -43,28 +43,41 @@ ev enforce_rpo_round([h[12]]){ ########################################################################################## fn apply_mds(state: felt[12]) -> felt[12]{ - return [sum([s * m for (s, m) in (state, mds_row)]) for mds_row in MDS]; + # Compute dot product of state vector with each MDS row + let result0 = sum([s * m for (s, m) in (state, MDSROWA)]); + let result1 = sum([s * m for (s, m) in (state, MDSROWB)]); + let result2 = sum([s * m for (s, m) in (state, MDSROWC)]); + let result3 = sum([s * m for (s, m) in (state, MDSROWD)]); + let result4 = sum([s * m for (s, m) in (state, MDSROWE)]); + let result5 = sum([s * m for (s, m) in (state, MDSROWF)]); + let result6 = sum([s * m for (s, m) in (state, MDSROWG)]); + let result7 = sum([s * m for (s, m) in (state, MDSROWH)]); + let result8 = sum([s * m for (s, m) in (state, MDSROWI)]); + let result9 = sum([s * m for (s, m) in (state, MDSROWJ)]); + let result10 = sum([s * m for (s, m) in (state, MDSROWK)]); + let result11 = sum([s * m for (s, m) in (state, MDSROWL)]); + + return [result0, result1, result2, result3, result4, result5, + result6, result7, result8, result9, result10, result11]; } ########################################################################################## # CONSTANTS AND PERIODIC COLUMNS ########################################################################################## -# MDS matrix used for computing the linear layer in a RPO round -const MDS = [ - [7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8], - [8, 7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21], - [21, 8, 7, 23, 8, 26, 13, 10, 9, 7, 6, 22], - [22, 21, 8, 7, 23, 8, 26, 13, 10, 9, 7, 6], - [6, 22, 21, 8, 7, 23, 8, 26, 13, 10, 9, 7], - [7, 6, 22, 21, 8, 7, 23, 8, 26, 13, 10, 9], - [9, 7, 6, 22, 21, 8, 7, 23, 8, 26, 13, 10], - [10, 9, 7, 6, 22, 21, 8, 7, 23, 8, 26, 13], - [13, 10, 9, 7, 6, 22, 21, 8, 7, 23, 8, 26], - [26, 13, 10, 9, 7, 6, 22, 21, 8, 7, 23, 8], - [8, 26, 13, 10, 9, 7, 6, 22, 21, 8, 7, 23], - [23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8, 7] -]; +# MDS matrix rows used for computing the linear layer in a RPO round +const MDSROWA = [7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8]; +const MDSROWB = [8, 7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21]; +const MDSROWC = [21, 8, 7, 23, 8, 26, 13, 10, 9, 7, 6, 22]; +const MDSROWD = [22, 21, 8, 7, 23, 8, 26, 13, 10, 9, 7, 6]; +const MDSROWE = [6, 22, 21, 8, 7, 23, 8, 26, 13, 10, 9, 7]; +const MDSROWF = [7, 6, 22, 21, 8, 7, 23, 8, 26, 13, 10, 9]; +const MDSROWG = [9, 7, 6, 22, 21, 8, 7, 23, 8, 26, 13, 10]; +const MDSROWH = [10, 9, 7, 6, 22, 21, 8, 7, 23, 8, 26, 13]; +const MDSROWI = [13, 10, 9, 7, 6, 22, 21, 8, 7, 23, 8, 26]; +const MDSROWJ = [26, 13, 10, 9, 7, 6, 22, 21, 8, 7, 23, 8]; +const MDSROWK = [8, 26, 13, 10, 9, 7, 6, 22, 21, 8, 7, 23]; +const MDSROWL = [23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8, 7]; periodic_columns{ # Round constants added to the hasher state in the first half of the RPO round From 28ced492bb51a3b598922e74ef419d537d288fc2 Mon Sep 17 00:00:00 2001 From: al <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:41:54 +0400 Subject: [PATCH 31/83] test: Add failing test for comprehension periodic binding scoping --- air-script/src/tests/mod.rs | 2 + air-script/tests/codegen/winterfell.rs | 13 +++ .../comprehension_periodic_binding.air | 19 ++++ .../comprehension_periodic_binding.rs | 97 +++++++++++++++++++ .../comprehension_periodic_binding/lib.air | 16 +++ .../comprehension_periodic_binding/mod.rs | 3 + 6 files changed, 150 insertions(+) create mode 100644 air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.air create mode 100644 air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs create mode 100644 air-script/tests/comprehension_periodic_binding/lib.air create mode 100644 air-script/tests/comprehension_periodic_binding/mod.rs diff --git a/air-script/src/tests/mod.rs b/air-script/src/tests/mod.rs index ce052cc87..587fb1552 100644 --- a/air-script/src/tests/mod.rs +++ b/air-script/src/tests/mod.rs @@ -7,6 +7,8 @@ mod buses; #[allow(unused_variables, dead_code, unused_mut)] mod computed_indices; #[allow(unused_variables, dead_code, unused_mut)] +mod comprehension_periodic_binding; +#[allow(unused_variables, dead_code, unused_mut)] mod constant_in_range; #[allow(unused_variables, dead_code, unused_mut)] mod constants; diff --git a/air-script/tests/codegen/winterfell.rs b/air-script/tests/codegen/winterfell.rs index e2c9fc621..3e8e30911 100644 --- a/air-script/tests/codegen/winterfell.rs +++ b/air-script/tests/codegen/winterfell.rs @@ -377,3 +377,16 @@ fn cross_module_constants() { let expected = expect_file!["../cross_module_constants/cross_module_constants.rs"]; expected.assert_eq(&generated_air); } + +#[test] +fn comprehension_periodic_binding() { + // Test that comprehension bindings over periodic columns are typed as Local, not PeriodicColumn + // This pattern is used when iterating over a vector containing periodic column references + let generated_air = + Test::new("tests/comprehension_periodic_binding/comprehension_periodic_binding.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../comprehension_periodic_binding/comprehension_periodic_binding.rs"]; + expected.assert_eq(&generated_air); +} diff --git a/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.air b/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.air new file mode 100644 index 000000000..a21bbcbd5 --- /dev/null +++ b/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.air @@ -0,0 +1,19 @@ +def ComprehensionPeriodicBindingTest + +use lib::test_comprehension; + +trace_columns { + main: [a, b], +} + +public_inputs { + stack_inputs: [1], +} + +boundary_constraints { + enf a.first = 0; +} + +integrity_constraints { + enf test_comprehension([a, b]); +} diff --git a/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs b/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs new file mode 100644 index 000000000..9e56fa9e2 --- /dev/null +++ b/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs @@ -0,0 +1,97 @@ +use winter_air::{Air, AirContext, Assertion, AuxRandElements, EvaluationFrame, ProofOptions as WinterProofOptions, TransitionConstraintDegree, TraceInfo}; +use winter_math::fields::f64::BaseElement as Felt; +use winter_math::{ExtensionOf, FieldElement, ToElements}; +use winter_utils::{ByteWriter, Serializable}; + +pub struct PublicInputs { + stack_inputs: [Felt; 1], +} + +impl PublicInputs { + pub fn new(stack_inputs: [Felt; 1]) -> Self { + Self { stack_inputs } + } +} + +impl Serializable for PublicInputs { + fn write_into(&self, target: &mut W) { + self.stack_inputs.write_into(target); + } +} + +impl ToElements for PublicInputs { + fn to_elements(&self) -> Vec { + let mut elements = Vec::new(); + elements.extend_from_slice(&self.stack_inputs); + elements + } +} + +pub struct ComprehensionPeriodicBindingTest { + context: AirContext, + stack_inputs: [Felt; 1], +} + +impl ComprehensionPeriodicBindingTest { + pub fn last_step(&self) -> usize { + self.trace_length() - self.context().num_transition_exemptions() + } +} + +impl Air for ComprehensionPeriodicBindingTest { + type BaseField = Felt; + type PublicInputs = PublicInputs; + + fn context(&self) -> &AirContext { + &self.context + } + + fn new(trace_info: TraceInfo, public_inputs: PublicInputs, options: WinterProofOptions) -> Self { + let main_degrees = vec![TransitionConstraintDegree::with_cycles(1, vec![2, 2])]; + let aux_degrees = vec![]; + let num_main_assertions = 1; + let num_aux_assertions = 0; + + let context = AirContext::new_multi_segment( + trace_info, + main_degrees, + aux_degrees, + num_main_assertions, + num_aux_assertions, + options, + ) + .set_num_transition_exemptions(2); + Self { context, stack_inputs: public_inputs.stack_inputs } + } + + fn get_periodic_column_values(&self) -> Vec> { + vec![vec![Felt::ONE, Felt::new(2)], vec![Felt::new(3), Felt::new(4)]] + } + + fn get_assertions(&self) -> Vec> { + let mut result = Vec::new(); + result.push(Assertion::single(0, 0, Felt::ZERO)); + result + } + + fn get_aux_assertions>(&self, aux_rand_elements: &AuxRandElements) -> Vec> { + let mut result = Vec::new(); + result + } + + fn evaluate_transition>(&self, frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E]) { + let main_current = frame.current(); + let main_next = frame.next(); + result[0] = main_next[0] - (main_current[0] * periodic_values[0] + main_current[1] * periodic_values[1]); + } + + fn evaluate_aux_transition(&self, main_frame: &EvaluationFrame, aux_frame: &EvaluationFrame, _periodic_values: &[F], aux_rand_elements: &AuxRandElements, result: &mut [E]) + where F: FieldElement, + E: FieldElement + ExtensionOf, + { + let main_current = main_frame.current(); + let main_next = main_frame.next(); + let aux_current = aux_frame.current(); + let aux_next = aux_frame.next(); + } +} \ No newline at end of file diff --git a/air-script/tests/comprehension_periodic_binding/lib.air b/air-script/tests/comprehension_periodic_binding/lib.air new file mode 100644 index 000000000..8667dc9ae --- /dev/null +++ b/air-script/tests/comprehension_periodic_binding/lib.air @@ -0,0 +1,16 @@ +mod lib + +periodic_columns { + k0: [1, 2], + k1: [3, 4], +} + +ev test_comprehension([a, b]) { + # Create local variable holding periodic column references + let cols = [k0, k1]; + let vals = [a, b]; + + # Iterate over the local variable - binding 'k' gets typed as PeriodicColumn + # but it's actually a local variable holding a value + enf a' = sum([x * k for (x, k) in (vals, cols)]); +} diff --git a/air-script/tests/comprehension_periodic_binding/mod.rs b/air-script/tests/comprehension_periodic_binding/mod.rs new file mode 100644 index 000000000..83244b98e --- /dev/null +++ b/air-script/tests/comprehension_periodic_binding/mod.rs @@ -0,0 +1,3 @@ +#[rustfmt::skip] +#[allow(clippy::all)] +mod comprehension_periodic_binding; From 0196da47f9672f3a7faab9e3f2208baaf1c4435c Mon Sep 17 00:00:00 2001 From: al <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:41:59 +0400 Subject: [PATCH 32/83] fix: Convert comprehension bindings to Local type for proper scoping --- parser/src/sema/semantic_analysis.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/parser/src/sema/semantic_analysis.rs b/parser/src/sema/semantic_analysis.rs index 8bda93fc3..b4bf9597e 100644 --- a/parser/src/sema/semantic_analysis.rs +++ b/parser/src/sema/semantic_analysis.rs @@ -633,6 +633,15 @@ impl VisitMut for SemanticAnalysis<'_> { 0, ))))) .expect("unexpected scalar iterable"); + // Comprehension bindings are local variables holding values, not direct + // references to module-level declarations like periodic columns or constants. + // Convert these to Local bindings to ensure proper scoping. + let binding_ty = match binding_ty { + BindingType::PeriodicColumn(_) | BindingType::Constant(_) => { + BindingType::Local(binding_ty.ty().unwrap_or(Type::Felt)) + }, + other => other, + }; binding_tys.push((binding, iterable.span(), Some(binding_ty))); }, Err(InvalidAccessError::InvalidBinding) => { From 53968fe5e92a0dc2c75149a36a9bcf97edd80f57 Mon Sep 17 00:00:00 2001 From: al <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 19:19:33 +0400 Subject: [PATCH 33/83] misc. fixes --- air-script/src/tests/mod.rs | 8 ++++---- air-script/tests/codegen/winterfell.rs | 16 +++++++++------- ...odule_constants.rs => cross_mod_constants.rs} | 0 air-script/tests/cross_module_constants/mod.rs | 4 +++- mir/src/passes/translate.rs | 9 +++++---- parser/src/ast/expression.rs | 6 +++--- parser/src/ast/trace.rs | 2 +- parser/src/lexer/mod.rs | 7 +++---- parser/src/parser/grammar.lalrpop | 4 ++-- parser/src/parser/tests/mod.rs | 4 ++-- 10 files changed, 32 insertions(+), 28 deletions(-) rename air-script/tests/cross_module_constants/{cross_module_constants.rs => cross_mod_constants.rs} (100%) diff --git a/air-script/src/tests/mod.rs b/air-script/src/tests/mod.rs index 587fb1552..75bd5f39a 100644 --- a/air-script/src/tests/mod.rs +++ b/air-script/src/tests/mod.rs @@ -5,18 +5,18 @@ mod bitwise; #[allow(unused_variables, dead_code, unused_mut)] mod buses; #[allow(unused_variables, dead_code, unused_mut)] -mod computed_indices; -#[allow(unused_variables, dead_code, unused_mut)] mod comprehension_periodic_binding; #[allow(unused_variables, dead_code, unused_mut)] +mod computed_indices; +#[allow(unused_variables, dead_code, unused_mut)] mod constant_in_range; #[allow(unused_variables, dead_code, unused_mut)] mod constants; #[allow(unused_variables, dead_code, unused_mut)] -mod cross_module_constants; -#[allow(unused_variables, dead_code, unused_mut)] mod constraint_comprehension; #[allow(unused_variables, dead_code, unused_mut)] +mod cross_module_constants; +#[allow(unused_variables, dead_code, unused_mut)] mod evaluators; #[allow(unused_variables, dead_code, unused_mut)] mod fibonacci; diff --git a/air-script/tests/codegen/winterfell.rs b/air-script/tests/codegen/winterfell.rs index 3e8e30911..efd1538d2 100644 --- a/air-script/tests/codegen/winterfell.rs +++ b/air-script/tests/codegen/winterfell.rs @@ -374,7 +374,7 @@ fn cross_module_constants() { .transpile(Target::Winterfell) .unwrap(); - let expected = expect_file!["../cross_module_constants/cross_module_constants.rs"]; + let expected = expect_file!["../cross_module_constants/cross_mod_constants.rs"]; expected.assert_eq(&generated_air); } @@ -382,11 +382,13 @@ fn cross_module_constants() { fn comprehension_periodic_binding() { // Test that comprehension bindings over periodic columns are typed as Local, not PeriodicColumn // This pattern is used when iterating over a vector containing periodic column references - let generated_air = - Test::new("tests/comprehension_periodic_binding/comprehension_periodic_binding.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../comprehension_periodic_binding/comprehension_periodic_binding.rs"]; + let generated_air = Test::new( + "tests/comprehension_periodic_binding/comprehension_periodic_binding.air".to_string(), + ) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = + expect_file!["../comprehension_periodic_binding/comprehension_periodic_binding.rs"]; expected.assert_eq(&generated_air); } diff --git a/air-script/tests/cross_module_constants/cross_module_constants.rs b/air-script/tests/cross_module_constants/cross_mod_constants.rs similarity index 100% rename from air-script/tests/cross_module_constants/cross_module_constants.rs rename to air-script/tests/cross_module_constants/cross_mod_constants.rs diff --git a/air-script/tests/cross_module_constants/mod.rs b/air-script/tests/cross_module_constants/mod.rs index fa516c053..e10ee53c7 100644 --- a/air-script/tests/cross_module_constants/mod.rs +++ b/air-script/tests/cross_module_constants/mod.rs @@ -1 +1,3 @@ -mod cross_module_constants; +#[rustfmt::skip] +#[allow(clippy::all)] +mod cross_mod_constants; diff --git a/mir/src/passes/translate.rs b/mir/src/passes/translate.rs index 6a631fe50..e1e54acf9 100644 --- a/mir/src/passes/translate.rs +++ b/mir/src/passes/translate.rs @@ -695,7 +695,7 @@ impl<'a> MirBuilder<'a> { // At this point during compilation, fully-qualified identifiers can only possibly refer // to a periodic column, as all functions have been inlined, and constants propagated. ast::ResolvableIdentifier::Resolved(qual_ident) => { - if let Some(pc) = self.mir.periodic_columns.get(&qual_ident).cloned() { + if let Some(pc) = self.mir.periodic_columns.get(qual_ident).cloned() { let node = Value::builder() .value(SpannedMirValue { span: access.span(), @@ -706,7 +706,7 @@ impl<'a> MirBuilder<'a> { }) .build(); Ok(node) - } else if let Some(bus) = self.mir.constraint_graph().get_bus_link(&qual_ident) { + } else if let Some(bus) = self.mir.constraint_graph().get_bus_link(qual_ident) { let node = Value::builder() .value(SpannedMirValue { span: access.span(), @@ -739,7 +739,7 @@ impl<'a> MirBuilder<'a> { }, // This must be one of public inputs or trace columns ast::ResolvableIdentifier::Global(ident) | ast::ResolvableIdentifier::Local(ident) => { - self.translate_symbol_access_global_or_local(&ident, access) + self.translate_symbol_access_global_or_local(ident, access) }, // These should have been eliminated by previous compiler passes ast::ResolvableIdentifier::Unresolved(_ident) => { @@ -1067,7 +1067,8 @@ impl<'a> MirBuilder<'a> { access: &'a ast::SymbolAccess, ) -> Option> { // If it's a slice access, we need to create a vector of MirAccessType::Index - if let AccessType::Slice(ast::RangeExpr { start, end, .. }) = &access.access_type { + if let AccessType::Slice(range) = &access.access_type { + let ast::RangeExpr { start, end, .. } = range.as_ref(); let ( ast::RangeBound::Const(Span { item: start, .. }), ast::RangeBound::Const(Span { item: end, .. }), diff --git a/parser/src/ast/expression.rs b/parser/src/ast/expression.rs index 97f26fa94..1ad12bb80 100644 --- a/parser/src/ast/expression.rs +++ b/parser/src/ast/expression.rs @@ -865,7 +865,7 @@ pub enum AccessType { #[default] Default, /// Access binds a sub-slice of a vector - Slice(RangeExpr), + Slice(Box), /// Access binds the value at a specific index of an aggregate value (i.e. vector or matrix) /// /// The result type may be either a scalar or a vector, depending on the type of the aggregate @@ -1073,7 +1073,7 @@ impl SymbolAccess { Err(InvalidAccessError::IndexOutOfBounds) }, Type::Vector(_) => Ok(Self { - access_type: AccessType::Slice(shifted), + access_type: AccessType::Slice(Box::new(shifted.clone())), ty: Some(Type::Vector(rlen)), ..self.clone() }), @@ -1081,7 +1081,7 @@ impl SymbolAccess { Err(InvalidAccessError::IndexOutOfBounds) }, Type::Matrix(_, cols) => Ok(Self { - access_type: AccessType::Slice(shifted), + access_type: AccessType::Slice(Box::new(shifted)), ty: Some(Type::Matrix(rlen, cols)), ..self.clone() }), diff --git a/parser/src/ast/trace.rs b/parser/src/ast/trace.rs index 5b26a05d0..71fab9071 100644 --- a/parser/src/ast/trace.rs +++ b/parser/src/ast/trace.rs @@ -287,7 +287,7 @@ impl TraceBinding { let range_expr1 = range_expr1.to_slice_range(); let combined_range = (range_expr.start + range_expr1.start)..(range_expr.end + range_expr1.end); - AccessType::Slice(combined_range.into()) + AccessType::Slice(Box::new(combined_range.into())) }, (AccessType::Slice(range_expr), AccessType::Index(index_expr)) => { let range_expr_usize = range_expr.to_slice_range(); diff --git a/parser/src/lexer/mod.rs b/parser/src/lexer/mod.rs index f354cf6f4..8265300ae 100644 --- a/parser/src/lexer/mod.rs +++ b/parser/src/lexer/mod.rs @@ -644,10 +644,9 @@ where match num.parse::() { Ok(i) => Token::Num(i), - Err(err) => Token::Error(LexicalError::InvalidInt { - span: self.span(), - reason: err.kind().clone(), - }), + Err(err) => { + Token::Error(LexicalError::InvalidInt { span: self.span(), reason: *err.kind() }) + }, } } } diff --git a/parser/src/parser/grammar.lalrpop b/parser/src/parser/grammar.lalrpop index b06578f68..e05958009 100644 --- a/parser/src/parser/grammar.lalrpop +++ b/parser/src/parser/grammar.lalrpop @@ -560,7 +560,7 @@ SymbolAccessBaseSpanned: Span<(Identifier, AccessType)> = { SymbolAccessBase: (Identifier, AccessType) = { => (ident, AccessType::Default), - "[" "]" => (ident, AccessType::Slice(range)), + "[" "]" => (ident, AccessType::Slice(Box::new(range))), => (ident, AccessType::Index(idx)), => (ident, AccessType::Matrix(row, col)), // accessing an identifier used in a section declaration, like a named trace segment, e.g. $main @@ -611,7 +611,7 @@ Iterables: Vec = { Iterable: Expr = { => Expr::SymbolAccess(SymbolAccess::new(ident.span(), ident, AccessType::Default, 0)), => Expr::Range(range), - "[" "]" => Expr::SymbolAccess(SymbolAccess::new(span!(l, r), ident, AccessType::Slice(range), 0)), + "[" "]" => Expr::SymbolAccess(SymbolAccess::new(span!(l, r), ident, AccessType::Slice(Box::new(range)), 0)), => if let ScalarExpr::Call(call) = function_call { Expr::Call(call) } else { diff --git a/parser/src/parser/tests/mod.rs b/parser/src/parser/tests/mod.rs index aa1a162e1..1b18c60fc 100644 --- a/parser/src/parser/tests/mod.rs +++ b/parser/src/parser/tests/mod.rs @@ -426,7 +426,7 @@ macro_rules! slice { ScalarExpr::SymbolAccess(SymbolAccess { span: miden_diagnostics::SourceSpan::UNKNOWN, name: ResolvableIdentifier::Unresolved(NamespacedIdentifier::Binding(ident!($name))), - access_type: AccessType::Slice($range.into()), + access_type: AccessType::Slice(Box::new($range.into())), offset: 0, ty: None, }) @@ -436,7 +436,7 @@ macro_rules! slice { ScalarExpr::SymbolAccess(SymbolAccess { span: miden_diagnostics::SourceSpan::UNKNOWN, name: ResolvableIdentifier::Local(ident!($name)), - access_type: AccessType::Slice($range.into()), + access_type: AccessType::Slice(Box::new($range.into())), offset: 0, ty: Some($ty), }) From bf52a32d65e57ad2c2cdad10385e586935763dbc Mon Sep 17 00:00:00 2001 From: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 20:06:35 +0400 Subject: [PATCH 34/83] Add Kernel ROM chiplet constraints (#484) --- constraints/chiplets.air | 12 +++++- constraints/kernel_rom.air | 77 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 constraints/kernel_rom.air diff --git a/constraints/chiplets.air b/constraints/chiplets.air index 77cec26d0..37d6a6b9b 100644 --- a/constraints/chiplets.air +++ b/constraints/chiplets.air @@ -35,6 +35,9 @@ use memory::flag_memory_active_not_last_row; use memory::flag_next_row_first_row_memory; use memory::memory_chiplet_constraints_first_row; +use kernel_rom::ker_rom_chiplet_constraints; +use kernel_rom::kernel_rom_chiplet_constraints_first_row; + use utils::*; ########################################################################################## @@ -69,7 +72,7 @@ ev chiplets_constraints([chiplets[20]]) { case bitwise_active: bitwise_chiplet_constraints([chiplets[2..15]]), case memory_active: memory_chiplet_constraints_all_rows([chiplets[3..18]]), case ace_active: ace_chiplet_constraints_all_rows([chiplets[3..20]]), - #case ker_rom_active: ker_rom_chiplet_constraints([chiplets[5..20]]), + case ker_rom_active: ker_rom_chiplet_constraints([chiplets[4..10]]), }; # CHIPLET CONSTRAINTS REQUIRING SPECIAL HANDLING @@ -95,6 +98,13 @@ ev chiplets_constraints([chiplets[20]]) { let next_row_first_ace = binary_and(memory_active, s2'); # Transitioning into ACE chiplet enf ace_chiplet_constraints_first_row([chiplets[4..20]]) when next_row_first_ace; + ## KERNEL ROM + + ### The kernel ROM chiplet requires initialization constraints to ensure proper startup. + ### We force sfirst' = 1 on the first kernel ROM row, establishing proper digest initialization. + let next_row_first_kernel_rom = binary_and(ace_active, s3'); # Transitioning into kernel ROM chiplet + enf kernel_rom_chiplet_constraints_first_row([chiplets[4..10]]) when next_row_first_kernel_rom; + } ########################################################################################## diff --git a/constraints/kernel_rom.air b/constraints/kernel_rom.air new file mode 100644 index 000000000..49b83f463 --- /dev/null +++ b/constraints/kernel_rom.air @@ -0,0 +1,77 @@ +########################################################################################## +# KERNEL ROM CHIPLET +########################################################################################## +# +# The Kernel ROM chiplet is responsible for tracking execution of kernel (system) calls. +# It maintains a record of all kernel procedure digests and ensures proper initialization +# of kernel procedures while preserving privacy about call frequency. +# +# The chiplet contains digest values for all kernel procedures and enforces contiguity +# constraints to ensure proper execution tracking. +# +# STATUS: Core constraints implemented, bus constraints not implemented +# +# REFERENCES: +# - Kernel ROM Chiplet: https://0xmiden.github.io/miden-vm/design/chiplets/kernel_rom.html +########################################################################################## + +mod kernel_rom + +use utils::*; + +########################################################################################## +# KERNEL ROM CHIPLET TRANSITION CONSTRAINTS +########################################################################################## + +# Enforces the constraints on the kernel ROM chiplet given its columns. +# +# Parameters: +# - s4: Chiplet selector flag +# - sfirst: Section first flag (1 = start of new digest block, 0 = continuation of a given block) +# - r[4]: Kernel procedure root/digest (4 field elements) +# +# Max constraint degree: 3 +ev ker_rom_chiplet_constraints([s4, sfirst, r[4]]) { + enf kernel_rom_selector([sfirst]); + enf kernel_rom_digest_contiguity([s4, sfirst, r]); +} + +# Enforces initialization constraints for the first row of kernel ROM chiplet execution. +# +# According to the official specification, the first row must have sfirst = 1 to ensure +# proper digest matching and initialization of kernel procedure tracking. +# +# Max constraint degree: 1 +ev kernel_rom_chiplet_constraints_first_row([s4, sfirst, r[4]]) { + # FIRST ROW INITIALIZATION CONSTRAINT: + # The first row of any kernel ROM chiplet execution must have sfirst' = 1 + # This ensures proper initialization of kernel procedure digest tracking. + # Without this, the chiplet could start in an invalid intermediate state. + enf sfirst' = 1; +} + +########################################################################################## +# HELPERS +########################################################################################## + +# Enforces that the kernel ROM selector is valid. +# +# Max constraint degree: 2 +ev kernel_rom_selector([sfirst]) { + # Enforce that sfirst is binary (0 or 1) + enf is_binary([sfirst]); +} + +# Enforces digest contiguity constraints for the kernel ROM. +# +# When sfirst' = 0 (not starting a new digest block), the digest values must remain +# unchanged from the current row to the next row. This ensures contiguous blocks of +# identical digest values for proper tracking of kernel procedure executions. +# +# Max constraint degree: 3 +ev kernel_rom_digest_contiguity([s4, sfirst, r[4]]) { + # Constraint is active when: + # - sfirst' = 0 (next row is not the start of a new digest block) + # - s4' = 0 (next row is still within the kernel ROM chiplet) + enf is_unchanged([r_i]) for r_i in r when binary_and(binary_not(s4'), binary_not(sfirst')); +} From 578816ea0e8f8e1d1dd7bfa40b29d49cda284d75 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Wed, 7 Jan 2026 13:24:11 +0100 Subject: [PATCH 35/83] chore: cleanup tests path --- .../comprehension_periodic_binding.air | 0 .../comprehension_periodic_binding.rs | 0 .../comprehension_periodic_binding_plonky3.rs | 56 +++ .../comprehension_periodic_binding/lib.air | 0 .../comprehension_periodic_binding/mod.rs | 0 .../cross_module_constants/constants_lib.air | 0 .../cross_mod_constants.rs | 0 .../cross_mod_constants_plonky3.rs | 42 ++ .../cross_module_constants.air | 0 .../tests/cross_module_constants/mod.rs | 0 air-script/src/tests/plonky3.rs | 27 ++ air-script/src/tests/winterfell.rs | 27 ++ air-script/tests/codegen/winterfell.rs | 394 ------------------ 13 files changed, 152 insertions(+), 394 deletions(-) rename air-script/{ => src}/tests/comprehension_periodic_binding/comprehension_periodic_binding.air (100%) rename air-script/{ => src}/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs (100%) create mode 100644 air-script/src/tests/comprehension_periodic_binding/comprehension_periodic_binding_plonky3.rs rename air-script/{ => src}/tests/comprehension_periodic_binding/lib.air (100%) rename air-script/{ => src}/tests/comprehension_periodic_binding/mod.rs (100%) rename air-script/{ => src}/tests/cross_module_constants/constants_lib.air (100%) rename air-script/{ => src}/tests/cross_module_constants/cross_mod_constants.rs (100%) create mode 100644 air-script/src/tests/cross_module_constants/cross_mod_constants_plonky3.rs rename air-script/{ => src}/tests/cross_module_constants/cross_module_constants.air (100%) rename air-script/{ => src}/tests/cross_module_constants/mod.rs (100%) delete mode 100644 air-script/tests/codegen/winterfell.rs diff --git a/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.air b/air-script/src/tests/comprehension_periodic_binding/comprehension_periodic_binding.air similarity index 100% rename from air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.air rename to air-script/src/tests/comprehension_periodic_binding/comprehension_periodic_binding.air diff --git a/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs b/air-script/src/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs similarity index 100% rename from air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs rename to air-script/src/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs diff --git a/air-script/src/tests/comprehension_periodic_binding/comprehension_periodic_binding_plonky3.rs b/air-script/src/tests/comprehension_periodic_binding/comprehension_periodic_binding_plonky3.rs new file mode 100644 index 000000000..a095ec7f0 --- /dev/null +++ b/air-script/src/tests/comprehension_periodic_binding/comprehension_periodic_binding_plonky3.rs @@ -0,0 +1,56 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 2; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 2; +pub const PERIOD: usize = 2; +pub const NUM_PUBLIC_VALUES: usize = 1; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct ComprehensionPeriodicBindingTest; + +impl MidenAir for ComprehensionPeriodicBindingTest +where F: Field, + EF: ExtensionField, +{ + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn num_public_values(&self) -> usize { + NUM_PUBLIC_VALUES + } + + fn periodic_table(&self) -> Vec> { + vec![ + vec![F::from_u64(1), F::from_u64(2)], + vec![F::from_u64(3), F::from_u64(4)], + ] + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into()); + + // Main integrity/transition constraints + builder.when_transition().assert_zero_ext(AB::ExprEF::from(main_next[0].clone().into()) - (AB::ExprEF::from(main_current[0].clone().into()) * AB::ExprEF::from(periodic_values[0].clone().into()) + AB::ExprEF::from(main_current[1].clone().into()) * AB::ExprEF::from(periodic_values[1].clone().into()))); + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/comprehension_periodic_binding/lib.air b/air-script/src/tests/comprehension_periodic_binding/lib.air similarity index 100% rename from air-script/tests/comprehension_periodic_binding/lib.air rename to air-script/src/tests/comprehension_periodic_binding/lib.air diff --git a/air-script/tests/comprehension_periodic_binding/mod.rs b/air-script/src/tests/comprehension_periodic_binding/mod.rs similarity index 100% rename from air-script/tests/comprehension_periodic_binding/mod.rs rename to air-script/src/tests/comprehension_periodic_binding/mod.rs diff --git a/air-script/tests/cross_module_constants/constants_lib.air b/air-script/src/tests/cross_module_constants/constants_lib.air similarity index 100% rename from air-script/tests/cross_module_constants/constants_lib.air rename to air-script/src/tests/cross_module_constants/constants_lib.air diff --git a/air-script/tests/cross_module_constants/cross_mod_constants.rs b/air-script/src/tests/cross_module_constants/cross_mod_constants.rs similarity index 100% rename from air-script/tests/cross_module_constants/cross_mod_constants.rs rename to air-script/src/tests/cross_module_constants/cross_mod_constants.rs diff --git a/air-script/src/tests/cross_module_constants/cross_mod_constants_plonky3.rs b/air-script/src/tests/cross_module_constants/cross_mod_constants_plonky3.rs new file mode 100644 index 000000000..47dc1b334 --- /dev/null +++ b/air-script/src/tests/cross_module_constants/cross_mod_constants_plonky3.rs @@ -0,0 +1,42 @@ +use p3_field::{ExtensionField, Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrixView; +use p3_matrix::stack::VerticalPair; +use p3_miden_air::{BusType, MidenAir, MidenAirBuilder, RowMajorMatrix}; + +pub const MAIN_WIDTH: usize = 5; +pub const AUX_WIDTH: usize = 0; +pub const NUM_PERIODIC_VALUES: usize = 0; +pub const PERIOD: usize = 0; +pub const NUM_PUBLIC_VALUES: usize = 1; +pub const MAX_BETA_CHALLENGE_POWER: usize = 0; + +pub struct CrossModuleConstantsTest; + +impl MidenAir for CrossModuleConstantsTest { + fn width(&self) -> usize { + MAIN_WIDTH + } + + fn eval(&self, builder: &mut AB) + where AB: MidenAirBuilder, + { + let public_values: [_; NUM_PUBLIC_VALUES] = builder.public_values().try_into().expect("Wrong number of public values"); + let periodic_values: [_; NUM_PERIODIC_VALUES] = builder.periodic_evals().try_into().expect("Wrong number of periodic values"); + // Note: for now, we do not have any preprocessed values + // let preprocessed = builder.preprocessed(); + let main = builder.main(); + let (main_current, main_next) = ( + main.row_slice(0).unwrap(), + main.row_slice(1).unwrap(), + ); + + // Main boundary constraints + builder.when_first_row().assert_zero(main_current[0].clone().into()); + + // Main integrity/transition constraints + builder.assert_zero(main_current[4].clone().into() - (main_current[0].clone().into() + main_current[1].clone().into().double() + main_current[2].clone().into() * AB::Expr::from_u64(3) + main_current[3].clone().into() * AB::Expr::from_u64(4))); + + // Aux integrity/transition constraints + } +} \ No newline at end of file diff --git a/air-script/tests/cross_module_constants/cross_module_constants.air b/air-script/src/tests/cross_module_constants/cross_module_constants.air similarity index 100% rename from air-script/tests/cross_module_constants/cross_module_constants.air rename to air-script/src/tests/cross_module_constants/cross_module_constants.air diff --git a/air-script/tests/cross_module_constants/mod.rs b/air-script/src/tests/cross_module_constants/mod.rs similarity index 100% rename from air-script/tests/cross_module_constants/mod.rs rename to air-script/src/tests/cross_module_constants/mod.rs diff --git a/air-script/src/tests/plonky3.rs b/air-script/src/tests/plonky3.rs index baf9c3453..9a685d06d 100644 --- a/air-script/src/tests/plonky3.rs +++ b/air-script/src/tests/plonky3.rs @@ -81,6 +81,21 @@ fn buses_varlen_boundary_last() { expected.assert_eq(&generated_air); } +#[test] +fn comprehension_periodic_binding() { + // Test that comprehension bindings over periodic columns are typed as Local, not PeriodicColumn + // This pattern is used when iterating over a vector containing periodic column references + let generated_air = Test::new( + "src/tests/comprehension_periodic_binding/comprehension_periodic_binding.air".to_string(), + ) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = + expect_file!["comprehension_periodic_binding/comprehension_periodic_binding_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + #[test] fn computed_indices_complex() { let generated_air = @@ -123,6 +138,18 @@ fn constants() { expected.assert_eq(&generated_air); } +#[test] +fn cross_module_constants() { + // Test that constants used in comprehension iterables work across module boundaries + let generated_air = + Test::new("src/tests/cross_module_constants/cross_module_constants.air".to_string()) + .transpile(Target::Plonky3) + .unwrap(); + + let expected = expect_file!["cross_module_constants/cross_mod_constants_plonky3.rs"]; + expected.assert_eq(&generated_air); +} + #[test] fn evaluators_nested_slice_call() { let generated_air = diff --git a/air-script/src/tests/winterfell.rs b/air-script/src/tests/winterfell.rs index 5e9f044df..f7bc29ae2 100644 --- a/air-script/src/tests/winterfell.rs +++ b/air-script/src/tests/winterfell.rs @@ -82,6 +82,21 @@ fn buses_varlen_boundary_last() { expected.assert_eq(&generated_air); } +#[test] +fn comprehension_periodic_binding() { + // Test that comprehension bindings over periodic columns are typed as Local, not PeriodicColumn + // This pattern is used when iterating over a vector containing periodic column references + let generated_air = Test::new( + "src/tests/comprehension_periodic_binding/comprehension_periodic_binding.air".to_string(), + ) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = + expect_file!["comprehension_periodic_binding/comprehension_periodic_binding.rs"]; + expected.assert_eq(&generated_air); +} + #[test] fn computed_indices_complex() { let generated_air = @@ -143,6 +158,18 @@ fn constraint_comprehension() { expected.assert_eq(&generated_air); } +#[test] +fn cross_module_constants() { + // Test that constants used in comprehension iterables work across module boundaries + let generated_air = + Test::new("src/tests/cross_module_constants/cross_module_constants.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["cross_module_constants/cross_mod_constants.rs"]; + expected.assert_eq(&generated_air); +} + #[test] fn evaluators_nested_slice_call() { let generated_air = diff --git a/air-script/tests/codegen/winterfell.rs b/air-script/tests/codegen/winterfell.rs deleted file mode 100644 index efd1538d2..000000000 --- a/air-script/tests/codegen/winterfell.rs +++ /dev/null @@ -1,394 +0,0 @@ -use expect_test::expect_file; - -use super::helpers::{Target, Test}; - -#[test] -fn binary() { - let generated_air = Test::new("tests/binary/binary.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../binary/binary.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn bitwise() { - let generated_air = Test::new("tests/bitwise/bitwise.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../bitwise/bitwise.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_complex() { - let generated_air = Test::new("tests/buses/buses_complex.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../buses/buses_complex.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_simple() { - let generated_air = Test::new("tests/buses/buses_simple.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../buses/buses_simple.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_simple_with_evaluators() { - let generated_air = Test::new("tests/buses/buses_simple_with_evaluators.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../buses/buses_simple.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_varlen_boundary_both() { - let generated_air = Test::new("tests/buses/buses_varlen_boundary_both.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../buses/buses_varlen_boundary_both.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_varlen_boundary_first() { - let generated_air = Test::new("tests/buses/buses_varlen_boundary_first.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../buses/buses_varlen_boundary_first.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn buses_varlen_boundary_last() { - let generated_air = Test::new("tests/buses/buses_varlen_boundary_last.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../buses/buses_varlen_boundary_last.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn computed_indices_complex() { - let generated_air = - Test::new("tests/computed_indices/computed_indices_complex.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../computed_indices/computed_indices_complex.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn computed_indices_simple() { - let generated_air = Test::new("tests/computed_indices/computed_indices_simple.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../computed_indices/computed_indices_simple.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn constant_in_range() { - let generated_air = Test::new("tests/constant_in_range/constant_in_range.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../constant_in_range/constant_in_range.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn constants() { - let generated_air = Test::new("tests/constants/constants.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../constants/constants.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn constraint_comprehension() { - let generated_air = - Test::new("tests/constraint_comprehension/constraint_comprehension.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../constraint_comprehension/constraint_comprehension.rs"]; - expected.assert_eq(&generated_air); - - let generated_air = - Test::new("tests/constraint_comprehension/cc_with_evaluators.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../constraint_comprehension/constraint_comprehension.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn evaluators() { - let generated_air = Test::new("tests/evaluators/evaluators.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../evaluators/evaluators.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn evaluators_slice() { - let generated_air = Test::new("tests/evaluators/evaluators_slice.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../evaluators/evaluators_slice.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn evaluators_nested_slice_call() { - let generated_air = Test::new("tests/evaluators/evaluators_nested_slice_call.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../evaluators/evaluators_nested_slice_call.rs"]; - expected.assert_eq(&generated_air); -} - -// TODO: add support for nested slicing in general expressions. -// -// #[test] -// fn evaluators_slice_slicing() { -// let generated_air = Test::new("tests/evaluators/evaluators_slice_slicing.air".to_string()) -// .transpile(Target::Winterfell) -// .unwrap(); -// -// let expected = expect_file!["../evaluators/evaluators_slice_slicing.rs"]; -// expected.assert_eq(&generated_air); -// } - -#[test] -fn fibonacci() { - let generated_air = Test::new("tests/fibonacci/fibonacci.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../fibonacci/fibonacci.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn functions_complex() { - let generated_air = Test::new("tests/functions/functions_complex.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../functions/functions_complex.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn functions_simple() { - let generated_air = Test::new("tests/functions/functions_simple.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../functions/functions_simple.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn functions_simple_inlined() { - // make sure that the constraints generated using inlined functions are the same as the ones - // generated using regular functions - let generated_air = Test::new("tests/functions/inlined_functions_simple.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../functions/functions_simple.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn indexed_trace_access() { - let generated_air = - Test::new("tests/indexed_trace_access/indexed_trace_access.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../indexed_trace_access/indexed_trace_access.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn list_comprehension() { - let generated_air = Test::new("tests/list_comprehension/list_comprehension.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../list_comprehension/list_comprehension.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn list_comprehension_nested() { - let generated_air = - Test::new("tests/list_comprehension/list_comprehension_nested.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../list_comprehension/list_comprehension_nested.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn list_folding() { - let generated_air = Test::new("tests/list_folding/list_folding.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../list_folding/list_folding.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn periodic_columns() { - let generated_air = Test::new("tests/periodic_columns/periodic_columns.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../periodic_columns/periodic_columns.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn pub_inputs() { - let generated_air = Test::new("tests/pub_inputs/pub_inputs.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../pub_inputs/pub_inputs.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn selectors() { - let generated_air = Test::new("tests/selectors/selectors.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../selectors/selectors.rs"]; - expected.assert_eq(&generated_air); - - let generated_air = Test::new("tests/selectors/selectors_with_evaluators.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../selectors/selectors_with_evaluators.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn selectors_combine_simple() { - let generated_air = Test::new("tests/selectors/selectors_combine_simple.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../selectors/selectors_combine_simple.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn selectors_combine_complex() { - let generated_air = Test::new("tests/selectors/selectors_combine_complex.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../selectors/selectors_combine_complex.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn selectors_combine_with_list_comprehensions() { - let generated_air = - Test::new("tests/selectors/selectors_combine_with_list_comprehensions.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../selectors/selectors_combine_with_list_comprehensions.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn system() { - let generated_air = Test::new("tests/system/system.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../system/system.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn trace_col_groups() { - let generated_air = Test::new("tests/trace_col_groups/trace_col_groups.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../trace_col_groups/trace_col_groups.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn variables() { - let generated_air = Test::new("tests/variables/variables.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../variables/variables.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn cross_module_constants() { - // Test that constants used in comprehension iterables work across module boundaries - let generated_air = - Test::new("tests/cross_module_constants/cross_module_constants.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../cross_module_constants/cross_mod_constants.rs"]; - expected.assert_eq(&generated_air); -} - -#[test] -fn comprehension_periodic_binding() { - // Test that comprehension bindings over periodic columns are typed as Local, not PeriodicColumn - // This pattern is used when iterating over a vector containing periodic column references - let generated_air = Test::new( - "tests/comprehension_periodic_binding/comprehension_periodic_binding.air".to_string(), - ) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = - expect_file!["../comprehension_periodic_binding/comprehension_periodic_binding.rs"]; - expected.assert_eq(&generated_air); -} From b8053f21712d51edfa499a18df1dbdfbff5188b3 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Thu, 22 Jan 2026 08:08:33 +0100 Subject: [PATCH 36/83] chore: Update p3-miden dependancies --- air-script/Cargo.toml | 14 ++++---------- .../src/tests/buses/buses_complex_plonky3.rs | 4 ++-- air-script/src/tests/buses/buses_simple_plonky3.rs | 4 ++-- .../buses/buses_varlen_boundary_both_plonky3.rs | 4 ++-- .../buses/buses_varlen_boundary_first_plonky3.rs | 4 ++-- .../buses/buses_varlen_boundary_last_plonky3.rs | 4 ++-- .../selectors/selectors_combine_complex_plonky3.rs | 4 ++-- air-script/src/tests/winterfell.rs | 3 +-- codegen/plonky3/src/air/mod.rs | 4 ++-- 9 files changed, 19 insertions(+), 26 deletions(-) diff --git a/air-script/Cargo.toml b/air-script/Cargo.toml index 7e05c4e4b..ec4ff0fc9 100644 --- a/air-script/Cargo.toml +++ b/air-script/Cargo.toml @@ -32,9 +32,7 @@ mir = { package = "air-mir", path = "../mir", version = "0.5" } p3-field = { version = "0.4.2", default-features = false } p3-matrix = { version = "0.4.2", default-features = false } # Internal p3-miden crates -#p3-miden-air = { version = "0.4.0", default-features = false } -# Internal p3-miden crates -p3-miden-air = { package = "p3-miden-air", path = "../../p3-miden/p3-miden-air", default-features = false } +p3-miden-air = { package = "p3-miden-air", git = "https://github.com/0xMiden/p3-miden", rev = "b6cb824d2a462bbcbbfb828c158de220aa7ca11f", default-features = false } # MassaLabs fork miden-processor = { package = "miden-processor", git="https://github.com/massalabs/miden-vm", rev = "bc553af69a2543a0789830e8508b019694528181", default-features = false } @@ -67,13 +65,9 @@ p3-symmetric = { version = "0.4.2", default-features = false } p3-uni-stark = { version = "0.4.2", default-features = false } p3-util = { version = "0.4.2", default-features = false } # Internal p3-miden crates -#p3-miden-fri = { package = "p3-miden-fri", git = "https://github.com/0xMiden/p3-miden", rev = "134c14d3b438e3c077cd0fba28903d41fe189e52", default-features = false } -#p3-miden-prover = { package = "p3-miden-prover", git = "https://github.com/0xMiden/p3-miden", rev = "134c14d3b438e3c077cd0fba28903d41fe189e52", default-features = false } -#p3-miden-uni-stark = { package = "p3-miden-uni-stark", git = "https://github.com/0xMiden/p3-miden", rev = "134c14d3b438e3c077cd0fba28903d41fe189e52", default-features = false } -# Internal p3-miden crates -p3-miden-fri = { package = "p3-miden-fri", path = "../../p3-miden/p3-miden-fri", default-features = false } -p3-miden-prover = { package = "p3-miden-prover", path = "../../p3-miden/p3-miden-prover", default-features = false } -p3-miden-uni-stark = { package = "p3-miden-uni-stark", path = "../../p3-miden/p3-miden-uni-stark", default-features = false } +p3-miden-fri = { package = "p3-miden-fri", git = "https://github.com/0xMiden/p3-miden", rev = "b6cb824d2a462bbcbbfb828c158de220aa7ca11f", default-features = false } +p3-miden-prover = { package = "p3-miden-prover", git = "https://github.com/0xMiden/p3-miden", rev = "b6cb824d2a462bbcbbfb828c158de220aa7ca11f", default-features = false } +p3-miden-uni-stark = { package = "p3-miden-uni-stark", git = "https://github.com/0xMiden/p3-miden", rev = "b6cb824d2a462bbcbbfb828c158de220aa7ca11f", default-features = false } winter-air = { package = "winter-air", version = "0.12", default-features = false } winter-math = { package = "winter-math", version = "0.12", default-features = false } diff --git a/air-script/src/tests/buses/buses_complex_plonky3.rs b/air-script/src/tests/buses/buses_complex_plonky3.rs index 2d038524d..09b4d81c3 100644 --- a/air-script/src/tests/buses/buses_complex_plonky3.rs +++ b/air-script/src/tests/buses/buses_complex_plonky3.rs @@ -29,8 +29,8 @@ where F: Field, AUX_WIDTH } - fn bus_types(&self) -> Vec { - vec![ + fn bus_types(&self) -> &[BusType] { + &[ BusType::Multiset, BusType::Logup, ] diff --git a/air-script/src/tests/buses/buses_simple_plonky3.rs b/air-script/src/tests/buses/buses_simple_plonky3.rs index 65efcf8fb..9644511c6 100644 --- a/air-script/src/tests/buses/buses_simple_plonky3.rs +++ b/air-script/src/tests/buses/buses_simple_plonky3.rs @@ -29,8 +29,8 @@ where F: Field, AUX_WIDTH } - fn bus_types(&self) -> Vec { - vec![ + fn bus_types(&self) -> &[BusType] { + &[ BusType::Multiset, BusType::Logup, ] diff --git a/air-script/src/tests/buses/buses_varlen_boundary_both_plonky3.rs b/air-script/src/tests/buses/buses_varlen_boundary_both_plonky3.rs index b32d738e1..34e3ee98b 100644 --- a/air-script/src/tests/buses/buses_varlen_boundary_both_plonky3.rs +++ b/air-script/src/tests/buses/buses_varlen_boundary_both_plonky3.rs @@ -29,8 +29,8 @@ where F: Field, AUX_WIDTH } - fn bus_types(&self) -> Vec { - vec![ + fn bus_types(&self) -> &[BusType] { + &[ BusType::Multiset, BusType::Logup, ] diff --git a/air-script/src/tests/buses/buses_varlen_boundary_first_plonky3.rs b/air-script/src/tests/buses/buses_varlen_boundary_first_plonky3.rs index b32d738e1..34e3ee98b 100644 --- a/air-script/src/tests/buses/buses_varlen_boundary_first_plonky3.rs +++ b/air-script/src/tests/buses/buses_varlen_boundary_first_plonky3.rs @@ -29,8 +29,8 @@ where F: Field, AUX_WIDTH } - fn bus_types(&self) -> Vec { - vec![ + fn bus_types(&self) -> &[BusType] { + &[ BusType::Multiset, BusType::Logup, ] diff --git a/air-script/src/tests/buses/buses_varlen_boundary_last_plonky3.rs b/air-script/src/tests/buses/buses_varlen_boundary_last_plonky3.rs index dba927c11..39e943a34 100644 --- a/air-script/src/tests/buses/buses_varlen_boundary_last_plonky3.rs +++ b/air-script/src/tests/buses/buses_varlen_boundary_last_plonky3.rs @@ -29,8 +29,8 @@ where F: Field, AUX_WIDTH } - fn bus_types(&self) -> Vec { - vec![ + fn bus_types(&self) -> &[BusType] { + &[ BusType::Multiset, BusType::Logup, ] diff --git a/air-script/src/tests/selectors/selectors_combine_complex_plonky3.rs b/air-script/src/tests/selectors/selectors_combine_complex_plonky3.rs index a89f39d38..8bebdce34 100644 --- a/air-script/src/tests/selectors/selectors_combine_complex_plonky3.rs +++ b/air-script/src/tests/selectors/selectors_combine_complex_plonky3.rs @@ -29,8 +29,8 @@ where F: Field, AUX_WIDTH } - fn bus_types(&self) -> Vec { - vec![ + fn bus_types(&self) -> &[BusType] { + &[ BusType::Multiset, ] } diff --git a/air-script/src/tests/winterfell.rs b/air-script/src/tests/winterfell.rs index f7bc29ae2..1c2b0b6af 100644 --- a/air-script/src/tests/winterfell.rs +++ b/air-script/src/tests/winterfell.rs @@ -92,8 +92,7 @@ fn comprehension_periodic_binding() { .transpile(Target::Winterfell) .unwrap(); - let expected = - expect_file!["comprehension_periodic_binding/comprehension_periodic_binding.rs"]; + let expected = expect_file!["comprehension_periodic_binding/comprehension_periodic_binding.rs"]; expected.assert_eq(&generated_air); } diff --git a/codegen/plonky3/src/air/mod.rs b/codegen/plonky3/src/air/mod.rs index 32a585ce9..aa38b8bd0 100644 --- a/codegen/plonky3/src/air/mod.rs +++ b/codegen/plonky3/src/air/mod.rs @@ -118,8 +118,8 @@ fn add_air_struct(scope: &mut Scope, ir: &Air, name: &str) { miden_air_impl.new_fn("aux_width").arg_ref_self().ret("usize").line("AUX_WIDTH"); - let bus_types_fn = miden_air_impl.new_fn("bus_types").arg_ref_self().ret("Vec"); - bus_types_fn.line("vec!["); + let bus_types_fn = miden_air_impl.new_fn("bus_types").arg_ref_self().ret("&[BusType]"); + bus_types_fn.line("&["); for (_id, bus) in &ir.buses { let bus_type_str = match bus.bus_type { air_ir::BusType::Multiset => "BusType::Multiset", From 4d00cfea67c8afe1bfbb79a19d7e8207f34f2732 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Thu, 8 Jan 2026 18:43:29 +0100 Subject: [PATCH 37/83] tests(compare-wf-p3): compare eval test harness on all frames for plonly3 and winterfell --- air-script/Cargo.toml | 1 + .../test_utils/cross_backend_comparison.rs | 528 ++++++++++++++++++ air-script/src/test_utils/mod.rs | 2 + air-script/src/tests/comparison/mod.rs | 4 + air-script/src/tests/mod.rs | 1 + 5 files changed, 536 insertions(+) create mode 100644 air-script/src/test_utils/cross_backend_comparison.rs create mode 100644 air-script/src/tests/comparison/mod.rs diff --git a/air-script/Cargo.toml b/air-script/Cargo.toml index ec4ff0fc9..cb3346966 100644 --- a/air-script/Cargo.toml +++ b/air-script/Cargo.toml @@ -40,6 +40,7 @@ miden-air = { package = "miden-air", git="https://github.com/massalabs/miden-vm" [dev-dependencies] expect-test = "1.4" +pretty_assertions = "1.4" # Upstream Plonky3 dependencies p3-air = { version = "0.4.2", default-features = false } diff --git a/air-script/src/test_utils/cross_backend_comparison.rs b/air-script/src/test_utils/cross_backend_comparison.rs new file mode 100644 index 000000000..6affd8e69 --- /dev/null +++ b/air-script/src/test_utils/cross_backend_comparison.rs @@ -0,0 +1,528 @@ +//! Cross-backend constraint evaluation comparison utilities. +//! +//! This module provides infrastructure for comparing constraint evaluations between +//! Winterfell and Plonky3 backends. It verifies that both backends produce equivalent +//! constraint evaluation results for the same trace data. +//! +//! # Design +//! +//! The comparison works by: +//! 1. Building the same trace data for both backends +//! 2. Evaluating all constraints at each row using both backends +//! 3. Comparing the canonical u64 representations of the results +//! +//! For Plonky3, we use a `ConstraintCapturingBuilder` that implements `MidenAirBuilder` +//! and captures all values passed to `assert_zero` calls. +//! +//! For Winterfell, we directly call `evaluate_transition` and manually evaluate boundary +//! constraints to produce comparable results. + +use std::fmt; + +use p3_field::{Field, PrimeCharacteristicRing, PrimeField64}; +use p3_goldilocks::Goldilocks; +use p3_matrix::{Matrix, dense::RowMajorMatrix}; +use p3_miden_air::MidenAirBuilder; +use winter_air::{Air, EvaluationFrame}; +use winter_math::{FieldElement, fields::f64::BaseElement as WinterfellFelt}; + +// ============================================================================ +// Canonical u64 Conversion +// ============================================================================ + +/// Trait for converting field elements to their canonical u64 representation. +/// +/// This is used for comparing field elements across different backends that may +/// use different internal representations (e.g., Montgomery form vs raw). +pub trait ToCanonicalU64 { + fn to_canonical_u64(&self) -> u64; +} + +impl ToCanonicalU64 for WinterfellFelt { + fn to_canonical_u64(&self) -> u64 { + self.as_int() + } +} + +impl ToCanonicalU64 for Goldilocks { + fn to_canonical_u64(&self) -> u64 { + self.as_canonical_u64() + } +} + +// ============================================================================ +// Constraint Mismatch Reporting +// ============================================================================ + +/// Represents a single constraint evaluation mismatch between backends. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConstraintMismatch { + pub row: usize, + pub constraint_index: usize, + pub winterfell_value: u64, + pub plonky3_value: u64, +} + +impl fmt::Display for ConstraintMismatch { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Row {}, Constraint {}: Winterfell={}, Plonky3={}", + self.row, self.constraint_index, self.winterfell_value, self.plonky3_value + ) + } +} + +/// Result of comparing constraint evaluations between backends. +#[derive(Debug)] +pub struct ComparisonResult { + pub mismatches: Vec, + pub total_rows: usize, + pub total_constraints_checked: usize, +} + +impl ComparisonResult { + pub fn is_ok(&self) -> bool { + self.mismatches.is_empty() + } + + /// Formats the comparison result for display. + pub fn format_report(&self) -> String { + if self.is_ok() { + return format!( + "All constraints match! Checked {} constraints across {} rows.", + self.total_constraints_checked, self.total_rows + ); + } + + let mut report = String::new(); + report.push_str("Constraint evaluation mismatches found!\n\n"); + + for mismatch in &self.mismatches { + report.push_str(&format!("{}\n", mismatch)); + } + + report.push_str(&format!( + "\nSummary: {} mismatches found across {} rows ({} total constraints checked)", + self.mismatches.len(), + self.total_rows, + self.total_constraints_checked + )); + + report + } +} + +// ============================================================================ +// Winterfell Constraint Evaluation +// ============================================================================ + +/// Evaluates Winterfell transition constraints at a specific row. +/// +/// Returns a vector of constraint evaluation values as canonical u64. +pub fn evaluate_winterfell_transition_at_row( + air: &A, + trace: &[Vec], + row: usize, +) -> Vec +where + A: Air, +{ + let trace_width = trace.len(); + let trace_length = trace[0].len(); + + // Build current and next row data + let current: Vec = (0..trace_width).map(|col| trace[col][row]).collect(); + + let next_row = (row + 1) % trace_length; + let next: Vec = (0..trace_width).map(|col| trace[col][next_row]).collect(); + + // Create evaluation frame + let frame = EvaluationFrame::from_rows(current, next); + + // Allocate result buffer based on number of transition constraints + let num_constraints = air.context().num_transition_constraints(); + let mut result = vec![WinterfellFelt::ZERO; num_constraints]; + + // Evaluate transition constraints (empty periodic values for simple AIRs) + let periodic_values: Vec = vec![]; + air.evaluate_transition(&frame, &periodic_values, &mut result); + + // Convert to canonical u64 + result.iter().map(|e| e.to_canonical_u64()).collect() +} + +/// Gets Winterfell boundary constraint info. +/// Returns (column, row, expected_value) for each assertion. +pub fn get_winterfell_boundary_assertions(air: &A) -> Vec<(usize, usize, u64)> +where + A: Air, +{ + air.get_assertions() + .iter() + .map(|assertion| { + // For single assertions, get the value directly + // The assertion contains: column index, step (row), and expected value + let col = assertion.column(); + let step = assertion.first_step(); + // Get values from the assertion - for single value assertions + let values = assertion.values(); + let expected = if !values.is_empty() { + values[0].to_canonical_u64() + } else { + 0 + }; + (col, step, expected) + }) + .collect() +} + +/// Evaluates boundary constraints at a specific row for Winterfell. +/// Returns the constraint evaluation (actual - expected) for each boundary constraint +/// that applies to this row, multiplied by the first_row indicator (like Plonky3 does). +pub fn evaluate_winterfell_boundary_at_row( + air: &A, + trace: &[Vec], + row: usize, + num_rows: usize, +) -> Vec +where + A: Air, +{ + let assertions = get_winterfell_boundary_assertions(air); + let mut results = Vec::new(); + + for (col, assertion_row, expected) in assertions { + // Compute (actual - expected) + let actual = trace[col][row].to_canonical_u64(); + + // For first row constraints: multiply by is_first_row indicator + // For last row constraints: multiply by is_last_row indicator + let is_first_row = if row == 0 { 1u64 } else { 0u64 }; + let is_last_row = if row == num_rows - 1 { 1u64 } else { 0u64 }; + + if assertion_row == 0 { + // First row boundary constraint + // Plonky3 computes: is_first_row * (actual - expected) + // We need to do the same arithmetic in the field + let actual_felt = WinterfellFelt::new(actual); + let expected_felt = WinterfellFelt::new(expected); + let is_first_felt = WinterfellFelt::new(is_first_row); + let diff = actual_felt - expected_felt; + let result = is_first_felt * diff; + results.push(result.to_canonical_u64()); + } else if assertion_row == num_rows - 1 { + // Last row boundary constraint + let actual_felt = WinterfellFelt::new(actual); + let expected_felt = WinterfellFelt::new(expected); + let is_last_felt = WinterfellFelt::new(is_last_row); + let diff = actual_felt - expected_felt; + let result = is_last_felt * diff; + results.push(result.to_canonical_u64()); + } + } + + results +} + +// ============================================================================ +// Plonky3 Constraint Capturing Builder +// ============================================================================ + +/// A view into two consecutive rows of the trace matrix for constraint evaluation. +pub struct TwoRowMatrixView { + current_row: Vec, + next_row: Vec, +} + +impl TwoRowMatrixView { + pub fn new(current_row: Vec, next_row: Vec) -> Self { + Self { current_row, next_row } + } +} + +impl Matrix for TwoRowMatrixView { + fn width(&self) -> usize { + self.current_row.len() + } + + fn height(&self) -> usize { + 2 + } + + fn row_slice(&self, r: usize) -> Option> { + match r { + 0 => Some(self.current_row.clone()), + 1 => Some(self.next_row.clone()), + _ => None, + } + } +} + +/// A builder that captures constraint evaluation values instead of asserting them. +/// +/// This implements `MidenAirBuilder` and records all values passed to `assert_zero` +/// for later comparison with Winterfell's constraint evaluations. +pub struct ConstraintCapturingBuilder { + /// View of current and next rows + main_view: TwoRowMatrixView, + /// Current row index being evaluated + current_row: usize, + /// Total number of rows in the trace + num_rows: usize, + /// Public input values + public_values: Vec, + /// Periodic column evaluations (empty for simple AIRs) + periodic_values: Vec, + /// Captured constraint evaluations + captured_constraints: Vec, +} + +impl ConstraintCapturingBuilder { + /// Creates a new constraint capturing builder for a specific row. + pub fn new( + trace: &RowMajorMatrix, + row: usize, + public_values: Vec, + periodic_values: Vec, + ) -> Self { + let num_rows = trace.height(); + let width = trace.width(); + + // Get current row + let current_row: Vec = trace + .row_slice(row) + .map(|s| s.iter().cloned().collect()) + .unwrap_or_else(|| vec![F::ZERO; width]); + + // Get next row (wrap around) + let next_row_idx = (row + 1) % num_rows; + let next_row: Vec = trace + .row_slice(next_row_idx) + .map(|s| s.iter().cloned().collect()) + .unwrap_or_else(|| vec![F::ZERO; width]); + + let main_view = TwoRowMatrixView::new(current_row, next_row); + + Self { + main_view, + current_row: row, + num_rows, + public_values, + periodic_values, + captured_constraints: Vec::new(), + } + } + + /// Returns the captured constraint values as canonical u64. + pub fn get_captured_constraints(&self) -> Vec + where + F: PrimeField64, + { + self.captured_constraints.iter().map(|f| f.as_canonical_u64()).collect() + } + + fn is_first_row_value(&self) -> F { + if self.current_row == 0 { F::ONE } else { F::ZERO } + } + + fn is_last_row_value(&self) -> F { + if self.current_row == self.num_rows - 1 { + F::ONE + } else { + F::ZERO + } + } + + fn is_transition_window_value(&self, size: usize) -> F { + if self.current_row < self.num_rows.saturating_sub(size - 1) { + F::ONE + } else { + F::ZERO + } + } +} + +impl MidenAirBuilder for ConstraintCapturingBuilder { + type F = F; + type Expr = F; + type Var = F; + type M = TwoRowMatrixView; + type PublicVar = F; + type PeriodicVal = F; + type EF = F; + type ExprEF = F; + type VarEF = F; + type MP = TwoRowMatrixView; + type RandomVar = F; + + fn main(&self) -> Self::M { + TwoRowMatrixView::new(self.main_view.current_row.clone(), self.main_view.next_row.clone()) + } + + fn is_first_row(&self) -> Self::Expr { + self.is_first_row_value() + } + + fn is_last_row(&self) -> Self::Expr { + self.is_last_row_value() + } + + fn is_transition_window(&self, size: usize) -> Self::Expr { + self.is_transition_window_value(size) + } + + fn assert_zero>(&mut self, x: I) { + self.captured_constraints.push(x.into()); + } + + fn public_values(&self) -> &[Self::PublicVar] { + &self.public_values + } + + fn periodic_evals(&self) -> &[Self::PeriodicVal] { + &self.periodic_values + } + + fn preprocessed(&self) -> Self::M { + self.main() + } + + fn assert_zero_ext(&mut self, x: I) + where + I: Into, + { + self.captured_constraints.push(x.into()); + } + + fn permutation(&self) -> Self::MP { + self.main() + } + + fn permutation_randomness(&self) -> &[Self::RandomVar] { + &[] + } + + fn aux_bus_boundary_values(&self) -> &[Self::VarEF] { + &[] + } +} + +// ============================================================================ +// Trace Conversion Utilities +// ============================================================================ + +/// Converts a Plonky3 RowMajorMatrix to a Winterfell-style column-major trace. +pub fn plonky3_trace_to_winterfell(trace: &RowMajorMatrix) -> Vec> +where + F: PrimeField64 + Clone + Send + Sync, +{ + let num_rows = trace.height(); + let num_cols = trace.width(); + + (0..num_cols) + .map(|col| { + (0..num_rows) + .map(|row| { + let row_slice = trace.row_slice(row).unwrap(); + WinterfellFelt::new(row_slice[col].as_canonical_u64()) + }) + .collect() + }) + .collect() +} + +/// Converts a Winterfell column-major trace to a Plonky3 RowMajorMatrix. +pub fn winterfell_trace_to_plonky3( + trace: &[Vec], +) -> RowMajorMatrix { + if trace.is_empty() { + return RowMajorMatrix::new(vec![], 0); + } + + let num_cols = trace.len(); + let num_rows = trace[0].len(); + + let mut values = Vec::with_capacity(num_rows * num_cols); + + for row in 0..num_rows { + for col in 0..num_cols { + let val = trace[col][row].as_int(); + values.push(F::from_u64(val)); + } + } + + RowMajorMatrix::new(values, num_cols) +} + +// ============================================================================ +// High-Level Comparison Functions +// ============================================================================ + +/// Compares constraint evaluations row by row. +/// +/// Returns a ComparisonResult with any mismatches found. +pub fn compare_evaluations_by_row( + winterfell_evals: &[Vec], + plonky3_evals: &[Vec], +) -> ComparisonResult { + let total_rows = winterfell_evals.len().max(plonky3_evals.len()); + let mut mismatches = Vec::new(); + let mut total_constraints_checked = 0; + + for row in 0..total_rows { + let w_row = winterfell_evals.get(row); + let p_row = plonky3_evals.get(row); + + match (w_row, p_row) { + (Some(w), Some(p)) => { + // Check if constraint counts match + if w.len() != p.len() { + mismatches.push(ConstraintMismatch { + row, + constraint_index: usize::MAX, + winterfell_value: w.len() as u64, + plonky3_value: p.len() as u64, + }); + continue; + } + + total_constraints_checked += w.len(); + + for (idx, (w_val, p_val)) in w.iter().zip(p.iter()).enumerate() { + if w_val != p_val { + mismatches.push(ConstraintMismatch { + row, + constraint_index: idx, + winterfell_value: *w_val, + plonky3_value: *p_val, + }); + } + } + }, + (Some(w), None) => { + mismatches.push(ConstraintMismatch { + row, + constraint_index: usize::MAX, + winterfell_value: w.len() as u64, + plonky3_value: 0, + }); + }, + (None, Some(p)) => { + mismatches.push(ConstraintMismatch { + row, + constraint_index: usize::MAX, + winterfell_value: 0, + plonky3_value: p.len() as u64, + }); + }, + (None, None) => {}, + } + } + + ComparisonResult { + mismatches, + total_rows, + total_constraints_checked, + } +} diff --git a/air-script/src/test_utils/mod.rs b/air-script/src/test_utils/mod.rs index 03ce65907..2b4182aee 100644 --- a/air-script/src/test_utils/mod.rs +++ b/air-script/src/test_utils/mod.rs @@ -2,6 +2,8 @@ pub mod air_tester_macros; /// Code generation for tests/**/*.air files. pub mod codegen; +/// Cross-backend constraint evaluation comparison utilities. +pub mod cross_backend_comparison; /// Conversion utilities for test inputs (both public inputs and variable-length public inputs). pub mod pub_inputs_conversion_utils; /// Winterfell-specific traits diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs new file mode 100644 index 000000000..78cbb9bf2 --- /dev/null +++ b/air-script/src/tests/comparison/mod.rs @@ -0,0 +1,4 @@ +//! Cross-backend comparison tests. +//! +//! This module contains tests that verify Winterfell and Plonky3 backends +//! produce equivalent constraint evaluations for the same AIR and trace data. diff --git a/air-script/src/tests/mod.rs b/air-script/src/tests/mod.rs index 75bd5f39a..a0603be81 100644 --- a/air-script/src/tests/mod.rs +++ b/air-script/src/tests/mod.rs @@ -43,6 +43,7 @@ mod trace_col_groups; #[allow(unused_variables, dead_code, unused_mut)] mod variables; +mod comparison; mod docs_sync; mod plonky3; mod winterfell; From 7238a2c6b884414f6ab1c438e4263ac53b41b0b3 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Thu, 8 Jan 2026 18:45:46 +0100 Subject: [PATCH 38/83] tests(compare-wf-p3): binary test --- air-script/src/tests/binary/mod.rs | 4 +- air-script/src/tests/comparison/binary.rs | 228 ++++++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 2 + 3 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/binary.rs diff --git a/air-script/src/tests/binary/mod.rs b/air-script/src/tests/binary/mod.rs index 08cbf3225..14be89c53 100644 --- a/air-script/src/tests/binary/mod.rs +++ b/air-script/src/tests/binary/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod binary; +pub mod binary; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod binary_plonky3; +pub mod binary_plonky3; mod test_air_plonky3; mod test_air_winterfell; diff --git a/air-script/src/tests/comparison/binary.rs b/air-script/src/tests/comparison/binary.rs new file mode 100644 index 000000000..05bc84636 --- /dev/null +++ b/air-script/src/tests/comparison/binary.rs @@ -0,0 +1,228 @@ +//! Cross-backend comparison test for the Binary AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the Binary AIR at every row of the trace. + +use p3_field::PrimeCharacteristicRing; +use p3_goldilocks::Goldilocks; +use p3_matrix::dense::RowMajorMatrix; +use p3_miden_air::MidenAir; +use pretty_assertions::assert_eq; +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{ + ConstraintCapturingBuilder, compare_evaluations_by_row, + evaluate_winterfell_boundary_at_row, evaluate_winterfell_transition_at_row, + winterfell_trace_to_plonky3, + }, + tests::binary::{ + binary::{BinaryAir as WinterfellBinaryAir, PublicInputs}, + binary_plonky3::BinaryAir as Plonky3BinaryAir, + }, +}; + +/// Build a trace for the Binary AIR. +/// +/// The Binary AIR has 2 columns (a, b) that alternate between 0 and 1: +/// - Row 0: (start, start) +/// - Row 1: (1-start, 1-start) +/// - Row 2: (start, start) +/// - ... +fn build_binary_trace(length: usize, start_value: u64) -> Vec> { + let mut col_a = vec![Felt::ZERO; length]; + let mut col_b = vec![Felt::ZERO; length]; + + let start = Felt::new(start_value); + let one = Felt::ONE; + + col_a[0] = start; + col_b[0] = start; + + for i in 1..length { + col_a[i] = one - col_a[i - 1]; + col_b[i] = one - col_b[i - 1]; + } + + vec![col_a, col_b] +} + +/// Build public inputs for the Binary AIR. +fn build_public_inputs(first_value: u64) -> ([Felt; 16], Vec) { + let mut winterfell_inputs = [Felt::ZERO; 16]; + winterfell_inputs[0] = Felt::new(first_value); + + let plonky3_inputs: Vec = (0..16) + .map(|i| { + if i == 0 { + Goldilocks::from_u64(first_value) + } else { + Goldilocks::ZERO + } + }) + .collect(); + + (winterfell_inputs, plonky3_inputs) +} + +#[test] +fn test_binary_air_constraint_comparison() { + // Test parameters + let trace_length = 64; // Small trace for testing + let start_value = 0u64; // Start with 0 (satisfies binary constraint a*a - a = 0) + + // Build trace (Winterfell format: column-major) + let winterfell_trace = build_binary_trace(trace_length, start_value); + + // Convert to Plonky3 format (row-major) + let plonky3_trace: RowMajorMatrix = winterfell_trace_to_plonky3(&winterfell_trace); + + // Build public inputs + let (winterfell_pub_inputs, plonky3_pub_inputs) = build_public_inputs(start_value); + + // Create Winterfell AIR + let trace_info = TraceInfo::new(2, trace_length); + let proof_options = WinterProofOptions::new( + 32, + 8, + 0, + winter_air::FieldExtension::None, + 8, + 31, + winter_air::BatchingMethod::Linear, + winter_air::BatchingMethod::Linear, + ); + let winterfell_air = WinterfellBinaryAir::new( + trace_info, + PublicInputs::new(winterfell_pub_inputs), + proof_options, + ); + + // Create Plonky3 AIR + let plonky3_air = Plonky3BinaryAir; + + // Evaluate constraints at each row and collect results + let mut winterfell_results: Vec> = Vec::new(); + let mut plonky3_results: Vec> = Vec::new(); + + for row in 0..trace_length { + // Winterfell: evaluate transition constraints + let w_transition = + evaluate_winterfell_transition_at_row(&winterfell_air, &winterfell_trace, row); + + // Winterfell: evaluate boundary constraints (with selector multiplication) + let w_boundary = evaluate_winterfell_boundary_at_row( + &winterfell_air, + &winterfell_trace, + row, + trace_length, + ); + + // Combine: boundary constraints first, then transition constraints + // This matches the order in the Plonky3 generated code + let mut w_all = w_boundary; + w_all.extend(w_transition); + winterfell_results.push(w_all); + + // Plonky3: create a capturing builder for this row + let mut builder = ConstraintCapturingBuilder::new( + &plonky3_trace, + row, + plonky3_pub_inputs.clone(), + vec![], // No periodic values for binary AIR + ); + + // Evaluate the Plonky3 AIR + MidenAir::::eval(&plonky3_air, &mut builder); + + // Get captured constraints + let p_all = builder.get_captured_constraints(); + plonky3_results.push(p_all); + } + + // Compare results + let comparison = compare_evaluations_by_row(&winterfell_results, &plonky3_results); + + if !comparison.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", comparison.format_report()); + } + + // Also use pretty_assertions for a nice diff if there are mismatches + assert_eq!( + winterfell_results, plonky3_results, + "Winterfell and Plonky3 constraint evaluations should match" + ); + + println!( + "Binary AIR comparison passed: {} constraints checked across {} rows", + comparison.total_constraints_checked, comparison.total_rows + ); +} + +/// Test with a different starting value to ensure constraints are properly evaluated. +#[test] +fn test_binary_air_constraint_comparison_start_one() { + let trace_length = 64; + let start_value = 1u64; // Start with 1 + + let winterfell_trace = build_binary_trace(trace_length, start_value); + let plonky3_trace: RowMajorMatrix = winterfell_trace_to_plonky3(&winterfell_trace); + + let (winterfell_pub_inputs, plonky3_pub_inputs) = build_public_inputs(start_value); + + let trace_info = TraceInfo::new(2, trace_length); + let proof_options = WinterProofOptions::new( + 32, + 8, + 0, + winter_air::FieldExtension::None, + 8, + 31, + winter_air::BatchingMethod::Linear, + winter_air::BatchingMethod::Linear, + ); + let winterfell_air = WinterfellBinaryAir::new( + trace_info, + PublicInputs::new(winterfell_pub_inputs), + proof_options, + ); + + let plonky3_air = Plonky3BinaryAir; + + let mut winterfell_results: Vec> = Vec::new(); + let mut plonky3_results: Vec> = Vec::new(); + + for row in 0..trace_length { + let w_transition = + evaluate_winterfell_transition_at_row(&winterfell_air, &winterfell_trace, row); + let w_boundary = evaluate_winterfell_boundary_at_row( + &winterfell_air, + &winterfell_trace, + row, + trace_length, + ); + + let mut w_all = w_boundary; + w_all.extend(w_transition); + winterfell_results.push(w_all); + + let mut builder = ConstraintCapturingBuilder::new( + &plonky3_trace, + row, + plonky3_pub_inputs.clone(), + vec![], + ); + + MidenAir::::eval(&plonky3_air, &mut builder); + plonky3_results.push(builder.get_captured_constraints()); + } + + let comparison = compare_evaluations_by_row(&winterfell_results, &plonky3_results); + + if !comparison.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", comparison.format_report()); + } + + assert_eq!(winterfell_results, plonky3_results); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 78cbb9bf2..4ab9426d0 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -2,3 +2,5 @@ //! //! This module contains tests that verify Winterfell and Plonky3 backends //! produce equivalent constraint evaluations for the same AIR and trace data. + +mod binary; From 7d95fd7431e252b1596a71459b2691c8b659f2a6 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Thu, 8 Jan 2026 18:16:58 +0100 Subject: [PATCH 39/83] tests(compare-wf-p3): compare wf-p3 test harness + test constants --- .../test_utils/cross_backend_comparison.rs | 297 +++++++++++++++++- air-script/src/tests/comparison/binary.rs | 296 +++++++---------- air-script/src/tests/comparison/constants.rs | 161 ++++++++++ air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/constants/mod.rs | 4 +- 5 files changed, 566 insertions(+), 193 deletions(-) create mode 100644 air-script/src/tests/comparison/constants.rs diff --git a/air-script/src/test_utils/cross_backend_comparison.rs b/air-script/src/test_utils/cross_backend_comparison.rs index 6affd8e69..21e40d23d 100644 --- a/air-script/src/test_utils/cross_backend_comparison.rs +++ b/air-script/src/test_utils/cross_backend_comparison.rs @@ -22,9 +22,13 @@ use std::fmt; use p3_field::{Field, PrimeCharacteristicRing, PrimeField64}; use p3_goldilocks::Goldilocks; use p3_matrix::{Matrix, dense::RowMajorMatrix}; -use p3_miden_air::MidenAirBuilder; -use winter_air::{Air, EvaluationFrame}; -use winter_math::{FieldElement, fields::f64::BaseElement as WinterfellFelt}; +use p3_miden_air::{MidenAir, MidenAirBuilder}; +use winter_air::{ + Air, BatchingMethod, EvaluationFrame, FieldExtension, ProofOptions as WinterProofOptions, + TraceInfo, +}; +use winter_math::{FieldElement, ToElements, fields::f64::BaseElement as WinterfellFelt}; +use winter_utils::Serializable; // ============================================================================ // Canonical u64 Conversion @@ -120,6 +124,10 @@ impl ComparisonResult { /// Evaluates Winterfell transition constraints at a specific row. /// /// Returns a vector of constraint evaluation values as canonical u64. +/// +/// Note: This does NOT apply the `is_transition` multiplier. Use +/// `evaluate_winterfell_transition_at_row_with_selector` if you need +/// the results to be comparable with Plonky3's `when_transition()` behavior. pub fn evaluate_winterfell_transition_at_row( air: &A, trace: &[Vec], @@ -152,6 +160,57 @@ where result.iter().map(|e| e.to_canonical_u64()).collect() } +/// Evaluates Winterfell transition constraints at a specific row, +/// with each result multiplied by `is_transition`. +/// +/// This makes the results comparable with Plonky3's `when_transition()` behavior, +/// where constraints are multiplied by a selector that is 1 on all rows except +/// the last one. +/// +/// Note: This applies the selector to ALL transition constraints, including +/// those that are NOT wrapped in `when_transition()` in Plonky3. For valid +/// traces where all constraints evaluate to 0, this doesn't matter. For +/// invalid traces or debugging, be aware of this difference. +pub fn evaluate_winterfell_transition_at_row_with_selector( + air: &A, + trace: &[Vec], + row: usize, + num_rows: usize, +) -> Vec +where + A: Air, +{ + let trace_width = trace.len(); + let trace_length = trace[0].len(); + + // Build current and next row data + let current: Vec = (0..trace_width).map(|col| trace[col][row]).collect(); + + let next_row = (row + 1) % trace_length; + let next: Vec = (0..trace_width).map(|col| trace[col][next_row]).collect(); + + // Create evaluation frame + let frame = EvaluationFrame::from_rows(current, next); + + // Allocate result buffer based on number of transition constraints + let num_constraints = air.context().num_transition_constraints(); + let mut result = vec![WinterfellFelt::ZERO; num_constraints]; + + // Evaluate transition constraints (empty periodic values for simple AIRs) + let periodic_values: Vec = vec![]; + air.evaluate_transition(&frame, &periodic_values, &mut result); + + // Apply is_transition selector: 1 for rows 0..n-1, 0 for row n-1 + let is_transition = if row < num_rows - 1 { + WinterfellFelt::ONE + } else { + WinterfellFelt::ZERO + }; + + // Convert to canonical u64, applying the selector + result.iter().map(|e| (is_transition * *e).to_canonical_u64()).collect() +} + /// Gets Winterfell boundary constraint info. /// Returns (column, row, expected_value) for each assertion. pub fn get_winterfell_boundary_assertions(air: &A) -> Vec<(usize, usize, u64)> @@ -526,3 +585,235 @@ pub fn compare_evaluations_by_row( total_constraints_checked, } } + +// ============================================================================ +// Cross-Backend Test Configuration Trait +// ============================================================================ + +/// Trait for configuring cross-backend comparison tests. +/// +/// Implement this trait for each AIR to enable cross-backend constraint comparison. +/// The trait provides a unified interface for building traces, public inputs, +/// and AIR instances for both Winterfell and Plonky3 backends. +/// +/// # Example +/// +/// ```ignore +/// struct MyTestConfig; +/// +/// impl CrossBackendTestConfig for MyTestConfig { +/// type WinterfellAir = MyWinterfellAir; +/// type Plonky3Air = MyPlonky3Air; +/// type WinterfellPublicInputs = MyPublicInputs; +/// +/// fn trace_width(&self) -> usize { 2 } +/// fn trace_length(&self) -> usize { 64 } +/// // ... other methods +/// } +/// ``` +pub trait CrossBackendTestConfig { + /// The Winterfell AIR type. + type WinterfellAir: Air; + + /// The Plonky3 AIR type. + type Plonky3Air: MidenAir; + + /// The Winterfell public inputs type. + type WinterfellPublicInputs: Serializable + ToElements; + + /// Returns the trace width (number of columns). + fn trace_width(&self) -> usize; + + /// Returns the trace length (number of rows). + fn trace_length(&self) -> usize; + + /// Builds the trace in Winterfell format (column-major). + fn build_winterfell_trace(&self) -> Vec>; + + /// Builds the Winterfell public inputs. + fn build_winterfell_public_inputs(&self) -> Self::WinterfellPublicInputs; + + /// Builds the Plonky3 public inputs. + fn build_plonky3_public_inputs(&self) -> Vec; + + /// Creates the Winterfell AIR instance. + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: Self::WinterfellPublicInputs, + options: WinterProofOptions, + ) -> Self::WinterfellAir; + + /// Creates the Plonky3 AIR instance. + fn create_plonky3_air(&self) -> Self::Plonky3Air; + + /// Returns the number of public values for Plonky3. + fn num_public_values(&self) -> usize; + + /// Returns the periodic column values (empty by default). + /// + /// Each inner Vec represents a periodic column, with values that repeat. + /// TODO: Implement periodic column support in future. + fn periodic_column_values(&self) -> Vec> { + vec![] + } +} + +// ============================================================================ +// Default Proof Options +// ============================================================================ + +/// Creates default proof options for testing. +pub fn default_proof_options() -> WinterProofOptions { + WinterProofOptions::new( + 32, // number of queries + 8, // blowup factor + 0, // grinding factor + FieldExtension::None, // field extension + 8, // FRI folding factor + 31, // FRI max remainder polynomial degree + BatchingMethod::Linear, // constraint composition batching + BatchingMethod::Linear, // DEEP polynomial batching + ) +} + +// ============================================================================ +// Main Comparison Function +// ============================================================================ + +/// Runs a full cross-backend comparison for the given test configuration. +/// +/// This function: +/// 1. Builds the trace using the config +/// 2. Creates both Winterfell and Plonky3 AIR instances +/// 3. Evaluates all constraints at each row for both backends +/// 4. Compares the results and returns a detailed report +/// +/// # Arguments +/// +/// * `config` - The test configuration implementing `CrossBackendTestConfig` +/// +/// # Returns +/// +/// A `ComparisonResult` containing any mismatches found and statistics. +pub fn run_cross_backend_comparison(config: &C) -> ComparisonResult +where + C: CrossBackendTestConfig, +{ + let trace_length = config.trace_length(); + + // Build trace in Winterfell format + let winterfell_trace = config.build_winterfell_trace(); + + // Convert to Plonky3 format + let plonky3_trace: RowMajorMatrix = winterfell_trace_to_plonky3(&winterfell_trace); + + // Build public inputs + let winterfell_pub_inputs = config.build_winterfell_public_inputs(); + let plonky3_pub_inputs = config.build_plonky3_public_inputs(); + + // Create Winterfell AIR + let trace_info = TraceInfo::new(config.trace_width(), trace_length); + let proof_options = default_proof_options(); + let winterfell_air = + config.create_winterfell_air(trace_info, winterfell_pub_inputs, proof_options); + + // Create Plonky3 AIR + let plonky3_air = config.create_plonky3_air(); + + // Get the last_step for Winterfell (where last-row boundary constraints apply) + let last_step = trace_length - winterfell_air.context().num_transition_exemptions(); + + // Evaluate constraints at each row + let mut winterfell_results: Vec> = Vec::new(); + let mut plonky3_results: Vec> = Vec::new(); + + for row in 0..trace_length { + // Winterfell: evaluate boundary constraints + let w_boundary = evaluate_winterfell_boundary_at_row_with_last_step( + &winterfell_air, + &winterfell_trace, + row, + trace_length, + last_step, + ); + + // Winterfell: evaluate transition constraints with is_transition selector + // This makes results comparable with Plonky3's when_transition() behavior + let w_transition = evaluate_winterfell_transition_at_row_with_selector( + &winterfell_air, + &winterfell_trace, + row, + trace_length, + ); + + // Combine: boundary constraints first, then transition constraints + let mut w_all = w_boundary; + w_all.extend(w_transition); + winterfell_results.push(w_all); + + // Plonky3: create a capturing builder for this row + let mut builder = ConstraintCapturingBuilder::new( + &plonky3_trace, + row, + plonky3_pub_inputs.clone(), + vec![], // TODO: periodic values support + ); + + // Evaluate the Plonky3 AIR + MidenAir::::eval(&plonky3_air, &mut builder); + + // Get captured constraints + let p_all = builder.get_captured_constraints(); + plonky3_results.push(p_all); + } + + // Compare results + compare_evaluations_by_row(&winterfell_results, &plonky3_results) +} + +/// Evaluates boundary constraints at a specific row for Winterfell. +/// +/// This version takes the explicit `last_step` value from Winterfell's AIR, +/// which accounts for transition exemptions when determining when last-row +/// boundary constraints should apply. +pub fn evaluate_winterfell_boundary_at_row_with_last_step( + air: &A, + trace: &[Vec], + row: usize, + num_rows: usize, + last_step: usize, +) -> Vec +where + A: Air, +{ + let assertions = get_winterfell_boundary_assertions(air); + let mut results = Vec::new(); + + for (col, assertion_row, expected) in assertions { + let actual = trace[col][row].to_canonical_u64(); + + let is_first_row = if row == 0 { 1u64 } else { 0u64 }; + let is_last_row = if row == num_rows - 1 { 1u64 } else { 0u64 }; + + if assertion_row == 0 { + // First row boundary constraint + let actual_felt = WinterfellFelt::new(actual); + let expected_felt = WinterfellFelt::new(expected); + let is_first_felt = WinterfellFelt::new(is_first_row); + let diff = actual_felt - expected_felt; + let result = is_first_felt * diff; + results.push(result.to_canonical_u64()); + } else if assertion_row == last_step { + // Last row boundary constraint (using last_step from AIR) + let actual_felt = WinterfellFelt::new(actual); + let expected_felt = WinterfellFelt::new(expected); + let is_last_felt = WinterfellFelt::new(is_last_row); + let diff = actual_felt - expected_felt; + let result = is_last_felt * diff; + results.push(result.to_canonical_u64()); + } + } + + results +} diff --git a/air-script/src/tests/comparison/binary.rs b/air-script/src/tests/comparison/binary.rs index 05bc84636..a16cc0697 100644 --- a/air-script/src/tests/comparison/binary.rs +++ b/air-script/src/tests/comparison/binary.rs @@ -5,224 +5,144 @@ use p3_field::PrimeCharacteristicRing; use p3_goldilocks::Goldilocks; -use p3_matrix::dense::RowMajorMatrix; -use p3_miden_air::MidenAir; -use pretty_assertions::assert_eq; use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{ - ConstraintCapturingBuilder, compare_evaluations_by_row, - evaluate_winterfell_boundary_at_row, evaluate_winterfell_transition_at_row, - winterfell_trace_to_plonky3, - }, + test_utils::cross_backend_comparison::{CrossBackendTestConfig, run_cross_backend_comparison}, tests::binary::{ binary::{BinaryAir as WinterfellBinaryAir, PublicInputs}, binary_plonky3::BinaryAir as Plonky3BinaryAir, }, }; -/// Build a trace for the Binary AIR. -/// -/// The Binary AIR has 2 columns (a, b) that alternate between 0 and 1: -/// - Row 0: (start, start) -/// - Row 1: (1-start, 1-start) -/// - Row 2: (start, start) -/// - ... -fn build_binary_trace(length: usize, start_value: u64) -> Vec> { - let mut col_a = vec![Felt::ZERO; length]; - let mut col_b = vec![Felt::ZERO; length]; - - let start = Felt::new(start_value); - let one = Felt::ONE; - - col_a[0] = start; - col_b[0] = start; - - for i in 1..length { - col_a[i] = one - col_a[i - 1]; - col_b[i] = one - col_b[i - 1]; - } +// ============================================================================ +// Test Configuration +// ============================================================================ - vec![col_a, col_b] +/// Configuration for Binary AIR cross-backend comparison tests. +struct BinaryTestConfig { + /// The starting value for the binary trace (0 or 1). + start_value: u64, + /// The trace length. + trace_length: usize, } -/// Build public inputs for the Binary AIR. -fn build_public_inputs(first_value: u64) -> ([Felt; 16], Vec) { - let mut winterfell_inputs = [Felt::ZERO; 16]; - winterfell_inputs[0] = Felt::new(first_value); - - let plonky3_inputs: Vec = (0..16) - .map(|i| { - if i == 0 { - Goldilocks::from_u64(first_value) - } else { - Goldilocks::ZERO - } - }) - .collect(); - - (winterfell_inputs, plonky3_inputs) +impl BinaryTestConfig { + fn new(start_value: u64, trace_length: usize) -> Self { + Self { start_value, trace_length } + } + + /// Build a trace for the Binary AIR. + /// + /// The Binary AIR has 2 columns (a, b) that alternate between 0 and 1: + /// - Row 0: (start, start) + /// - Row 1: (1-start, 1-start) + /// - Row 2: (start, start) + /// - ... + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut col_a = vec![Felt::ZERO; length]; + let mut col_b = vec![Felt::ZERO; length]; + + let start = Felt::new(self.start_value); + let one = Felt::ONE; + + col_a[0] = start; + col_b[0] = start; + + for i in 1..length { + col_a[i] = one - col_a[i - 1]; + col_b[i] = one - col_b[i - 1]; + } + + vec![col_a, col_b] + } } -#[test] -fn test_binary_air_constraint_comparison() { - // Test parameters - let trace_length = 64; // Small trace for testing - let start_value = 0u64; // Start with 0 (satisfies binary constraint a*a - a = 0) - - // Build trace (Winterfell format: column-major) - let winterfell_trace = build_binary_trace(trace_length, start_value); - - // Convert to Plonky3 format (row-major) - let plonky3_trace: RowMajorMatrix = winterfell_trace_to_plonky3(&winterfell_trace); - - // Build public inputs - let (winterfell_pub_inputs, plonky3_pub_inputs) = build_public_inputs(start_value); - - // Create Winterfell AIR - let trace_info = TraceInfo::new(2, trace_length); - let proof_options = WinterProofOptions::new( - 32, - 8, - 0, - winter_air::FieldExtension::None, - 8, - 31, - winter_air::BatchingMethod::Linear, - winter_air::BatchingMethod::Linear, - ); - let winterfell_air = WinterfellBinaryAir::new( - trace_info, - PublicInputs::new(winterfell_pub_inputs), - proof_options, - ); +impl CrossBackendTestConfig for BinaryTestConfig { + type WinterfellAir = WinterfellBinaryAir; + type Plonky3Air = Plonky3BinaryAir; + type WinterfellPublicInputs = PublicInputs; - // Create Plonky3 AIR - let plonky3_air = Plonky3BinaryAir; - - // Evaluate constraints at each row and collect results - let mut winterfell_results: Vec> = Vec::new(); - let mut plonky3_results: Vec> = Vec::new(); - - for row in 0..trace_length { - // Winterfell: evaluate transition constraints - let w_transition = - evaluate_winterfell_transition_at_row(&winterfell_air, &winterfell_trace, row); - - // Winterfell: evaluate boundary constraints (with selector multiplication) - let w_boundary = evaluate_winterfell_boundary_at_row( - &winterfell_air, - &winterfell_trace, - row, - trace_length, - ); - - // Combine: boundary constraints first, then transition constraints - // This matches the order in the Plonky3 generated code - let mut w_all = w_boundary; - w_all.extend(w_transition); - winterfell_results.push(w_all); - - // Plonky3: create a capturing builder for this row - let mut builder = ConstraintCapturingBuilder::new( - &plonky3_trace, - row, - plonky3_pub_inputs.clone(), - vec![], // No periodic values for binary AIR - ); - - // Evaluate the Plonky3 AIR - MidenAir::::eval(&plonky3_air, &mut builder); - - // Get captured constraints - let p_all = builder.get_captured_constraints(); - plonky3_results.push(p_all); + fn trace_width(&self) -> usize { + 2 } - // Compare results - let comparison = compare_evaluations_by_row(&winterfell_results, &plonky3_results); + fn trace_length(&self) -> usize { + self.trace_length + } - if !comparison.is_ok() { - panic!("Constraint evaluation comparison failed!\n\n{}", comparison.format_report()); + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() } - // Also use pretty_assertions for a nice diff if there are mismatches - assert_eq!( - winterfell_results, plonky3_results, - "Winterfell and Plonky3 constraint evaluations should match" - ); + fn build_winterfell_public_inputs(&self) -> PublicInputs { + let mut inputs = [Felt::ZERO; 16]; + inputs[0] = Felt::new(self.start_value); + PublicInputs::new(inputs) + } + + fn build_plonky3_public_inputs(&self) -> Vec { + (0..16) + .map(|i| { + if i == 0 { + Goldilocks::from_u64(self.start_value) + } else { + Goldilocks::ZERO + } + }) + .collect() + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellBinaryAir { + WinterfellBinaryAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3BinaryAir { + Plonky3BinaryAir + } + + fn num_public_values(&self) -> usize { + 16 + } +} + +// ============================================================================ +// Tests +// ============================================================================ + +#[test] +fn test_binary_air_constraint_comparison() { + let config = BinaryTestConfig::new(0, 64); + let result = run_cross_backend_comparison(&config); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } println!( "Binary AIR comparison passed: {} constraints checked across {} rows", - comparison.total_constraints_checked, comparison.total_rows + result.total_constraints_checked, result.total_rows ); } -/// Test with a different starting value to ensure constraints are properly evaluated. #[test] fn test_binary_air_constraint_comparison_start_one() { - let trace_length = 64; - let start_value = 1u64; // Start with 1 - - let winterfell_trace = build_binary_trace(trace_length, start_value); - let plonky3_trace: RowMajorMatrix = winterfell_trace_to_plonky3(&winterfell_trace); - - let (winterfell_pub_inputs, plonky3_pub_inputs) = build_public_inputs(start_value); - - let trace_info = TraceInfo::new(2, trace_length); - let proof_options = WinterProofOptions::new( - 32, - 8, - 0, - winter_air::FieldExtension::None, - 8, - 31, - winter_air::BatchingMethod::Linear, - winter_air::BatchingMethod::Linear, - ); - let winterfell_air = WinterfellBinaryAir::new( - trace_info, - PublicInputs::new(winterfell_pub_inputs), - proof_options, - ); - - let plonky3_air = Plonky3BinaryAir; - - let mut winterfell_results: Vec> = Vec::new(); - let mut plonky3_results: Vec> = Vec::new(); - - for row in 0..trace_length { - let w_transition = - evaluate_winterfell_transition_at_row(&winterfell_air, &winterfell_trace, row); - let w_boundary = evaluate_winterfell_boundary_at_row( - &winterfell_air, - &winterfell_trace, - row, - trace_length, - ); - - let mut w_all = w_boundary; - w_all.extend(w_transition); - winterfell_results.push(w_all); - - let mut builder = ConstraintCapturingBuilder::new( - &plonky3_trace, - row, - plonky3_pub_inputs.clone(), - vec![], - ); - - MidenAir::::eval(&plonky3_air, &mut builder); - plonky3_results.push(builder.get_captured_constraints()); - } - - let comparison = compare_evaluations_by_row(&winterfell_results, &plonky3_results); + let config = BinaryTestConfig::new(1, 64); + let result = run_cross_backend_comparison(&config); - if !comparison.is_ok() { - panic!("Constraint evaluation comparison failed!\n\n{}", comparison.format_report()); + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); } - assert_eq!(winterfell_results, plonky3_results); + println!( + "Binary AIR comparison (start=1) passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); } diff --git a/air-script/src/tests/comparison/constants.rs b/air-script/src/tests/comparison/constants.rs new file mode 100644 index 000000000..c2ad40e58 --- /dev/null +++ b/air-script/src/tests/comparison/constants.rs @@ -0,0 +1,161 @@ +//! Cross-backend comparison test for the Constants AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the Constants AIR at every row of the trace. +//! +//! The Constants AIR tests: +//! - `when_first_row()` boundary constraints (5 constraints) +//! - `when_last_row()` boundary constraints (1 constraint) +//! - `when_transition()` transition constraints (4 constraints) +//! - Global integrity constraints (1 constraint without `when_transition()`) + +use p3_field::PrimeCharacteristicRing; +use p3_goldilocks::Goldilocks; +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, run_cross_backend_comparison}, + tests::constants::{ + constants::{ConstantsAir as WinterfellConstantsAir, PublicInputs}, + constants_plonky3::ConstantsAir as Plonky3ConstantsAir, + }, +}; + +// ============================================================================ +// Test Configuration +// ============================================================================ + +/// Configuration for Constants AIR cross-backend comparison tests. +struct ConstantsTestConfig { + /// The trace length. + trace_length: usize, +} + +impl ConstantsTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + /// Build a trace for the Constants AIR. + /// + /// The trace has 7 columns with the following constraints: + /// - Boundary (first row): col[0]=1, col[1]=1, col[2]=0, col[3]=1, col[4]=1 + /// - Boundary (last row): col[6]=0 + /// - Transition: col[0]' = col[0] + 1, col[1]' = 0, col[2]' = col[2], col[5]' = col[5] + 1 + /// - Integrity (global): col[4] = 1 + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + // Initialize columns + let mut col0 = vec![Felt::ZERO; length]; // Increments by 1 each row + let mut col1 = vec![Felt::ZERO; length]; // 1 at first row, 0 elsewhere + let mut col2 = vec![Felt::ZERO; length]; // Always 0 + let mut col3 = vec![Felt::ZERO; length]; // 1 at first row (no transition constraint) + let mut col4 = vec![Felt::ZERO; length]; // Always 1 (global integrity) + let mut col5 = vec![Felt::ZERO; length]; // Increments by 1 each row + let mut col6 = vec![Felt::ZERO; length]; // Always 0 (last row boundary) + + // First row values + col0[0] = Felt::ONE; + col1[0] = Felt::ONE; + col2[0] = Felt::ZERO; + col3[0] = Felt::ONE; + col4[0] = Felt::ONE; + col5[0] = Felt::ZERO; + col6[0] = Felt::ZERO; + + // Fill subsequent rows based on transition constraints + for i in 1..length { + col0[i] = col0[i - 1] + Felt::ONE; // col[0]' = col[0] + 1 + col1[i] = Felt::ZERO; // col[1]' = 0 + col2[i] = col2[i - 1]; // col[2]' = col[2] + col3[i] = col3[i - 1]; // No constraint, keep same + col4[i] = Felt::ONE; // Global: col[4] = 1 + col5[i] = col5[i - 1] + Felt::ONE; // col[5]' = col[5] + 1 + col6[i] = Felt::ZERO; // Last row should be 0 + } + + vec![col0, col1, col2, col3, col4, col5, col6] + } +} + +impl CrossBackendTestConfig for ConstantsTestConfig { + type WinterfellAir = WinterfellConstantsAir; + type Plonky3Air = Plonky3ConstantsAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 7 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + // The Constants AIR has 32 public inputs but doesn't use them in constraints + // (it uses literal constants like Felt::ONE instead) + PublicInputs::new([Felt::ZERO; 4], [Felt::ZERO; 4], [Felt::ZERO; 4], [Felt::ZERO; 20]) + } + + fn build_plonky3_public_inputs(&self) -> Vec { + vec![Goldilocks::ZERO; 32] + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellConstantsAir { + WinterfellConstantsAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3ConstantsAir { + Plonky3ConstantsAir + } + + fn num_public_values(&self) -> usize { + 32 + } +} + +// ============================================================================ +// Tests +// ============================================================================ + +#[test] +fn test_constants_air_constraint_comparison() { + let config = ConstantsTestConfig::new(64); + let result = run_cross_backend_comparison(&config); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "Constants AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_constants_air_constraint_comparison_longer_trace() { + // Test with a longer trace to exercise more rows + let config = ConstantsTestConfig::new(128); + let result = run_cross_backend_comparison(&config); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "Constants AIR comparison (128 rows) passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 4ab9426d0..ad73228e4 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -4,3 +4,4 @@ //! produce equivalent constraint evaluations for the same AIR and trace data. mod binary; +mod constants; diff --git a/air-script/src/tests/constants/mod.rs b/air-script/src/tests/constants/mod.rs index 1540ba337..f412a995b 100644 --- a/air-script/src/tests/constants/mod.rs +++ b/air-script/src/tests/constants/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod constants; +pub mod constants; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod constants_plonky3; +pub mod constants_plonky3; mod test_air_plonky3; mod test_air_winterfell; From ca759037c15b1e5491e027b673bc076cd8188a0a Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Thu, 8 Jan 2026 18:27:41 +0100 Subject: [PATCH 40/83] tests(compare-wf-p3): add support for periodic columns in test harness --- .../test_utils/cross_backend_comparison.rs | 120 ++++++++++++++++-- 1 file changed, 111 insertions(+), 9 deletions(-) diff --git a/air-script/src/test_utils/cross_backend_comparison.rs b/air-script/src/test_utils/cross_backend_comparison.rs index 21e40d23d..4894ee9bf 100644 --- a/air-script/src/test_utils/cross_backend_comparison.rs +++ b/air-script/src/test_utils/cross_backend_comparison.rs @@ -177,6 +177,32 @@ pub fn evaluate_winterfell_transition_at_row_with_selector( row: usize, num_rows: usize, ) -> Vec +where + A: Air, +{ + // Use the version with periodic values, passing empty periodic values + evaluate_winterfell_transition_at_row_with_periodic(air, trace, row, num_rows, &[]) +} + +/// Evaluates Winterfell transition constraints at a specific row, +/// with periodic column values and `is_transition` selector applied. +/// +/// This is the full-featured version that supports periodic columns. +/// +/// # Arguments +/// +/// * `air` - The Winterfell AIR instance +/// * `trace` - The trace in column-major format +/// * `row` - The row to evaluate at +/// * `num_rows` - Total number of rows in the trace +/// * `periodic_values` - The periodic column values evaluated at this row +pub fn evaluate_winterfell_transition_at_row_with_periodic( + air: &A, + trace: &[Vec], + row: usize, + num_rows: usize, + periodic_values: &[WinterfellFelt], +) -> Vec where A: Air, { @@ -196,9 +222,8 @@ where let num_constraints = air.context().num_transition_constraints(); let mut result = vec![WinterfellFelt::ZERO; num_constraints]; - // Evaluate transition constraints (empty periodic values for simple AIRs) - let periodic_values: Vec = vec![]; - air.evaluate_transition(&frame, &periodic_values, &mut result); + // Evaluate transition constraints with periodic values + air.evaluate_transition(&frame, periodic_values, &mut result); // Apply is_transition selector: 1 for rows 0..n-1, 0 for row n-1 let is_transition = if row < num_rows - 1 { @@ -211,6 +236,60 @@ where result.iter().map(|e| (is_transition * *e).to_canonical_u64()).collect() } +// ============================================================================ +// Periodic Column Evaluation +// ============================================================================ + +/// Evaluates periodic column values at a specific row. +/// +/// Periodic columns repeat with a given period. At row `r`, the value is +/// `column[r % period]` where `period` is the length of the column. +/// +/// # Arguments +/// +/// * `periodic_columns` - Vec of periodic column definitions, each as Vec +/// * `row` - The row index to evaluate at +/// +/// # Returns +/// +/// A vector of field elements, one for each periodic column, evaluated at the given row. +pub fn evaluate_periodic_values_at_row( + periodic_columns: &[Vec], + row: usize, +) -> Vec { + periodic_columns + .iter() + .map(|column| { + if column.is_empty() { + F::ZERO + } else { + let period = column.len(); + let idx = row % period; + F::from_u64(column[idx]) + } + }) + .collect() +} + +/// Evaluates periodic column values at a specific row for Winterfell (WinterfellFelt). +pub fn evaluate_winterfell_periodic_at_row( + periodic_columns: &[Vec], + row: usize, +) -> Vec { + periodic_columns + .iter() + .map(|column| { + if column.is_empty() { + WinterfellFelt::ZERO + } else { + let period = column.len(); + let idx = row % period; + WinterfellFelt::new(column[idx]) + } + }) + .collect() +} + /// Gets Winterfell boundary constraint info. /// Returns (column, row, expected_value) for each assertion. pub fn get_winterfell_boundary_assertions(air: &A) -> Vec<(usize, usize, u64)> @@ -652,8 +731,22 @@ pub trait CrossBackendTestConfig { /// Returns the periodic column values (empty by default). /// - /// Each inner Vec represents a periodic column, with values that repeat. - /// TODO: Implement periodic column support in future. + /// Each inner Vec represents a periodic column, with values that repeat + /// cyclically. The period is determined by the length of each inner Vec. + /// + /// For example, `vec![1, 0, 0, 0]` defines a periodic column with period 4 + /// that has value 1 on rows 0, 4, 8, ... and value 0 elsewhere. + /// + /// # Example + /// + /// ```ignore + /// fn periodic_column_values(&self) -> Vec> { + /// vec![ + /// vec![1, 0, 0, 0, 0, 0, 0, 0], // k0: period 8, value 1 at rows 0, 8, 16, ... + /// vec![1, 1, 1, 1, 1, 1, 1, 0], // k1: period 8, value 1 except at rows 7, 15, 23, ... + /// ] + /// } + /// ``` fn periodic_column_values(&self) -> Vec> { vec![] } @@ -724,11 +817,19 @@ where // Get the last_step for Winterfell (where last-row boundary constraints apply) let last_step = trace_length - winterfell_air.context().num_transition_exemptions(); + // Get periodic column definitions + let periodic_columns = config.periodic_column_values(); + // Evaluate constraints at each row let mut winterfell_results: Vec> = Vec::new(); let mut plonky3_results: Vec> = Vec::new(); for row in 0..trace_length { + // Evaluate periodic values at this row for both backends + let winterfell_periodic = evaluate_winterfell_periodic_at_row(&periodic_columns, row); + let plonky3_periodic: Vec = + evaluate_periodic_values_at_row(&periodic_columns, row); + // Winterfell: evaluate boundary constraints let w_boundary = evaluate_winterfell_boundary_at_row_with_last_step( &winterfell_air, @@ -739,12 +840,13 @@ where ); // Winterfell: evaluate transition constraints with is_transition selector - // This makes results comparable with Plonky3's when_transition() behavior - let w_transition = evaluate_winterfell_transition_at_row_with_selector( + // and periodic values + let w_transition = evaluate_winterfell_transition_at_row_with_periodic( &winterfell_air, &winterfell_trace, row, trace_length, + &winterfell_periodic, ); // Combine: boundary constraints first, then transition constraints @@ -752,12 +854,12 @@ where w_all.extend(w_transition); winterfell_results.push(w_all); - // Plonky3: create a capturing builder for this row + // Plonky3: create a capturing builder for this row with periodic values let mut builder = ConstraintCapturingBuilder::new( &plonky3_trace, row, plonky3_pub_inputs.clone(), - vec![], // TODO: periodic values support + plonky3_periodic, ); // Evaluate the Plonky3 AIR From 703bb55839a390be2327e88d80dfd92dffa5aac5 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Thu, 8 Jan 2026 18:39:02 +0100 Subject: [PATCH 41/83] tests(compare-wf-p3): bitwise test --- air-script/src/tests/bitwise/mod.rs | 4 +- air-script/src/tests/comparison/bitwise.rs | 157 +++++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + 3 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/bitwise.rs diff --git a/air-script/src/tests/bitwise/mod.rs b/air-script/src/tests/bitwise/mod.rs index 54b732efb..bbefba2b3 100644 --- a/air-script/src/tests/bitwise/mod.rs +++ b/air-script/src/tests/bitwise/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod bitwise; +pub mod bitwise; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod bitwise_plonky3; +pub mod bitwise_plonky3; mod test_air_plonky3; mod test_air_winterfell; diff --git a/air-script/src/tests/comparison/bitwise.rs b/air-script/src/tests/comparison/bitwise.rs new file mode 100644 index 000000000..3980dec58 --- /dev/null +++ b/air-script/src/tests/comparison/bitwise.rs @@ -0,0 +1,157 @@ +//! Cross-backend comparison test for the Bitwise AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the Bitwise AIR at every row of the trace. +//! +//! The Bitwise AIR is important because it uses **periodic columns**: +//! - k0: `[1, 0, 0, 0, 0, 0, 0, 0]` (period 8) - active on rows 0, 8, 16, ... +//! - k1: `[1, 1, 1, 1, 1, 1, 1, 0]` (period 8) - inactive only on rows 7, 15, 23, ... +//! +//! This test validates that the periodic column evaluation works correctly +//! in the cross-backend comparison framework. + +use p3_field::PrimeCharacteristicRing; +use p3_goldilocks::Goldilocks; +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, run_cross_backend_comparison}, + tests::bitwise::{ + bitwise::{BitwiseAir as WinterfellBitwiseAir, PublicInputs}, + bitwise_plonky3::BitwiseAir as Plonky3BitwiseAir, + }, +}; + +// ============================================================================ +// Test Configuration +// ============================================================================ + +/// Configuration for Bitwise AIR cross-backend comparison tests. +struct BitwiseTestConfig { + /// The trace length (must be divisible by the period 8). + trace_length: usize, +} + +impl BitwiseTestConfig { + fn new(trace_length: usize) -> Self { + // Trace length should be divisible by 8 (the periodic column period) + assert!(trace_length % 8 == 0, "Trace length must be divisible by 8 for bitwise AIR"); + Self { trace_length } + } + + /// Build a trace for the Bitwise AIR. + /// + /// The Bitwise AIR has 14 columns. For testing, we use an all-zeros trace + /// which satisfies all the constraints (binary checks, decomposition, etc.). + /// + /// The constraints include: + /// - Binary checks: col[i]^2 - col[i] = 0 for binary columns + /// - Decomposition constraints using periodic column k0 + /// - Transition constraints using periodic column k1 + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let trace_width = 14; + + // Initialize all columns to zero + // This satisfies all binary constraints (0^2 - 0 = 0) + // and most other constraints (they become 0 * something = 0) + let trace: Vec> = vec![vec![Felt::ZERO; length]; trace_width]; + + // Column 13 must be 0 at first row (boundary constraint) + // Already zero, so nothing to do + + trace + } +} + +impl CrossBackendTestConfig for BitwiseTestConfig { + type WinterfellAir = WinterfellBitwiseAir; + type Plonky3Air = Plonky3BitwiseAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 14 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn build_plonky3_public_inputs(&self) -> Vec { + vec![Goldilocks::ZERO; 16] + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellBitwiseAir { + WinterfellBitwiseAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3BitwiseAir { + Plonky3BitwiseAir + } + + fn num_public_values(&self) -> usize { + 16 + } + + /// Returns the periodic column values for the Bitwise AIR. + /// + /// Two periodic columns with period 8: + /// - k0: `[1, 0, 0, 0, 0, 0, 0, 0]` - marks the first row of each 8-row block + /// - k1: `[1, 1, 1, 1, 1, 1, 1, 0]` - active on all rows except the last of each block + fn periodic_column_values(&self) -> Vec> { + vec![ + vec![1, 0, 0, 0, 0, 0, 0, 0], // k0 + vec![1, 1, 1, 1, 1, 1, 1, 0], // k1 + ] + } +} + +// ============================================================================ +// Tests +// ============================================================================ + +#[test] +fn test_bitwise_air_constraint_comparison() { + // Use 64 rows (8 complete periods) + let config = BitwiseTestConfig::new(64); + let result = run_cross_backend_comparison(&config); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "Bitwise AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_bitwise_air_constraint_comparison_larger_trace() { + // Use 512 rows (64 complete periods) - same as the Winterfell test + let config = BitwiseTestConfig::new(512); + let result = run_cross_backend_comparison(&config); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "Bitwise AIR comparison (512 rows) passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index ad73228e4..c1dfd2348 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -4,4 +4,5 @@ //! produce equivalent constraint evaluations for the same AIR and trace data. mod binary; +mod bitwise; mod constants; From 95583f6ce14573936cebc3d5a8ae1c73af3ea997 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Thu, 8 Jan 2026 19:52:21 +0100 Subject: [PATCH 42/83] tests(compare-wf-p3): fibonnaci test --- air-script/src/tests/comparison/fibonacci.rs | 186 +++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/fibonacci/mod.rs | 4 +- 3 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/fibonacci.rs diff --git a/air-script/src/tests/comparison/fibonacci.rs b/air-script/src/tests/comparison/fibonacci.rs new file mode 100644 index 000000000..b40f011fc --- /dev/null +++ b/air-script/src/tests/comparison/fibonacci.rs @@ -0,0 +1,186 @@ +//! Cross-backend comparison test for the Fibonacci AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the Fibonacci AIR at every row of the trace. +//! +//! The Fibonacci AIR computes the Fibonacci sequence with constraints: +//! - Boundary: `a.first = stack_inputs[0]`, `b.first = stack_inputs[1]`, `b.last = stack_output[0]` +//! - Transition: `b' = a + b`, `a' = b` + +use p3_field::PrimeCharacteristicRing; +use p3_goldilocks::Goldilocks; +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, run_cross_backend_comparison}, + tests::fibonacci::{ + fibonacci::{FibonacciAir as WinterfellFibonacciAir, PublicInputs}, + fibonacci_plonky3::FibonacciAir as Plonky3FibonacciAir, + }, +}; + +// ============================================================================ +// Test Configuration +// ============================================================================ + +/// Configuration for Fibonacci AIR cross-backend comparison tests. +struct FibonacciTestConfig { + /// The first Fibonacci number (fib_0). + fib_0: u64, + /// The second Fibonacci number (fib_1). + fib_1: u64, + /// The trace length. + trace_length: usize, +} + +impl FibonacciTestConfig { + fn new(fib_0: u64, fib_1: u64, trace_length: usize) -> Self { + Self { fib_0, fib_1, trace_length } + } + + /// Build a trace for the Fibonacci AIR. + /// + /// The Fibonacci AIR has 2 columns (a, b) with the recurrence: + /// - `a' = b` (next row's a equals current row's b) + /// - `b' = a + b` (next row's b equals sum of current row's a and b) + /// + /// Starting with `a[0] = fib_0`, `b[0] = fib_1`: + /// ```text + /// Row | a | b + /// ----|----------|---------- + /// 0 | fib_0 | fib_1 + /// 1 | fib_1 | fib_0 + fib_1 + /// 2 | fib_2 | fib_3 + /// ... + /// ``` + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut col_a = vec![Felt::ZERO; length]; + let mut col_b = vec![Felt::ZERO; length]; + + col_a[0] = Felt::new(self.fib_0); + col_b[0] = Felt::new(self.fib_1); + + for i in 1..length { + // a' = b (next a is current b) + col_a[i] = col_b[i - 1]; + // b' = a + b (next b is sum of current a and b) + col_b[i] = col_a[i - 1] + col_b[i - 1]; + } + + vec![col_a, col_b] + } + + /// Compute the expected value of `b` at the last step. + /// + /// The last step is `trace_length - num_transition_exemptions` where + /// `num_transition_exemptions = 2` for the Fibonacci AIR. + fn expected_output(&self) -> Felt { + let trace = self.build_trace(); + let last_step = self.trace_length - 2; // num_transition_exemptions = 2 + trace[1][last_step] // column b at last_step + } +} + +impl CrossBackendTestConfig for FibonacciTestConfig { + type WinterfellAir = WinterfellFibonacciAir; + type Plonky3Air = Plonky3FibonacciAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 2 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + let stack_inputs = [Felt::new(self.fib_0), Felt::new(self.fib_1)]; + let stack_output = [self.expected_output()]; + PublicInputs::new(stack_inputs, stack_output) + } + + fn build_plonky3_public_inputs(&self) -> Vec { + // Plonky3 public inputs: [stack_inputs[0], stack_inputs[1], stack_output[0]] + vec![ + Goldilocks::from_u64(self.fib_0), + Goldilocks::from_u64(self.fib_1), + Goldilocks::from_u64(self.expected_output().as_int()), + ] + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellFibonacciAir { + WinterfellFibonacciAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3FibonacciAir { + Plonky3FibonacciAir + } + + fn num_public_values(&self) -> usize { + 3 // stack_inputs[0], stack_inputs[1], stack_output[0] + } +} + +// ============================================================================ +// Tests +// ============================================================================ + +#[test] +fn test_fibonacci_air_constraint_comparison() { + // Standard Fibonacci starting with 0, 1 + let config = FibonacciTestConfig::new(0, 1, 64); + let result = run_cross_backend_comparison(&config); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "Fibonacci AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_fibonacci_air_constraint_comparison_different_start() { + // Fibonacci-like sequence starting with 1, 1 + let config = FibonacciTestConfig::new(1, 1, 64); + let result = run_cross_backend_comparison(&config); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "Fibonacci AIR comparison (start=1,1) passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_fibonacci_air_constraint_comparison_larger_values() { + // Test with larger starting values to exercise field arithmetic + let config = FibonacciTestConfig::new(100, 200, 32); + let result = run_cross_backend_comparison(&config); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "Fibonacci AIR comparison (start=100,200) passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index c1dfd2348..de0da344a 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -6,3 +6,4 @@ mod binary; mod bitwise; mod constants; +mod fibonacci; diff --git a/air-script/src/tests/fibonacci/mod.rs b/air-script/src/tests/fibonacci/mod.rs index 57c53c30e..335c18956 100644 --- a/air-script/src/tests/fibonacci/mod.rs +++ b/air-script/src/tests/fibonacci/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod fibonacci; +pub mod fibonacci; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod fibonacci_plonky3; +pub mod fibonacci_plonky3; mod test_air_plonky3; mod test_air_winterfell; From 25c2c936e7394c4d425d5cd5e94ce99fc2b8b341 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 9 Jan 2026 10:21:55 +0100 Subject: [PATCH 43/83] tests(compare-wf-p3): constraint comprehension test --- .../comparison/constraint_comprehension.rs | 173 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + .../src/tests/constraint_comprehension/mod.rs | 4 +- 3 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/constraint_comprehension.rs diff --git a/air-script/src/tests/comparison/constraint_comprehension.rs b/air-script/src/tests/comparison/constraint_comprehension.rs new file mode 100644 index 000000000..786cc4565 --- /dev/null +++ b/air-script/src/tests/comparison/constraint_comprehension.rs @@ -0,0 +1,173 @@ +//! Cross-backend comparison test for the ConstraintComprehension AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the ConstraintComprehension AIR at every row of the trace. +//! +//! The ConstraintComprehension AIR tests list comprehension in constraints: +//! - Boundary: `c[2].first = 0` (column 8 at row 0) +//! - Integrity: `c = d for (c, d) in (c, d)` expands to c[i] = d[i] for i in 0..4 +//! +//! Trace columns: [clk, fmp[2], ctx, a, b, c[4], d[4]] (14 total) +//! Column indices: clk=0, fmp=1-2, ctx=3, a=4, b=5, c=6-9, d=10-13 + +use p3_field::PrimeCharacteristicRing; +use p3_goldilocks::Goldilocks; +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, run_cross_backend_comparison}, + tests::constraint_comprehension::{ + constraint_comprehension::{ + ConstraintComprehensionAir as WinterfellConstraintComprehensionAir, PublicInputs, + }, + constraint_comprehension_plonky3::ConstraintComprehensionAir as Plonky3ConstraintComprehensionAir, + }, +}; + +// ============================================================================ +// Test Configuration +// ============================================================================ + +/// Configuration for ConstraintComprehension AIR cross-backend comparison tests. +struct ConstraintComprehensionTestConfig { + /// The trace length. + trace_length: usize, +} + +impl ConstraintComprehensionTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + /// Build a trace for the ConstraintComprehension AIR. + /// + /// The trace has 14 columns: [clk, fmp[2], ctx, a, b, c[4], d[4]] + /// - Indices: clk=0, fmp=1-2, ctx=3, a=4, b=5, c=6-9, d=10-13 + /// + /// Constraints: + /// - Boundary: c[2].first = 0 → column 8 at row 0 must be 0 + /// - Integrity: c[i] = d[i] for all rows and i in 0..4 + /// + /// We build a meaningful trace where c and d have matching non-zero values. + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + // Initialize all 14 columns + let mut trace: Vec> = vec![vec![Felt::ZERO; length]; 14]; + + // Fill with meaningful values + for row in 0..length { + // clk increments + trace[0][row] = Felt::new(row as u64); + + // fmp[0], fmp[1] - some values + trace[1][row] = Felt::new(row as u64 * 2); + trace[2][row] = Felt::new(row as u64 * 3); + + // ctx + trace[3][row] = Felt::new(100); + + // a, b - some values + trace[4][row] = Felt::new(row as u64 + 10); + trace[5][row] = Felt::new(row as u64 + 20); + + // c[0..4] - columns 6-9 + // c[2] (column 8) must be 0 at first row due to boundary constraint + trace[6][row] = Felt::new(row as u64 + 1); // c[0] + trace[7][row] = Felt::new(row as u64 + 2); // c[1] + trace[8][row] = if row == 0 { + Felt::ZERO // c[2] must be 0 at first row + } else { + Felt::new(row as u64 + 3) + }; + trace[9][row] = Felt::new(row as u64 + 4); // c[3] + + // d[0..4] - columns 10-13 + // Must equal c[0..4] due to integrity constraint: c[i] = d[i] + trace[10][row] = trace[6][row]; // d[0] = c[0] + trace[11][row] = trace[7][row]; // d[1] = c[1] + trace[12][row] = trace[8][row]; // d[2] = c[2] + trace[13][row] = trace[9][row]; // d[3] = c[3] + } + + trace + } +} + +impl CrossBackendTestConfig for ConstraintComprehensionTestConfig { + type WinterfellAir = WinterfellConstraintComprehensionAir; + type Plonky3Air = Plonky3ConstraintComprehensionAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 14 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn build_plonky3_public_inputs(&self) -> Vec { + vec![Goldilocks::ZERO; 16] + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellConstraintComprehensionAir { + WinterfellConstraintComprehensionAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3ConstraintComprehensionAir { + Plonky3ConstraintComprehensionAir + } + + fn num_public_values(&self) -> usize { + 16 + } +} + +// ============================================================================ +// Tests +// ============================================================================ + +#[test] +fn test_constraint_comprehension_air_constraint_comparison() { + let config = ConstraintComprehensionTestConfig::new(64); + let result = run_cross_backend_comparison(&config); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "ConstraintComprehension AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_constraint_comprehension_air_constraint_comparison_small_trace() { + let config = ConstraintComprehensionTestConfig::new(16); + let result = run_cross_backend_comparison(&config); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "ConstraintComprehension AIR comparison (16 rows) passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index de0da344a..69bd73734 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -6,4 +6,5 @@ mod binary; mod bitwise; mod constants; +mod constraint_comprehension; mod fibonacci; diff --git a/air-script/src/tests/constraint_comprehension/mod.rs b/air-script/src/tests/constraint_comprehension/mod.rs index 960d9a5a6..7095e16b9 100644 --- a/air-script/src/tests/constraint_comprehension/mod.rs +++ b/air-script/src/tests/constraint_comprehension/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod constraint_comprehension; +pub mod constraint_comprehension; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod constraint_comprehension_plonky3; +pub mod constraint_comprehension_plonky3; mod test_air_plonky3; mod test_air_winterfell; From a1a21440c0e15c547958e822b4ad5b2af266f4ec Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 9 Jan 2026 15:17:24 +0100 Subject: [PATCH 44/83] tests(compare-wf-p3): randomized tests --- air-script/Cargo.toml | 2 + .../test_utils/cross_backend_comparison.rs | 127 +++++++++++++++++- air-script/src/tests/comparison/binary.rs | 28 +++- air-script/src/tests/comparison/bitwise.rs | 28 +++- air-script/src/tests/comparison/constants.rs | 28 +++- .../comparison/constraint_comprehension.rs | 28 +++- air-script/src/tests/comparison/fibonacci.rs | 28 +++- parser/src/ast/mod.rs | 25 ++-- 8 files changed, 269 insertions(+), 25 deletions(-) diff --git a/air-script/Cargo.toml b/air-script/Cargo.toml index cb3346966..b5207f31e 100644 --- a/air-script/Cargo.toml +++ b/air-script/Cargo.toml @@ -76,3 +76,5 @@ winter-utils = { package = "winter-utils", version = "0.12", default-features = winter-prover = { package = "winter-prover", version = "0.12", default-features = false } winter-verifier = { package = "winter-verifier", version = "0.12", default-features = false } winterfell = { package = "winterfell", version = "0.12", default-features = false } +rand = { version = "0.9", features = ["small_rng"] } +rand_chacha = "0.9" diff --git a/air-script/src/test_utils/cross_backend_comparison.rs b/air-script/src/test_utils/cross_backend_comparison.rs index 4894ee9bf..170ed1cf3 100644 --- a/air-script/src/test_utils/cross_backend_comparison.rs +++ b/air-script/src/test_utils/cross_backend_comparison.rs @@ -17,12 +17,18 @@ //! For Winterfell, we directly call `evaluate_transition` and manually evaluate boundary //! constraints to produce comparable results. -use std::fmt; +use std::{ + collections::hash_map::DefaultHasher, + fmt, + hash::{Hash, Hasher}, +}; use p3_field::{Field, PrimeCharacteristicRing, PrimeField64}; use p3_goldilocks::Goldilocks; use p3_matrix::{Matrix, dense::RowMajorMatrix}; use p3_miden_air::{MidenAir, MidenAirBuilder}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; use winter_air::{ Air, BatchingMethod, EvaluationFrame, FieldExtension, ProofOptions as WinterProofOptions, TraceInfo, @@ -30,6 +36,28 @@ use winter_air::{ use winter_math::{FieldElement, ToElements, fields::f64::BaseElement as WinterfellFelt}; use winter_utils::Serializable; +// ============================================================================ +// Constants +// ============================================================================ + +/// The Goldilocks field modulus: 2^64 - 2^32 + 1 +const GOLDILOCKS_MODULUS: u64 = 0xFFFF_FFFF_0000_0001; + +// ============================================================================ +// Random Seed Generation +// ============================================================================ + +/// Generates a deterministic seed from a test name and iteration number. +/// +/// This ensures different tests get different random traces even with the same iteration, +/// making test results reproducible while avoiding trace collisions across tests. +pub fn generate_test_seed(test_name: &str, iteration: u64) -> u64 { + let mut hasher = DefaultHasher::new(); + test_name.hash(&mut hasher); + iteration.hash(&mut hasher); + hasher.finish() +} + // ============================================================================ // Canonical u64 Conversion // ============================================================================ @@ -750,6 +778,38 @@ pub trait CrossBackendTestConfig { fn periodic_column_values(&self) -> Vec> { vec![] } + + /// Builds a random trace in Winterfell format (column-major) using a seeded RNG. + /// + /// The seed is generated from the test name and iteration number using + /// [`generate_test_seed`], ensuring different tests get unique random traces + /// even when using the same iteration numbers. + /// + /// Default implementation generates uniformly random field elements in + /// the range `[0, GOLDILOCKS_MODULUS)`. Override this if your AIR has + /// specific requirements for trace structure. + /// + /// # Arguments + /// * `test_name` - Name of the test (used for seed generation) + /// * `iteration` - Iteration number (used for seed generation) + fn build_random_winterfell_trace( + &self, + test_name: &str, + iteration: u64, + ) -> Vec> { + let seed = generate_test_seed(test_name, iteration); + let mut rng = ChaCha8Rng::seed_from_u64(seed); + let width = self.trace_width(); + let length = self.trace_length(); + + (0..width) + .map(|_| { + (0..length) + .map(|_| WinterfellFelt::new(rng.random_range(0..GOLDILOCKS_MODULUS))) + .collect() + }) + .collect() + } } // ============================================================================ @@ -771,7 +831,7 @@ pub fn default_proof_options() -> WinterProofOptions { } // ============================================================================ -// Main Comparison Function +// Main Comparison Functions // ============================================================================ /// Runs a full cross-backend comparison for the given test configuration. @@ -793,10 +853,31 @@ pub fn run_cross_backend_comparison(config: &C) -> ComparisonResult where C: CrossBackendTestConfig, { - let trace_length = config.trace_length(); - - // Build trace in Winterfell format let winterfell_trace = config.build_winterfell_trace(); + run_cross_backend_comparison_with_trace(config, winterfell_trace) +} + +/// Runs a full cross-backend comparison using a provided trace. +/// +/// This is useful for testing with custom or random traces instead of +/// the valid trace generated by `build_winterfell_trace`. +/// +/// # Arguments +/// +/// * `config` - The test configuration implementing `CrossBackendTestConfig` +/// * `winterfell_trace` - The trace in Winterfell format (column-major) +/// +/// # Returns +/// +/// A `ComparisonResult` containing any mismatches found and statistics. +pub fn run_cross_backend_comparison_with_trace( + config: &C, + winterfell_trace: Vec>, +) -> ComparisonResult +where + C: CrossBackendTestConfig, +{ + let trace_length = config.trace_length(); // Convert to Plonky3 format let plonky3_trace: RowMajorMatrix = winterfell_trace_to_plonky3(&winterfell_trace); @@ -820,11 +901,13 @@ where // Get periodic column definitions let periodic_columns = config.periodic_column_values(); - // Evaluate constraints at each row + // Evaluate constraints at each row where transition constraints are enforced. + // Rows >= last_step have transition exemptions in Winterfell, so we skip them + // to ensure both backends are compared on rows with the same constraint semantics. let mut winterfell_results: Vec> = Vec::new(); let mut plonky3_results: Vec> = Vec::new(); - for row in 0..trace_length { + for row in 0..last_step { // Evaluate periodic values at this row for both backends let winterfell_periodic = evaluate_winterfell_periodic_at_row(&periodic_columns, row); let plonky3_periodic: Vec = @@ -874,6 +957,36 @@ where compare_evaluations_by_row(&winterfell_results, &plonky3_results) } +/// Runs a full cross-backend comparison using a randomly generated trace. +/// +/// This tests that both backends produce identical constraint evaluations +/// for random (invalid) inputs, verifying that the constraint logic matches +/// even when constraints are not satisfied. +/// +/// The random trace is generated deterministically from the test name and +/// iteration number, ensuring reproducible results. +/// +/// # Arguments +/// +/// * `config` - The test configuration implementing `CrossBackendTestConfig` +/// * `test_name` - Name of the test (used for seed generation) +/// * `iteration` - Iteration number (used for seed generation) +/// +/// # Returns +/// +/// A `ComparisonResult` containing any mismatches found and statistics. +pub fn run_cross_backend_comparison_random( + config: &C, + test_name: &str, + iteration: u64, +) -> ComparisonResult +where + C: CrossBackendTestConfig, +{ + let random_trace = config.build_random_winterfell_trace(test_name, iteration); + run_cross_backend_comparison_with_trace(config, random_trace) +} + /// Evaluates boundary constraints at a specific row for Winterfell. /// /// This version takes the explicit `last_step` value from Winterfell's AIR, diff --git a/air-script/src/tests/comparison/binary.rs b/air-script/src/tests/comparison/binary.rs index a16cc0697..4eb011b8c 100644 --- a/air-script/src/tests/comparison/binary.rs +++ b/air-script/src/tests/comparison/binary.rs @@ -9,7 +9,9 @@ use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{CrossBackendTestConfig, run_cross_backend_comparison}, + test_utils::cross_backend_comparison::{ + CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, + }, tests::binary::{ binary::{BinaryAir as WinterfellBinaryAir, PublicInputs}, binary_plonky3::BinaryAir as Plonky3BinaryAir, @@ -146,3 +148,27 @@ fn test_binary_air_constraint_comparison_start_one() { result.total_constraints_checked, result.total_rows ); } + +#[test] +fn test_binary_air_constraint_comparison_random_inputs() { + let config = BinaryTestConfig::new(0, 64); + + // Test with iterations 0 through 50 for thorough coverage + for iteration in 0u64..=50 { + let result = run_cross_backend_comparison_random( + &config, + "test_binary_air_constraint_comparison_random_inputs", + iteration, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("Binary AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/bitwise.rs b/air-script/src/tests/comparison/bitwise.rs index 3980dec58..99d740f16 100644 --- a/air-script/src/tests/comparison/bitwise.rs +++ b/air-script/src/tests/comparison/bitwise.rs @@ -16,7 +16,9 @@ use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{CrossBackendTestConfig, run_cross_backend_comparison}, + test_utils::cross_backend_comparison::{ + CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, + }, tests::bitwise::{ bitwise::{BitwiseAir as WinterfellBitwiseAir, PublicInputs}, bitwise_plonky3::BitwiseAir as Plonky3BitwiseAir, @@ -155,3 +157,27 @@ fn test_bitwise_air_constraint_comparison_larger_trace() { result.total_constraints_checked, result.total_rows ); } + +#[test] +fn test_bitwise_air_constraint_comparison_random_inputs() { + let config = BitwiseTestConfig::new(64); + + // Test with iterations 0 through 50 for thorough coverage + for iteration in 0u64..=50 { + let result = run_cross_backend_comparison_random( + &config, + "test_bitwise_air_constraint_comparison_random_inputs", + iteration, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("Bitwise AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/constants.rs b/air-script/src/tests/comparison/constants.rs index c2ad40e58..995cf5a3a 100644 --- a/air-script/src/tests/comparison/constants.rs +++ b/air-script/src/tests/comparison/constants.rs @@ -15,7 +15,9 @@ use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{CrossBackendTestConfig, run_cross_backend_comparison}, + test_utils::cross_backend_comparison::{ + CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, + }, tests::constants::{ constants::{ConstantsAir as WinterfellConstantsAir, PublicInputs}, constants_plonky3::ConstantsAir as Plonky3ConstantsAir, @@ -159,3 +161,27 @@ fn test_constants_air_constraint_comparison_longer_trace() { result.total_constraints_checked, result.total_rows ); } + +#[test] +fn test_constants_air_constraint_comparison_random_inputs() { + let config = ConstantsTestConfig::new(64); + + // Test with iterations 0 through 50 for thorough coverage + for iteration in 0u64..=50 { + let result = run_cross_backend_comparison_random( + &config, + "test_constants_air_constraint_comparison_random_inputs", + iteration, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("Constants AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/constraint_comprehension.rs b/air-script/src/tests/comparison/constraint_comprehension.rs index 786cc4565..87bcbff95 100644 --- a/air-script/src/tests/comparison/constraint_comprehension.rs +++ b/air-script/src/tests/comparison/constraint_comprehension.rs @@ -16,7 +16,9 @@ use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{CrossBackendTestConfig, run_cross_backend_comparison}, + test_utils::cross_backend_comparison::{ + CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, + }, tests::constraint_comprehension::{ constraint_comprehension::{ ConstraintComprehensionAir as WinterfellConstraintComprehensionAir, PublicInputs, @@ -171,3 +173,27 @@ fn test_constraint_comprehension_air_constraint_comparison_small_trace() { result.total_constraints_checked, result.total_rows ); } + +#[test] +fn test_constraint_comprehension_air_constraint_comparison_random_inputs() { + let config = ConstraintComprehensionTestConfig::new(64); + + // Test with iterations 0 through 50 for thorough coverage + for iteration in 0u64..=50 { + let result = run_cross_backend_comparison_random( + &config, + "test_constraint_comprehension_air_constraint_comparison_random_inputs", + iteration, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("ConstraintComprehension AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/fibonacci.rs b/air-script/src/tests/comparison/fibonacci.rs index b40f011fc..dba6e8194 100644 --- a/air-script/src/tests/comparison/fibonacci.rs +++ b/air-script/src/tests/comparison/fibonacci.rs @@ -13,7 +13,9 @@ use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{CrossBackendTestConfig, run_cross_backend_comparison}, + test_utils::cross_backend_comparison::{ + CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, + }, tests::fibonacci::{ fibonacci::{FibonacciAir as WinterfellFibonacciAir, PublicInputs}, fibonacci_plonky3::FibonacciAir as Plonky3FibonacciAir, @@ -184,3 +186,27 @@ fn test_fibonacci_air_constraint_comparison_larger_values() { result.total_constraints_checked, result.total_rows ); } + +#[test] +fn test_fibonacci_air_constraint_comparison_random_inputs() { + let config = FibonacciTestConfig::new(0, 1, 64); + + // Test with iterations 0 through 50 for thorough coverage + for iteration in 0u64..=50 { + let result = run_cross_backend_comparison_random( + &config, + "test_fibonacci_air_constraint_comparison_random_inputs", + iteration, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("Fibonacci AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/parser/src/ast/mod.rs b/parser/src/ast/mod.rs index 1686455e7..1447c7b3c 100644 --- a/parser/src/ast/mod.rs +++ b/parser/src/ast/mod.rs @@ -508,19 +508,18 @@ impl Library { // importing module, if it was parsed from disk. If no path is available, // we default to the current working directory. - let (real_path, source_dir) = match codemap - .name(imports.first().unwrap().span().source_id()) - { - // If we have no source span, default to the current working directory - Err(_) => (false, cwd.clone()), - // If the file is virtual, then we've either already parsed imports for this module, - // or we have to fall back to the current working directory, but we have no relative - // path from which to base our search. - Ok(FileName::Virtual(_)) => (false, cwd.clone()), - Ok(FileName::Real(path)) => { - (true, path.parent().unwrap_or_else(|| Path::new(".")).to_path_buf()) - }, - }; + let (real_path, source_dir) = + match codemap.name(imports.first().unwrap().span().source_id()) { + // If we have no source span, default to the current working directory + Err(_) => (false, cwd.clone()), + // If the file is virtual, then we've either already parsed imports for this module, + // or we have to fall back to the current working directory, but we have no relative + // path from which to base our search. + Ok(FileName::Virtual(_)) => (false, cwd.clone()), + Ok(FileName::Real(path)) => { + (true, path.parent().unwrap_or_else(|| Path::new(".")).to_path_buf()) + }, + }; // For each module imported, try to load the module from the library, if it is // unavailable we must do extra work to load it into the library, as From d2b767c5d8dc0b009b02a0d24261efb88bdcd499 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 9 Jan 2026 16:54:17 +0100 Subject: [PATCH 45/83] tests(compare-wf-p3): remove useless comments --- .../test_utils/cross_backend_comparison.rs | 48 ------------------- air-script/src/tests/comparison/binary.rs | 8 ---- air-script/src/tests/comparison/bitwise.rs | 8 ---- air-script/src/tests/comparison/constants.rs | 8 ---- .../comparison/constraint_comprehension.rs | 8 ---- air-script/src/tests/comparison/fibonacci.rs | 8 ---- 6 files changed, 88 deletions(-) diff --git a/air-script/src/test_utils/cross_backend_comparison.rs b/air-script/src/test_utils/cross_backend_comparison.rs index 170ed1cf3..677ca2849 100644 --- a/air-script/src/test_utils/cross_backend_comparison.rs +++ b/air-script/src/test_utils/cross_backend_comparison.rs @@ -36,17 +36,9 @@ use winter_air::{ use winter_math::{FieldElement, ToElements, fields::f64::BaseElement as WinterfellFelt}; use winter_utils::Serializable; -// ============================================================================ -// Constants -// ============================================================================ - /// The Goldilocks field modulus: 2^64 - 2^32 + 1 const GOLDILOCKS_MODULUS: u64 = 0xFFFF_FFFF_0000_0001; -// ============================================================================ -// Random Seed Generation -// ============================================================================ - /// Generates a deterministic seed from a test name and iteration number. /// /// This ensures different tests get different random traces even with the same iteration, @@ -58,10 +50,6 @@ pub fn generate_test_seed(test_name: &str, iteration: u64) -> u64 { hasher.finish() } -// ============================================================================ -// Canonical u64 Conversion -// ============================================================================ - /// Trait for converting field elements to their canonical u64 representation. /// /// This is used for comparing field elements across different backends that may @@ -82,10 +70,6 @@ impl ToCanonicalU64 for Goldilocks { } } -// ============================================================================ -// Constraint Mismatch Reporting -// ============================================================================ - /// Represents a single constraint evaluation mismatch between backends. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ConstraintMismatch { @@ -145,10 +129,6 @@ impl ComparisonResult { } } -// ============================================================================ -// Winterfell Constraint Evaluation -// ============================================================================ - /// Evaluates Winterfell transition constraints at a specific row. /// /// Returns a vector of constraint evaluation values as canonical u64. @@ -264,10 +244,6 @@ where result.iter().map(|e| (is_transition * *e).to_canonical_u64()).collect() } -// ============================================================================ -// Periodic Column Evaluation -// ============================================================================ - /// Evaluates periodic column values at a specific row. /// /// Periodic columns repeat with a given period. At row `r`, the value is @@ -391,10 +367,6 @@ where results } -// ============================================================================ -// Plonky3 Constraint Capturing Builder -// ============================================================================ - /// A view into two consecutive rows of the trace matrix for constraint evaluation. pub struct TwoRowMatrixView { current_row: Vec, @@ -574,10 +546,6 @@ impl MidenAirBuilder for ConstraintC } } -// ============================================================================ -// Trace Conversion Utilities -// ============================================================================ - /// Converts a Plonky3 RowMajorMatrix to a Winterfell-style column-major trace. pub fn plonky3_trace_to_winterfell(trace: &RowMajorMatrix) -> Vec> where @@ -621,10 +589,6 @@ pub fn winterfell_trace_to_plonky3( RowMajorMatrix::new(values, num_cols) } -// ============================================================================ -// High-Level Comparison Functions -// ============================================================================ - /// Compares constraint evaluations row by row. /// /// Returns a ComparisonResult with any mismatches found. @@ -693,10 +657,6 @@ pub fn compare_evaluations_by_row( } } -// ============================================================================ -// Cross-Backend Test Configuration Trait -// ============================================================================ - /// Trait for configuring cross-backend comparison tests. /// /// Implement this trait for each AIR to enable cross-backend constraint comparison. @@ -812,10 +772,6 @@ pub trait CrossBackendTestConfig { } } -// ============================================================================ -// Default Proof Options -// ============================================================================ - /// Creates default proof options for testing. pub fn default_proof_options() -> WinterProofOptions { WinterProofOptions::new( @@ -830,10 +786,6 @@ pub fn default_proof_options() -> WinterProofOptions { ) } -// ============================================================================ -// Main Comparison Functions -// ============================================================================ - /// Runs a full cross-backend comparison for the given test configuration. /// /// This function: diff --git a/air-script/src/tests/comparison/binary.rs b/air-script/src/tests/comparison/binary.rs index 4eb011b8c..e3d495fd1 100644 --- a/air-script/src/tests/comparison/binary.rs +++ b/air-script/src/tests/comparison/binary.rs @@ -18,10 +18,6 @@ use crate::{ }, }; -// ============================================================================ -// Test Configuration -// ============================================================================ - /// Configuration for Binary AIR cross-backend comparison tests. struct BinaryTestConfig { /// The starting value for the binary trace (0 or 1). @@ -115,10 +111,6 @@ impl CrossBackendTestConfig for BinaryTestConfig { } } -// ============================================================================ -// Tests -// ============================================================================ - #[test] fn test_binary_air_constraint_comparison() { let config = BinaryTestConfig::new(0, 64); diff --git a/air-script/src/tests/comparison/bitwise.rs b/air-script/src/tests/comparison/bitwise.rs index 99d740f16..06231c640 100644 --- a/air-script/src/tests/comparison/bitwise.rs +++ b/air-script/src/tests/comparison/bitwise.rs @@ -25,10 +25,6 @@ use crate::{ }, }; -// ============================================================================ -// Test Configuration -// ============================================================================ - /// Configuration for Bitwise AIR cross-backend comparison tests. struct BitwiseTestConfig { /// The trace length (must be divisible by the period 8). @@ -122,10 +118,6 @@ impl CrossBackendTestConfig for BitwiseTestConfig { } } -// ============================================================================ -// Tests -// ============================================================================ - #[test] fn test_bitwise_air_constraint_comparison() { // Use 64 rows (8 complete periods) diff --git a/air-script/src/tests/comparison/constants.rs b/air-script/src/tests/comparison/constants.rs index 995cf5a3a..2d15c55ab 100644 --- a/air-script/src/tests/comparison/constants.rs +++ b/air-script/src/tests/comparison/constants.rs @@ -24,10 +24,6 @@ use crate::{ }, }; -// ============================================================================ -// Test Configuration -// ============================================================================ - /// Configuration for Constants AIR cross-backend comparison tests. struct ConstantsTestConfig { /// The trace length. @@ -127,10 +123,6 @@ impl CrossBackendTestConfig for ConstantsTestConfig { } } -// ============================================================================ -// Tests -// ============================================================================ - #[test] fn test_constants_air_constraint_comparison() { let config = ConstantsTestConfig::new(64); diff --git a/air-script/src/tests/comparison/constraint_comprehension.rs b/air-script/src/tests/comparison/constraint_comprehension.rs index 87bcbff95..8c2f9b169 100644 --- a/air-script/src/tests/comparison/constraint_comprehension.rs +++ b/air-script/src/tests/comparison/constraint_comprehension.rs @@ -27,10 +27,6 @@ use crate::{ }, }; -// ============================================================================ -// Test Configuration -// ============================================================================ - /// Configuration for ConstraintComprehension AIR cross-backend comparison tests. struct ConstraintComprehensionTestConfig { /// The trace length. @@ -140,10 +136,6 @@ impl CrossBackendTestConfig for ConstraintComprehensionTestConfig { } } -// ============================================================================ -// Tests -// ============================================================================ - #[test] fn test_constraint_comprehension_air_constraint_comparison() { let config = ConstraintComprehensionTestConfig::new(64); diff --git a/air-script/src/tests/comparison/fibonacci.rs b/air-script/src/tests/comparison/fibonacci.rs index dba6e8194..b14c981d0 100644 --- a/air-script/src/tests/comparison/fibonacci.rs +++ b/air-script/src/tests/comparison/fibonacci.rs @@ -22,10 +22,6 @@ use crate::{ }, }; -// ============================================================================ -// Test Configuration -// ============================================================================ - /// Configuration for Fibonacci AIR cross-backend comparison tests. struct FibonacciTestConfig { /// The first Fibonacci number (fib_0). @@ -135,10 +131,6 @@ impl CrossBackendTestConfig for FibonacciTestConfig { } } -// ============================================================================ -// Tests -// ============================================================================ - #[test] fn test_fibonacci_air_constraint_comparison() { // Standard Fibonacci starting with 0, 1 From ed1417a5a22911863ef9a0d7ca90cca0e7f37b09 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 23 Jan 2026 16:54:34 +0100 Subject: [PATCH 46/83] tests(compare-wf-p3): cosss_module_constants test --- .../comparison/cross_module_constants.rs | 148 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + .../src/tests/cross_module_constants/mod.rs | 6 +- 3 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 air-script/src/tests/comparison/cross_module_constants.rs diff --git a/air-script/src/tests/comparison/cross_module_constants.rs b/air-script/src/tests/comparison/cross_module_constants.rs new file mode 100644 index 000000000..308f0f0ea --- /dev/null +++ b/air-script/src/tests/comparison/cross_module_constants.rs @@ -0,0 +1,148 @@ +//! Cross-backend comparison test for the CrossModuleConstants AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the CrossModuleConstants AIR at every row of the trace. +//! +//! The CrossModuleConstants AIR tests cross-module evaluators using constants: +//! - Boundary: `a.first = 0` +//! - Transition: `result = a + 2*b + 3*c + 4*d` +//! +//! Trace columns: [a, b, c, d, result] (5 total) +//! Column indices: a=0, b=1, c=2, d=3, result=4 + +use p3_field::PrimeCharacteristicRing; +use p3_goldilocks::Goldilocks; +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{ + CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, + }, + tests::cross_module_constants::{ + cross_mod_constants::{ + CrossModuleConstantsTest as WinterfellCrossModuleConstantsTest, PublicInputs, + }, + cross_mod_constants_plonky3::CrossModuleConstantsTest as Plonky3CrossModuleConstantsTest, + }, +}; + +/// Configuration for CrossModuleConstants AIR cross-backend comparison tests. +struct CrossModuleConstantsTestConfig { + /// The trace length. + trace_length: usize, +} + +impl CrossModuleConstantsTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 5]; + + for row in 0..length { + let a = if row == 0 { Felt::ZERO } else { Felt::new(row as u64) }; + let b = Felt::new((row + 1) as u64); + let c = Felt::new((row + 2) as u64); + let d = Felt::new((row + 3) as u64); + let result = a + b.double() + c * Felt::new(3) + d * Felt::new(4); + + trace[0][row] = a; + trace[1][row] = b; + trace[2][row] = c; + trace[3][row] = d; + trace[4][row] = result; + } + + trace + } + + fn expected_value(&self) -> Felt { + let trace = self.build_trace(); + let last_step = self.trace_length - 2; + trace[4][last_step] + } +} + +impl CrossBackendTestConfig for CrossModuleConstantsTestConfig { + type WinterfellAir = WinterfellCrossModuleConstantsTest; + type Plonky3Air = Plonky3CrossModuleConstantsTest; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 5 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([self.expected_value()]) + } + + fn build_plonky3_public_inputs(&self) -> Vec { + vec![Goldilocks::from_u64(self.expected_value().as_int())] + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellCrossModuleConstantsTest { + WinterfellCrossModuleConstantsTest::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3CrossModuleConstantsTest { + Plonky3CrossModuleConstantsTest + } + + fn num_public_values(&self) -> usize { + 1 + } +} + +#[test] +fn test_cross_module_constants_air_constraint_comparison() { + let config = CrossModuleConstantsTestConfig::new(64); + let result = run_cross_backend_comparison(&config); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "CrossModuleConstants AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_cross_module_constants_air_constraint_comparison_random_inputs() { + let config = CrossModuleConstantsTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_cross_backend_comparison_random( + &config, + "test_cross_module_constants_air_constraint_comparison_random_inputs", + iteration, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("CrossModuleConstants AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 69bd73734..cb1c4353b 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -7,4 +7,5 @@ mod binary; mod bitwise; mod constants; mod constraint_comprehension; +mod cross_module_constants; mod fibonacci; diff --git a/air-script/src/tests/cross_module_constants/mod.rs b/air-script/src/tests/cross_module_constants/mod.rs index e10ee53c7..34b9ca03f 100644 --- a/air-script/src/tests/cross_module_constants/mod.rs +++ b/air-script/src/tests/cross_module_constants/mod.rs @@ -1,3 +1,7 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod cross_mod_constants; +pub mod cross_mod_constants; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +pub mod cross_mod_constants_plonky3; From 10171caadd5c6402d8919d68ee1c4f05498fea05 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Tue, 27 Jan 2026 15:44:17 +0100 Subject: [PATCH 47/83] tests(compare-wf-p3): constant_in_range test --- .../src/tests/comparison/constant_in_range.rs | 158 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/constant_in_range/mod.rs | 4 +- 3 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/constant_in_range.rs diff --git a/air-script/src/tests/comparison/constant_in_range.rs b/air-script/src/tests/comparison/constant_in_range.rs new file mode 100644 index 000000000..dc7dbbbe3 --- /dev/null +++ b/air-script/src/tests/comparison/constant_in_range.rs @@ -0,0 +1,158 @@ +//! Cross-backend comparison test for the ConstantInRange AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the ConstantInRange AIR at every row of the trace. +//! +//! The ConstantInRange AIR tests comprehension over constant ranges: +//! - Boundary: `c[2].first = 0` +//! - Transition: `a = sum_{i=0..2}(i + b[i] - c[i] - d[i])` +//! +//! Trace columns: [a, b[3], c[4], d[4]] (12 total) +//! Column indices: a=0, b=1-3, c=4-7, d=8-11 + +use p3_field::PrimeCharacteristicRing; +use p3_goldilocks::Goldilocks; +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{ + CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, + }, + tests::constant_in_range::{ + constant_in_range::{ConstantInRangeAir as WinterfellConstantInRangeAir, PublicInputs}, + constant_in_range_plonky3::ConstantInRangeAir as Plonky3ConstantInRangeAir, + }, +}; + +/// Configuration for ConstantInRange AIR cross-backend comparison tests. +struct ConstantInRangeTestConfig { + /// The trace length. + trace_length: usize, +} + +impl ConstantInRangeTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 12]; + + for row in 0..length { + let mut b = [Felt::ZERO; 3]; + let mut c = [Felt::ZERO; 3]; + let mut d = [Felt::ZERO; 3]; + + for i in 0..3 { + b[i] = Felt::new((row as u64) + (i as u64) + 1); + c[i] = if row == 0 && i == 2 { + Felt::ZERO + } else { + Felt::new((row as u64) + (i as u64) + 10) + }; + d[i] = Felt::new((row as u64) + (i as u64) + 20); + } + + let a = + (0..3).fold(Felt::ZERO, |acc, i| acc + Felt::new(i as u64) + b[i] - c[i] - d[i]); + + trace[0][row] = a; + trace[1][row] = b[0]; + trace[2][row] = b[1]; + trace[3][row] = b[2]; + trace[4][row] = c[0]; + trace[5][row] = c[1]; + trace[6][row] = c[2]; + trace[7][row] = Felt::new((row as u64) + 30); + trace[8][row] = d[0]; + trace[9][row] = d[1]; + trace[10][row] = d[2]; + trace[11][row] = Felt::new((row as u64) + 40); + } + + trace + } +} + +impl CrossBackendTestConfig for ConstantInRangeTestConfig { + type WinterfellAir = WinterfellConstantInRangeAir; + type Plonky3Air = Plonky3ConstantInRangeAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 12 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn build_plonky3_public_inputs(&self) -> Vec { + vec![Goldilocks::ZERO; 16] + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellConstantInRangeAir { + WinterfellConstantInRangeAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3ConstantInRangeAir { + Plonky3ConstantInRangeAir + } + + fn num_public_values(&self) -> usize { + 16 + } +} + +#[test] +fn test_constant_in_range_air_constraint_comparison() { + let config = ConstantInRangeTestConfig::new(64); + let result = run_cross_backend_comparison(&config); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "ConstantInRange AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_constant_in_range_air_constraint_comparison_random_inputs() { + let config = ConstantInRangeTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_cross_backend_comparison_random( + &config, + "test_constant_in_range_air_constraint_comparison_random_inputs", + iteration, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("ConstantInRange AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index cb1c4353b..9b19e057f 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -5,6 +5,7 @@ mod binary; mod bitwise; +mod constant_in_range; mod constants; mod constraint_comprehension; mod cross_module_constants; diff --git a/air-script/src/tests/constant_in_range/mod.rs b/air-script/src/tests/constant_in_range/mod.rs index 68a0d2cbd..de99a6c37 100644 --- a/air-script/src/tests/constant_in_range/mod.rs +++ b/air-script/src/tests/constant_in_range/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod constant_in_range; +pub mod constant_in_range; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod constant_in_range_plonky3; +pub mod constant_in_range_plonky3; mod test_air_plonky3; mod test_air_winterfell; From 2b6fe0a7ea22063d38de7bcbc7c3b3863b2bb4e8 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 15:54:19 +0100 Subject: [PATCH 48/83] tests(compare-wf-p3): simplify test harness --- .../test_utils/cross_backend_comparison.rs | 155 ++++-------------- 1 file changed, 33 insertions(+), 122 deletions(-) diff --git a/air-script/src/test_utils/cross_backend_comparison.rs b/air-script/src/test_utils/cross_backend_comparison.rs index 677ca2849..5ff97c000 100644 --- a/air-script/src/test_utils/cross_backend_comparison.rs +++ b/air-script/src/test_utils/cross_backend_comparison.rs @@ -129,73 +129,12 @@ impl ComparisonResult { } } -/// Evaluates Winterfell transition constraints at a specific row. -/// -/// Returns a vector of constraint evaluation values as canonical u64. -/// -/// Note: This does NOT apply the `is_transition` multiplier. Use -/// `evaluate_winterfell_transition_at_row_with_selector` if you need -/// the results to be comparable with Plonky3's `when_transition()` behavior. -pub fn evaluate_winterfell_transition_at_row( - air: &A, - trace: &[Vec], - row: usize, -) -> Vec -where - A: Air, -{ - let trace_width = trace.len(); - let trace_length = trace[0].len(); - - // Build current and next row data - let current: Vec = (0..trace_width).map(|col| trace[col][row]).collect(); - - let next_row = (row + 1) % trace_length; - let next: Vec = (0..trace_width).map(|col| trace[col][next_row]).collect(); - - // Create evaluation frame - let frame = EvaluationFrame::from_rows(current, next); - - // Allocate result buffer based on number of transition constraints - let num_constraints = air.context().num_transition_constraints(); - let mut result = vec![WinterfellFelt::ZERO; num_constraints]; - - // Evaluate transition constraints (empty periodic values for simple AIRs) - let periodic_values: Vec = vec![]; - air.evaluate_transition(&frame, &periodic_values, &mut result); - - // Convert to canonical u64 - result.iter().map(|e| e.to_canonical_u64()).collect() -} - -/// Evaluates Winterfell transition constraints at a specific row, -/// with each result multiplied by `is_transition`. -/// -/// This makes the results comparable with Plonky3's `when_transition()` behavior, -/// where constraints are multiplied by a selector that is 1 on all rows except -/// the last one. -/// -/// Note: This applies the selector to ALL transition constraints, including -/// those that are NOT wrapped in `when_transition()` in Plonky3. For valid -/// traces where all constraints evaluate to 0, this doesn't matter. For -/// invalid traces or debugging, be aware of this difference. -pub fn evaluate_winterfell_transition_at_row_with_selector( - air: &A, - trace: &[Vec], - row: usize, - num_rows: usize, -) -> Vec -where - A: Air, -{ - // Use the version with periodic values, passing empty periodic values - evaluate_winterfell_transition_at_row_with_periodic(air, trace, row, num_rows, &[]) -} - /// Evaluates Winterfell transition constraints at a specific row, /// with periodic column values and `is_transition` selector applied. /// -/// This is the full-featured version that supports periodic columns. +/// This is the unified evaluation function that supports: +/// - Periodic columns (pass values via `periodic_values`) +/// - Transition selectors (applied automatically) /// /// # Arguments /// @@ -203,8 +142,8 @@ where /// * `trace` - The trace in column-major format /// * `row` - The row to evaluate at /// * `num_rows` - Total number of rows in the trace -/// * `periodic_values` - The periodic column values evaluated at this row -pub fn evaluate_winterfell_transition_at_row_with_periodic( +/// * `periodic_values` - The periodic column values evaluated at this row (empty for simple AIRs) +pub fn evaluate_winterfell_transition( air: &A, trace: &[Vec], row: usize, @@ -319,54 +258,6 @@ where .collect() } -/// Evaluates boundary constraints at a specific row for Winterfell. -/// Returns the constraint evaluation (actual - expected) for each boundary constraint -/// that applies to this row, multiplied by the first_row indicator (like Plonky3 does). -pub fn evaluate_winterfell_boundary_at_row( - air: &A, - trace: &[Vec], - row: usize, - num_rows: usize, -) -> Vec -where - A: Air, -{ - let assertions = get_winterfell_boundary_assertions(air); - let mut results = Vec::new(); - - for (col, assertion_row, expected) in assertions { - // Compute (actual - expected) - let actual = trace[col][row].to_canonical_u64(); - - // For first row constraints: multiply by is_first_row indicator - // For last row constraints: multiply by is_last_row indicator - let is_first_row = if row == 0 { 1u64 } else { 0u64 }; - let is_last_row = if row == num_rows - 1 { 1u64 } else { 0u64 }; - - if assertion_row == 0 { - // First row boundary constraint - // Plonky3 computes: is_first_row * (actual - expected) - // We need to do the same arithmetic in the field - let actual_felt = WinterfellFelt::new(actual); - let expected_felt = WinterfellFelt::new(expected); - let is_first_felt = WinterfellFelt::new(is_first_row); - let diff = actual_felt - expected_felt; - let result = is_first_felt * diff; - results.push(result.to_canonical_u64()); - } else if assertion_row == num_rows - 1 { - // Last row boundary constraint - let actual_felt = WinterfellFelt::new(actual); - let expected_felt = WinterfellFelt::new(expected); - let is_last_felt = WinterfellFelt::new(is_last_row); - let diff = actual_felt - expected_felt; - let result = is_last_felt * diff; - results.push(result.to_canonical_u64()); - } - } - - results -} - /// A view into two consecutive rows of the trace matrix for constraint evaluation. pub struct TwoRowMatrixView { current_row: Vec, @@ -701,7 +592,16 @@ pub trait CrossBackendTestConfig { fn build_winterfell_public_inputs(&self) -> Self::WinterfellPublicInputs; /// Builds the Plonky3 public inputs. - fn build_plonky3_public_inputs(&self) -> Vec; + /// + /// Default implementation auto-converts from Winterfell public inputs. + /// Override this if you need custom conversion logic. + fn build_plonky3_public_inputs(&self) -> Vec { + self.build_winterfell_public_inputs() + .to_elements() + .into_iter() + .map(|felt| Goldilocks::from_u64(felt.as_int())) + .collect() + } /// Creates the Winterfell AIR instance. fn create_winterfell_air( @@ -715,7 +615,11 @@ pub trait CrossBackendTestConfig { fn create_plonky3_air(&self) -> Self::Plonky3Air; /// Returns the number of public values for Plonky3. - fn num_public_values(&self) -> usize; + /// + /// Default implementation returns the length of Plonky3 public inputs. + fn num_public_values(&self) -> usize { + self.build_plonky3_public_inputs().len() + } /// Returns the periodic column values (empty by default). /// @@ -866,7 +770,7 @@ where evaluate_periodic_values_at_row(&periodic_columns, row); // Winterfell: evaluate boundary constraints - let w_boundary = evaluate_winterfell_boundary_at_row_with_last_step( + let w_boundary = evaluate_winterfell_boundary( &winterfell_air, &winterfell_trace, row, @@ -876,7 +780,7 @@ where // Winterfell: evaluate transition constraints with is_transition selector // and periodic values - let w_transition = evaluate_winterfell_transition_at_row_with_periodic( + let w_transition = evaluate_winterfell_transition( &winterfell_air, &winterfell_trace, row, @@ -941,10 +845,17 @@ where /// Evaluates boundary constraints at a specific row for Winterfell. /// -/// This version takes the explicit `last_step` value from Winterfell's AIR, -/// which accounts for transition exemptions when determining when last-row -/// boundary constraints should apply. -pub fn evaluate_winterfell_boundary_at_row_with_last_step( +/// This function evaluates boundary constraints with proper handling of the last step +/// from Winterfell's AIR context, which accounts for transition exemptions when +/// determining when last-row boundary constraints should apply. +/// +/// # Arguments +/// * `air` - The Winterfell AIR instance +/// * `trace` - The trace in column-major format +/// * `row` - The row to evaluate at +/// * `num_rows` - Total number of rows in the trace +/// * `last_step` - The last step index from AIR context (accounts for transition exemptions) +pub fn evaluate_winterfell_boundary( air: &A, trace: &[Vec], row: usize, From ce0fff2d71fa890212aa1dec4560b0f4f903d6bb Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 16:00:14 +0100 Subject: [PATCH 49/83] tests(compare-wf-p3): simplify all tests --- air-script/src/tests/comparison/binary.rs | 18 ------------------ air-script/src/tests/comparison/bitwise.rs | 10 ---------- .../src/tests/comparison/constant_in_range.rs | 10 ---------- air-script/src/tests/comparison/constants.rs | 10 ---------- .../comparison/constraint_comprehension.rs | 10 ---------- .../tests/comparison/cross_module_constants.rs | 10 ---------- air-script/src/tests/comparison/fibonacci.rs | 15 --------------- 7 files changed, 83 deletions(-) diff --git a/air-script/src/tests/comparison/binary.rs b/air-script/src/tests/comparison/binary.rs index e3d495fd1..46982cd1e 100644 --- a/air-script/src/tests/comparison/binary.rs +++ b/air-script/src/tests/comparison/binary.rs @@ -3,8 +3,6 @@ //! This test verifies that Winterfell and Plonky3 produce equivalent //! constraint evaluations for the Binary AIR at every row of the trace. -use p3_field::PrimeCharacteristicRing; -use p3_goldilocks::Goldilocks; use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; @@ -81,18 +79,6 @@ impl CrossBackendTestConfig for BinaryTestConfig { PublicInputs::new(inputs) } - fn build_plonky3_public_inputs(&self) -> Vec { - (0..16) - .map(|i| { - if i == 0 { - Goldilocks::from_u64(self.start_value) - } else { - Goldilocks::ZERO - } - }) - .collect() - } - fn create_winterfell_air( &self, trace_info: TraceInfo, @@ -105,10 +91,6 @@ impl CrossBackendTestConfig for BinaryTestConfig { fn create_plonky3_air(&self) -> Plonky3BinaryAir { Plonky3BinaryAir } - - fn num_public_values(&self) -> usize { - 16 - } } #[test] diff --git a/air-script/src/tests/comparison/bitwise.rs b/air-script/src/tests/comparison/bitwise.rs index 06231c640..cda7f16c1 100644 --- a/air-script/src/tests/comparison/bitwise.rs +++ b/air-script/src/tests/comparison/bitwise.rs @@ -10,8 +10,6 @@ //! This test validates that the periodic column evaluation works correctly //! in the cross-backend comparison framework. -use p3_field::PrimeCharacteristicRing; -use p3_goldilocks::Goldilocks; use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; @@ -84,10 +82,6 @@ impl CrossBackendTestConfig for BitwiseTestConfig { PublicInputs::new([Felt::ZERO; 16]) } - fn build_plonky3_public_inputs(&self) -> Vec { - vec![Goldilocks::ZERO; 16] - } - fn create_winterfell_air( &self, trace_info: TraceInfo, @@ -101,10 +95,6 @@ impl CrossBackendTestConfig for BitwiseTestConfig { Plonky3BitwiseAir } - fn num_public_values(&self) -> usize { - 16 - } - /// Returns the periodic column values for the Bitwise AIR. /// /// Two periodic columns with period 8: diff --git a/air-script/src/tests/comparison/constant_in_range.rs b/air-script/src/tests/comparison/constant_in_range.rs index dc7dbbbe3..b586deecb 100644 --- a/air-script/src/tests/comparison/constant_in_range.rs +++ b/air-script/src/tests/comparison/constant_in_range.rs @@ -10,8 +10,6 @@ //! Trace columns: [a, b[3], c[4], d[4]] (12 total) //! Column indices: a=0, b=1-3, c=4-7, d=8-11 -use p3_field::PrimeCharacteristicRing; -use p3_goldilocks::Goldilocks; use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; @@ -97,10 +95,6 @@ impl CrossBackendTestConfig for ConstantInRangeTestConfig { PublicInputs::new([Felt::ZERO; 16]) } - fn build_plonky3_public_inputs(&self) -> Vec { - vec![Goldilocks::ZERO; 16] - } - fn create_winterfell_air( &self, trace_info: TraceInfo, @@ -113,10 +107,6 @@ impl CrossBackendTestConfig for ConstantInRangeTestConfig { fn create_plonky3_air(&self) -> Plonky3ConstantInRangeAir { Plonky3ConstantInRangeAir } - - fn num_public_values(&self) -> usize { - 16 - } } #[test] diff --git a/air-script/src/tests/comparison/constants.rs b/air-script/src/tests/comparison/constants.rs index 2d15c55ab..6038127a8 100644 --- a/air-script/src/tests/comparison/constants.rs +++ b/air-script/src/tests/comparison/constants.rs @@ -9,8 +9,6 @@ //! - `when_transition()` transition constraints (4 constraints) //! - Global integrity constraints (1 constraint without `when_transition()`) -use p3_field::PrimeCharacteristicRing; -use p3_goldilocks::Goldilocks; use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; @@ -101,10 +99,6 @@ impl CrossBackendTestConfig for ConstantsTestConfig { PublicInputs::new([Felt::ZERO; 4], [Felt::ZERO; 4], [Felt::ZERO; 4], [Felt::ZERO; 20]) } - fn build_plonky3_public_inputs(&self) -> Vec { - vec![Goldilocks::ZERO; 32] - } - fn create_winterfell_air( &self, trace_info: TraceInfo, @@ -117,10 +111,6 @@ impl CrossBackendTestConfig for ConstantsTestConfig { fn create_plonky3_air(&self) -> Plonky3ConstantsAir { Plonky3ConstantsAir } - - fn num_public_values(&self) -> usize { - 32 - } } #[test] diff --git a/air-script/src/tests/comparison/constraint_comprehension.rs b/air-script/src/tests/comparison/constraint_comprehension.rs index 8c2f9b169..ae9bf27ef 100644 --- a/air-script/src/tests/comparison/constraint_comprehension.rs +++ b/air-script/src/tests/comparison/constraint_comprehension.rs @@ -10,8 +10,6 @@ //! Trace columns: [clk, fmp[2], ctx, a, b, c[4], d[4]] (14 total) //! Column indices: clk=0, fmp=1-2, ctx=3, a=4, b=5, c=6-9, d=10-13 -use p3_field::PrimeCharacteristicRing; -use p3_goldilocks::Goldilocks; use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; @@ -114,10 +112,6 @@ impl CrossBackendTestConfig for ConstraintComprehensionTestConfig { PublicInputs::new([Felt::ZERO; 16]) } - fn build_plonky3_public_inputs(&self) -> Vec { - vec![Goldilocks::ZERO; 16] - } - fn create_winterfell_air( &self, trace_info: TraceInfo, @@ -130,10 +124,6 @@ impl CrossBackendTestConfig for ConstraintComprehensionTestConfig { fn create_plonky3_air(&self) -> Plonky3ConstraintComprehensionAir { Plonky3ConstraintComprehensionAir } - - fn num_public_values(&self) -> usize { - 16 - } } #[test] diff --git a/air-script/src/tests/comparison/cross_module_constants.rs b/air-script/src/tests/comparison/cross_module_constants.rs index 308f0f0ea..ccb8918a3 100644 --- a/air-script/src/tests/comparison/cross_module_constants.rs +++ b/air-script/src/tests/comparison/cross_module_constants.rs @@ -10,8 +10,6 @@ //! Trace columns: [a, b, c, d, result] (5 total) //! Column indices: a=0, b=1, c=2, d=3, result=4 -use p3_field::PrimeCharacteristicRing; -use p3_goldilocks::Goldilocks; use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; @@ -87,10 +85,6 @@ impl CrossBackendTestConfig for CrossModuleConstantsTestConfig { PublicInputs::new([self.expected_value()]) } - fn build_plonky3_public_inputs(&self) -> Vec { - vec![Goldilocks::from_u64(self.expected_value().as_int())] - } - fn create_winterfell_air( &self, trace_info: TraceInfo, @@ -103,10 +97,6 @@ impl CrossBackendTestConfig for CrossModuleConstantsTestConfig { fn create_plonky3_air(&self) -> Plonky3CrossModuleConstantsTest { Plonky3CrossModuleConstantsTest } - - fn num_public_values(&self) -> usize { - 1 - } } #[test] diff --git a/air-script/src/tests/comparison/fibonacci.rs b/air-script/src/tests/comparison/fibonacci.rs index b14c981d0..eaa319e89 100644 --- a/air-script/src/tests/comparison/fibonacci.rs +++ b/air-script/src/tests/comparison/fibonacci.rs @@ -7,8 +7,6 @@ //! - Boundary: `a.first = stack_inputs[0]`, `b.first = stack_inputs[1]`, `b.last = stack_output[0]` //! - Transition: `b' = a + b`, `a' = b` -use p3_field::PrimeCharacteristicRing; -use p3_goldilocks::Goldilocks; use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; @@ -104,15 +102,6 @@ impl CrossBackendTestConfig for FibonacciTestConfig { PublicInputs::new(stack_inputs, stack_output) } - fn build_plonky3_public_inputs(&self) -> Vec { - // Plonky3 public inputs: [stack_inputs[0], stack_inputs[1], stack_output[0]] - vec![ - Goldilocks::from_u64(self.fib_0), - Goldilocks::from_u64(self.fib_1), - Goldilocks::from_u64(self.expected_output().as_int()), - ] - } - fn create_winterfell_air( &self, trace_info: TraceInfo, @@ -125,10 +114,6 @@ impl CrossBackendTestConfig for FibonacciTestConfig { fn create_plonky3_air(&self) -> Plonky3FibonacciAir { Plonky3FibonacciAir } - - fn num_public_values(&self) -> usize { - 3 // stack_inputs[0], stack_inputs[1], stack_output[0] - } } #[test] From 6ba1aac913aa355b51cf79ff511e7dd0f4185e59 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 16:33:04 +0100 Subject: [PATCH 50/83] tests(compare-p3-wf): unify trace generation/passing --- .../test_utils/cross_backend_comparison.rs | 178 ++++++++++++------ air-script/src/tests/comparison/binary.rs | 16 +- air-script/src/tests/comparison/bitwise.rs | 16 +- .../src/tests/comparison/constant_in_range.rs | 14 +- air-script/src/tests/comparison/constants.rs | 16 +- .../comparison/constraint_comprehension.rs | 16 +- .../comparison/cross_module_constants.rs | 14 +- air-script/src/tests/comparison/fibonacci.rs | 18 +- 8 files changed, 175 insertions(+), 113 deletions(-) diff --git a/air-script/src/test_utils/cross_backend_comparison.rs b/air-script/src/test_utils/cross_backend_comparison.rs index 5ff97c000..8abdc11c6 100644 --- a/air-script/src/test_utils/cross_backend_comparison.rs +++ b/air-script/src/test_utils/cross_backend_comparison.rs @@ -43,6 +43,12 @@ const GOLDILOCKS_MODULUS: u64 = 0xFFFF_FFFF_0000_0001; /// /// This ensures different tests get different random traces even with the same iteration, /// making test results reproducible while avoiding trace collisions across tests. +/// +/// # Examples +/// ```ignore +/// let seed = generate_test_seed("my_test", 0); +/// let rng = ChaCha8Rng::seed_from_u64(seed); +/// ``` pub fn generate_test_seed(test_name: &str, iteration: u64) -> u64 { let mut hasher = DefaultHasher::new(); test_name.hash(&mut hasher); @@ -129,6 +135,39 @@ impl ComparisonResult { } } +/// Specifies the source of trace data for cross-backend comparison. +/// +/// # Examples +/// ```ignore +/// let result = run_comparison(&config, TraceSource::Default); +/// ``` +/// +/// ```ignore +/// let result = run_comparison( +/// &config, +/// TraceSource::Random { test_name: "my_test", iteration: 0 }, +/// ); +/// ``` +/// +/// ```ignore +/// let trace = vec![vec![WinterfellFelt::ZERO; 64]; 2]; +/// let result = run_comparison(&config, TraceSource::Custom(&trace)); +/// ``` +#[derive(Debug, Clone)] +pub enum TraceSource<'a> { + /// Use the trace from `config.build_winterfell_trace()`. + Default, + /// Generate a random trace with the given seed parameters. + Random { + /// Name of the test (used for seed generation). + test_name: &'static str, + /// Iteration number (used for seed generation). + iteration: u64, + }, + /// Use a custom provided trace. + Custom(&'a [Vec]), +} + /// Evaluates Winterfell transition constraints at a specific row, /// with periodic column values and `is_transition` selector applied. /// @@ -143,6 +182,12 @@ impl ComparisonResult { /// * `row` - The row to evaluate at /// * `num_rows` - Total number of rows in the trace /// * `periodic_values` - The periodic column values evaluated at this row (empty for simple AIRs) +/// +/// # Examples +/// ```ignore +/// let periodic = vec![WinterfellFelt::ONE]; +/// let values = evaluate_winterfell_transition(&air, &trace, 0, trace_len, &periodic); +/// ``` pub fn evaluate_winterfell_transition( air: &A, trace: &[Vec], @@ -196,6 +241,12 @@ where /// # Returns /// /// A vector of field elements, one for each periodic column, evaluated at the given row. +/// +/// # Examples +/// ```ignore +/// let columns = vec![vec![1, 0, 0, 0], vec![1, 1, 1, 0]]; +/// let values: Vec = evaluate_periodic_values_at_row(&columns, 5); +/// ``` pub fn evaluate_periodic_values_at_row( periodic_columns: &[Vec], row: usize, @@ -215,6 +266,12 @@ pub fn evaluate_periodic_values_at_row( } /// Evaluates periodic column values at a specific row for Winterfell (WinterfellFelt). +/// +/// # Examples +/// ```ignore +/// let columns = vec![vec![1, 0, 0, 0]]; +/// let values = evaluate_winterfell_periodic_at_row(&columns, 2); +/// ``` pub fn evaluate_winterfell_periodic_at_row( periodic_columns: &[Vec], row: usize, @@ -235,6 +292,14 @@ pub fn evaluate_winterfell_periodic_at_row( /// Gets Winterfell boundary constraint info. /// Returns (column, row, expected_value) for each assertion. +/// +/// # Examples +/// ```ignore +/// let assertions = get_winterfell_boundary_assertions(&air); +/// for (col, row, expected) in assertions { +/// println!("col={col}, row={row}, expected={expected}"); +/// } +/// ``` pub fn get_winterfell_boundary_assertions(air: &A) -> Vec<(usize, usize, u64)> where A: Air, @@ -438,6 +503,12 @@ impl MidenAirBuilder for ConstraintC } /// Converts a Plonky3 RowMajorMatrix to a Winterfell-style column-major trace. +/// +/// # Examples +/// ```ignore +/// let trace = RowMajorMatrix::new(vec![Goldilocks::ZERO; 8], 2); +/// let winterfell_trace = plonky3_trace_to_winterfell(&trace); +/// ``` pub fn plonky3_trace_to_winterfell(trace: &RowMajorMatrix) -> Vec> where F: PrimeField64 + Clone + Send + Sync, @@ -458,6 +529,12 @@ where } /// Converts a Winterfell column-major trace to a Plonky3 RowMajorMatrix. +/// +/// # Examples +/// ```ignore +/// let trace = vec![vec![WinterfellFelt::ZERO; 4]; 2]; +/// let plonky3_trace: RowMajorMatrix = winterfell_trace_to_plonky3(&trace); +/// ``` pub fn winterfell_trace_to_plonky3( trace: &[Vec], ) -> RowMajorMatrix { @@ -483,6 +560,14 @@ pub fn winterfell_trace_to_plonky3( /// Compares constraint evaluations row by row. /// /// Returns a ComparisonResult with any mismatches found. +/// +/// # Examples +/// ```ignore +/// let winterfell = vec![vec![0u64, 1u64]]; +/// let plonky3 = vec![vec![0u64, 1u64]]; +/// let result = compare_evaluations_by_row(&winterfell, &plonky3); +/// assert!(result.is_ok()); +/// ``` pub fn compare_evaluations_by_row( winterfell_evals: &[Vec], plonky3_evals: &[Vec], @@ -677,6 +762,11 @@ pub trait CrossBackendTestConfig { } /// Creates default proof options for testing. +/// +/// # Examples +/// ```ignore +/// let options = default_proof_options(); +/// ``` pub fn default_proof_options() -> WinterProofOptions { WinterProofOptions::new( 32, // number of queries @@ -690,10 +780,10 @@ pub fn default_proof_options() -> WinterProofOptions { ) } -/// Runs a full cross-backend comparison for the given test configuration. +/// Runs a full cross-backend comparison. /// /// This function: -/// 1. Builds the trace using the config +/// 1. Builds or selects a trace based on `source` /// 2. Creates both Winterfell and Plonky3 AIR instances /// 3. Evaluates all constraints at each row for both backends /// 4. Compares the results and returns a detailed report @@ -701,38 +791,35 @@ pub fn default_proof_options() -> WinterProofOptions { /// # Arguments /// /// * `config` - The test configuration implementing `CrossBackendTestConfig` +/// * `source` - Where to get the trace data from /// -/// # Returns +/// # Examples +/// ```ignore +/// let result = run_comparison(&config, TraceSource::Default); +/// assert!(result.is_ok()); +/// ``` /// -/// A `ComparisonResult` containing any mismatches found and statistics. -pub fn run_cross_backend_comparison(config: &C) -> ComparisonResult +/// ```ignore +/// let result = run_comparison( +/// &config, +/// TraceSource::Random { +/// test_name: "my_test", +/// iteration: 0, +/// }, +/// ); +/// ``` +pub fn run_comparison(config: &C, source: TraceSource<'_>) -> ComparisonResult where C: CrossBackendTestConfig, { - let winterfell_trace = config.build_winterfell_trace(); - run_cross_backend_comparison_with_trace(config, winterfell_trace) -} + let winterfell_trace = match source { + TraceSource::Default => config.build_winterfell_trace(), + TraceSource::Random { test_name, iteration } => { + config.build_random_winterfell_trace(test_name, iteration) + }, + TraceSource::Custom(trace) => trace.to_vec(), + }; -/// Runs a full cross-backend comparison using a provided trace. -/// -/// This is useful for testing with custom or random traces instead of -/// the valid trace generated by `build_winterfell_trace`. -/// -/// # Arguments -/// -/// * `config` - The test configuration implementing `CrossBackendTestConfig` -/// * `winterfell_trace` - The trace in Winterfell format (column-major) -/// -/// # Returns -/// -/// A `ComparisonResult` containing any mismatches found and statistics. -pub fn run_cross_backend_comparison_with_trace( - config: &C, - winterfell_trace: Vec>, -) -> ComparisonResult -where - C: CrossBackendTestConfig, -{ let trace_length = config.trace_length(); // Convert to Plonky3 format @@ -813,36 +900,6 @@ where compare_evaluations_by_row(&winterfell_results, &plonky3_results) } -/// Runs a full cross-backend comparison using a randomly generated trace. -/// -/// This tests that both backends produce identical constraint evaluations -/// for random (invalid) inputs, verifying that the constraint logic matches -/// even when constraints are not satisfied. -/// -/// The random trace is generated deterministically from the test name and -/// iteration number, ensuring reproducible results. -/// -/// # Arguments -/// -/// * `config` - The test configuration implementing `CrossBackendTestConfig` -/// * `test_name` - Name of the test (used for seed generation) -/// * `iteration` - Iteration number (used for seed generation) -/// -/// # Returns -/// -/// A `ComparisonResult` containing any mismatches found and statistics. -pub fn run_cross_backend_comparison_random( - config: &C, - test_name: &str, - iteration: u64, -) -> ComparisonResult -where - C: CrossBackendTestConfig, -{ - let random_trace = config.build_random_winterfell_trace(test_name, iteration); - run_cross_backend_comparison_with_trace(config, random_trace) -} - /// Evaluates boundary constraints at a specific row for Winterfell. /// /// This function evaluates boundary constraints with proper handling of the last step @@ -855,6 +912,11 @@ where /// * `row` - The row to evaluate at /// * `num_rows` - Total number of rows in the trace /// * `last_step` - The last step index from AIR context (accounts for transition exemptions) +/// +/// # Examples +/// ```ignore +/// let values = evaluate_winterfell_boundary(&air, &trace, 0, trace_len, last_step); +/// ``` pub fn evaluate_winterfell_boundary( air: &A, trace: &[Vec], diff --git a/air-script/src/tests/comparison/binary.rs b/air-script/src/tests/comparison/binary.rs index 46982cd1e..7d93a4398 100644 --- a/air-script/src/tests/comparison/binary.rs +++ b/air-script/src/tests/comparison/binary.rs @@ -7,9 +7,7 @@ use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{ - CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, - }, + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, tests::binary::{ binary::{BinaryAir as WinterfellBinaryAir, PublicInputs}, binary_plonky3::BinaryAir as Plonky3BinaryAir, @@ -96,7 +94,7 @@ impl CrossBackendTestConfig for BinaryTestConfig { #[test] fn test_binary_air_constraint_comparison() { let config = BinaryTestConfig::new(0, 64); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -111,7 +109,7 @@ fn test_binary_air_constraint_comparison() { #[test] fn test_binary_air_constraint_comparison_start_one() { let config = BinaryTestConfig::new(1, 64); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -129,10 +127,12 @@ fn test_binary_air_constraint_comparison_random_inputs() { // Test with iterations 0 through 50 for thorough coverage for iteration in 0u64..=50 { - let result = run_cross_backend_comparison_random( + let result = run_comparison( &config, - "test_binary_air_constraint_comparison_random_inputs", - iteration, + TraceSource::Random { + test_name: "test_binary_air_constraint_comparison_random_inputs", + iteration, + }, ); if !result.is_ok() { diff --git a/air-script/src/tests/comparison/bitwise.rs b/air-script/src/tests/comparison/bitwise.rs index cda7f16c1..d56e21e83 100644 --- a/air-script/src/tests/comparison/bitwise.rs +++ b/air-script/src/tests/comparison/bitwise.rs @@ -14,9 +14,7 @@ use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{ - CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, - }, + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, tests::bitwise::{ bitwise::{BitwiseAir as WinterfellBitwiseAir, PublicInputs}, bitwise_plonky3::BitwiseAir as Plonky3BitwiseAir, @@ -112,7 +110,7 @@ impl CrossBackendTestConfig for BitwiseTestConfig { fn test_bitwise_air_constraint_comparison() { // Use 64 rows (8 complete periods) let config = BitwiseTestConfig::new(64); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -128,7 +126,7 @@ fn test_bitwise_air_constraint_comparison() { fn test_bitwise_air_constraint_comparison_larger_trace() { // Use 512 rows (64 complete periods) - same as the Winterfell test let config = BitwiseTestConfig::new(512); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -146,10 +144,12 @@ fn test_bitwise_air_constraint_comparison_random_inputs() { // Test with iterations 0 through 50 for thorough coverage for iteration in 0u64..=50 { - let result = run_cross_backend_comparison_random( + let result = run_comparison( &config, - "test_bitwise_air_constraint_comparison_random_inputs", - iteration, + TraceSource::Random { + test_name: "test_bitwise_air_constraint_comparison_random_inputs", + iteration, + }, ); if !result.is_ok() { diff --git a/air-script/src/tests/comparison/constant_in_range.rs b/air-script/src/tests/comparison/constant_in_range.rs index b586deecb..730a8d0cd 100644 --- a/air-script/src/tests/comparison/constant_in_range.rs +++ b/air-script/src/tests/comparison/constant_in_range.rs @@ -14,9 +14,7 @@ use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{ - CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, - }, + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, tests::constant_in_range::{ constant_in_range::{ConstantInRangeAir as WinterfellConstantInRangeAir, PublicInputs}, constant_in_range_plonky3::ConstantInRangeAir as Plonky3ConstantInRangeAir, @@ -112,7 +110,7 @@ impl CrossBackendTestConfig for ConstantInRangeTestConfig { #[test] fn test_constant_in_range_air_constraint_comparison() { let config = ConstantInRangeTestConfig::new(64); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -129,10 +127,12 @@ fn test_constant_in_range_air_constraint_comparison_random_inputs() { let config = ConstantInRangeTestConfig::new(64); for iteration in 0u64..=50 { - let result = run_cross_backend_comparison_random( + let result = run_comparison( &config, - "test_constant_in_range_air_constraint_comparison_random_inputs", - iteration, + TraceSource::Random { + test_name: "test_constant_in_range_air_constraint_comparison_random_inputs", + iteration, + }, ); if !result.is_ok() { diff --git a/air-script/src/tests/comparison/constants.rs b/air-script/src/tests/comparison/constants.rs index 6038127a8..7209db944 100644 --- a/air-script/src/tests/comparison/constants.rs +++ b/air-script/src/tests/comparison/constants.rs @@ -13,9 +13,7 @@ use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{ - CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, - }, + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, tests::constants::{ constants::{ConstantsAir as WinterfellConstantsAir, PublicInputs}, constants_plonky3::ConstantsAir as Plonky3ConstantsAir, @@ -116,7 +114,7 @@ impl CrossBackendTestConfig for ConstantsTestConfig { #[test] fn test_constants_air_constraint_comparison() { let config = ConstantsTestConfig::new(64); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -132,7 +130,7 @@ fn test_constants_air_constraint_comparison() { fn test_constants_air_constraint_comparison_longer_trace() { // Test with a longer trace to exercise more rows let config = ConstantsTestConfig::new(128); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -150,10 +148,12 @@ fn test_constants_air_constraint_comparison_random_inputs() { // Test with iterations 0 through 50 for thorough coverage for iteration in 0u64..=50 { - let result = run_cross_backend_comparison_random( + let result = run_comparison( &config, - "test_constants_air_constraint_comparison_random_inputs", - iteration, + TraceSource::Random { + test_name: "test_constants_air_constraint_comparison_random_inputs", + iteration, + }, ); if !result.is_ok() { diff --git a/air-script/src/tests/comparison/constraint_comprehension.rs b/air-script/src/tests/comparison/constraint_comprehension.rs index ae9bf27ef..5b0e66794 100644 --- a/air-script/src/tests/comparison/constraint_comprehension.rs +++ b/air-script/src/tests/comparison/constraint_comprehension.rs @@ -14,9 +14,7 @@ use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{ - CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, - }, + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, tests::constraint_comprehension::{ constraint_comprehension::{ ConstraintComprehensionAir as WinterfellConstraintComprehensionAir, PublicInputs, @@ -129,7 +127,7 @@ impl CrossBackendTestConfig for ConstraintComprehensionTestConfig { #[test] fn test_constraint_comprehension_air_constraint_comparison() { let config = ConstraintComprehensionTestConfig::new(64); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -144,7 +142,7 @@ fn test_constraint_comprehension_air_constraint_comparison() { #[test] fn test_constraint_comprehension_air_constraint_comparison_small_trace() { let config = ConstraintComprehensionTestConfig::new(16); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -162,10 +160,12 @@ fn test_constraint_comprehension_air_constraint_comparison_random_inputs() { // Test with iterations 0 through 50 for thorough coverage for iteration in 0u64..=50 { - let result = run_cross_backend_comparison_random( + let result = run_comparison( &config, - "test_constraint_comprehension_air_constraint_comparison_random_inputs", - iteration, + TraceSource::Random { + test_name: "test_constraint_comprehension_air_constraint_comparison_random_inputs", + iteration, + }, ); if !result.is_ok() { diff --git a/air-script/src/tests/comparison/cross_module_constants.rs b/air-script/src/tests/comparison/cross_module_constants.rs index ccb8918a3..29e37c502 100644 --- a/air-script/src/tests/comparison/cross_module_constants.rs +++ b/air-script/src/tests/comparison/cross_module_constants.rs @@ -14,9 +14,7 @@ use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{ - CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, - }, + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, tests::cross_module_constants::{ cross_mod_constants::{ CrossModuleConstantsTest as WinterfellCrossModuleConstantsTest, PublicInputs, @@ -102,7 +100,7 @@ impl CrossBackendTestConfig for CrossModuleConstantsTestConfig { #[test] fn test_cross_module_constants_air_constraint_comparison() { let config = CrossModuleConstantsTestConfig::new(64); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -119,10 +117,12 @@ fn test_cross_module_constants_air_constraint_comparison_random_inputs() { let config = CrossModuleConstantsTestConfig::new(64); for iteration in 0u64..=50 { - let result = run_cross_backend_comparison_random( + let result = run_comparison( &config, - "test_cross_module_constants_air_constraint_comparison_random_inputs", - iteration, + TraceSource::Random { + test_name: "test_cross_module_constants_air_constraint_comparison_random_inputs", + iteration, + }, ); if !result.is_ok() { diff --git a/air-script/src/tests/comparison/fibonacci.rs b/air-script/src/tests/comparison/fibonacci.rs index eaa319e89..82298fd97 100644 --- a/air-script/src/tests/comparison/fibonacci.rs +++ b/air-script/src/tests/comparison/fibonacci.rs @@ -11,9 +11,7 @@ use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; use crate::{ - test_utils::cross_backend_comparison::{ - CrossBackendTestConfig, run_cross_backend_comparison, run_cross_backend_comparison_random, - }, + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, tests::fibonacci::{ fibonacci::{FibonacciAir as WinterfellFibonacciAir, PublicInputs}, fibonacci_plonky3::FibonacciAir as Plonky3FibonacciAir, @@ -120,7 +118,7 @@ impl CrossBackendTestConfig for FibonacciTestConfig { fn test_fibonacci_air_constraint_comparison() { // Standard Fibonacci starting with 0, 1 let config = FibonacciTestConfig::new(0, 1, 64); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -136,7 +134,7 @@ fn test_fibonacci_air_constraint_comparison() { fn test_fibonacci_air_constraint_comparison_different_start() { // Fibonacci-like sequence starting with 1, 1 let config = FibonacciTestConfig::new(1, 1, 64); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -152,7 +150,7 @@ fn test_fibonacci_air_constraint_comparison_different_start() { fn test_fibonacci_air_constraint_comparison_larger_values() { // Test with larger starting values to exercise field arithmetic let config = FibonacciTestConfig::new(100, 200, 32); - let result = run_cross_backend_comparison(&config); + let result = run_comparison(&config, TraceSource::Default); if !result.is_ok() { panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); @@ -170,10 +168,12 @@ fn test_fibonacci_air_constraint_comparison_random_inputs() { // Test with iterations 0 through 50 for thorough coverage for iteration in 0u64..=50 { - let result = run_cross_backend_comparison_random( + let result = run_comparison( &config, - "test_fibonacci_air_constraint_comparison_random_inputs", - iteration, + TraceSource::Random { + test_name: "test_fibonacci_air_constraint_comparison_random_inputs", + iteration, + }, ); if !result.is_ok() { From 75668c84489f81f311adb95706d372a69638153b Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 17:29:16 +0100 Subject: [PATCH 51/83] tests(compare-wf-p3): computed_indices_simple test --- .../comparison/computed_indices_simple.rs | 119 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/computed_indices/mod.rs | 4 +- 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/computed_indices_simple.rs diff --git a/air-script/src/tests/comparison/computed_indices_simple.rs b/air-script/src/tests/comparison/computed_indices_simple.rs new file mode 100644 index 000000000..3dead7e7c --- /dev/null +++ b/air-script/src/tests/comparison/computed_indices_simple.rs @@ -0,0 +1,119 @@ +//! Cross-backend comparison test for the ComputedIndicesSimple AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the ComputedIndicesSimple AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::computed_indices::{ + computed_indices_simple::{ + ComputedIndicesAir as WinterfellComputedIndicesAir, PublicInputs, + }, + computed_indices_simple_plonky3::ComputedIndicesAir as Plonky3ComputedIndicesAir, + }, +}; + +/// Configuration for ComputedIndicesSimple AIR cross-backend comparison tests. +struct ComputedIndicesSimpleTestConfig { + /// The trace length. + trace_length: usize, +} + +impl ComputedIndicesSimpleTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + let col0 = vec![Felt::ZERO; length]; + let col1 = vec![Felt::new(2); length]; + let col2 = vec![Felt::new(4); length]; + let col3 = vec![Felt::new(6); length]; + let col4 = vec![Felt::ZERO; length]; + let col5 = vec![Felt::ZERO; length]; + let col6 = vec![Felt::ZERO; length]; + let col7 = vec![Felt::ZERO; length]; + + vec![col0, col1, col2, col3, col4, col5, col6, col7] + } +} + +impl CrossBackendTestConfig for ComputedIndicesSimpleTestConfig { + type WinterfellAir = WinterfellComputedIndicesAir; + type Plonky3Air = Plonky3ComputedIndicesAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 8 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellComputedIndicesAir { + WinterfellComputedIndicesAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3ComputedIndicesAir { + Plonky3ComputedIndicesAir + } +} + +#[test] +fn test_computed_indices_simple_air_constraint_comparison() { + let config = ComputedIndicesSimpleTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "ComputedIndicesSimple AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_computed_indices_simple_air_constraint_comparison_random_inputs() { + let config = ComputedIndicesSimpleTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_computed_indices_simple_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("ComputedIndicesSimple AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 9b19e057f..723b90241 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -5,6 +5,7 @@ mod binary; mod bitwise; +mod computed_indices_simple; mod constant_in_range; mod constants; mod constraint_comprehension; diff --git a/air-script/src/tests/computed_indices/mod.rs b/air-script/src/tests/computed_indices/mod.rs index 948afee49..0fbd28ce4 100644 --- a/air-script/src/tests/computed_indices/mod.rs +++ b/air-script/src/tests/computed_indices/mod.rs @@ -3,7 +3,7 @@ mod computed_indices_complex; #[rustfmt::skip] #[allow(clippy::all)] -mod computed_indices_simple; +pub mod computed_indices_simple; #[rustfmt::skip] #[allow(clippy::all)] @@ -12,7 +12,7 @@ mod computed_indices_complex_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod computed_indices_simple_plonky3; +pub mod computed_indices_simple_plonky3; mod test_air_plonky3; mod test_air_winterfell; From 97701e4d4452037bfcd30c2d1cd1ed2375af042a Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 17:58:54 +0100 Subject: [PATCH 52/83] tests(compare-wf-p3): computed_indices_complex test --- .../comparison/computed_indices_complex.rs | 115 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/computed_indices/mod.rs | 4 +- 3 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/computed_indices_complex.rs diff --git a/air-script/src/tests/comparison/computed_indices_complex.rs b/air-script/src/tests/comparison/computed_indices_complex.rs new file mode 100644 index 000000000..45dfa182e --- /dev/null +++ b/air-script/src/tests/comparison/computed_indices_complex.rs @@ -0,0 +1,115 @@ +//! Cross-backend comparison test for the ComputedIndicesComplex AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the ComputedIndicesComplex AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::computed_indices::{ + computed_indices_complex::{ + ComputedIndicesAir as WinterfellComputedIndicesAir, PublicInputs, + }, + computed_indices_complex_plonky3::ComputedIndicesAir as Plonky3ComputedIndicesAir, + }, +}; + +/// Configuration for ComputedIndicesComplex AIR cross-backend comparison tests. +struct ComputedIndicesComplexTestConfig { + /// The trace length. + trace_length: usize, +} + +impl ComputedIndicesComplexTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + let col0 = vec![Felt::ZERO; length]; + let col1 = vec![Felt::ZERO; length]; + let col2 = vec![Felt::ZERO; length]; + let col3 = vec![Felt::ZERO; length]; + + vec![col0, col1, col2, col3] + } +} + +impl CrossBackendTestConfig for ComputedIndicesComplexTestConfig { + type WinterfellAir = WinterfellComputedIndicesAir; + type Plonky3Air = Plonky3ComputedIndicesAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 4 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 1]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellComputedIndicesAir { + WinterfellComputedIndicesAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3ComputedIndicesAir { + Plonky3ComputedIndicesAir + } +} + +#[test] +fn test_computed_indices_complex_air_constraint_comparison() { + let config = ComputedIndicesComplexTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "ComputedIndicesComplex AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_computed_indices_complex_air_constraint_comparison_random_inputs() { + let config = ComputedIndicesComplexTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_computed_indices_complex_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("ComputedIndicesComplex AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 723b90241..87f037200 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -5,6 +5,7 @@ mod binary; mod bitwise; +mod computed_indices_complex; mod computed_indices_simple; mod constant_in_range; mod constants; diff --git a/air-script/src/tests/computed_indices/mod.rs b/air-script/src/tests/computed_indices/mod.rs index 0fbd28ce4..f0f3cc7c2 100644 --- a/air-script/src/tests/computed_indices/mod.rs +++ b/air-script/src/tests/computed_indices/mod.rs @@ -1,6 +1,6 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod computed_indices_complex; +pub mod computed_indices_complex; #[rustfmt::skip] #[allow(clippy::all)] pub mod computed_indices_simple; @@ -8,7 +8,7 @@ pub mod computed_indices_simple; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod computed_indices_complex_plonky3; +pub mod computed_indices_complex_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] From f0638379b5714e54fc50da96c840bcfb1f9d03ed Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 18:05:54 +0100 Subject: [PATCH 53/83] tests(compare-wf-p3): indexed_trace_access test --- .../tests/comparison/indexed_trace_access.rs | 117 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + .../src/tests/indexed_trace_access/mod.rs | 4 +- 3 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/indexed_trace_access.rs diff --git a/air-script/src/tests/comparison/indexed_trace_access.rs b/air-script/src/tests/comparison/indexed_trace_access.rs new file mode 100644 index 000000000..1b7fa573b --- /dev/null +++ b/air-script/src/tests/comparison/indexed_trace_access.rs @@ -0,0 +1,117 @@ +//! Cross-backend comparison test for the IndexedTraceAccess AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the IndexedTraceAccess AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::indexed_trace_access::{ + indexed_trace_access::{PublicInputs, TraceAccessAir as WinterfellTraceAccessAir}, + indexed_trace_access_plonky3::TraceAccessAir as Plonky3TraceAccessAir, + }, +}; + +/// Configuration for IndexedTraceAccess AIR cross-backend comparison tests. +struct IndexedTraceAccessTestConfig { + /// The trace length. + trace_length: usize, +} + +impl IndexedTraceAccessTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + let mut col0 = vec![Felt::ZERO; length]; + for row in 1..length { + col0[row] = Felt::ONE; + } + + let col1 = vec![Felt::ZERO; length]; + let col2 = vec![Felt::ZERO; length]; + let col3 = vec![Felt::ZERO; length]; + + vec![col0, col1, col2, col3] + } +} + +impl CrossBackendTestConfig for IndexedTraceAccessTestConfig { + type WinterfellAir = WinterfellTraceAccessAir; + type Plonky3Air = Plonky3TraceAccessAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 4 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellTraceAccessAir { + WinterfellTraceAccessAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3TraceAccessAir { + Plonky3TraceAccessAir + } +} + +#[test] +fn test_indexed_trace_access_air_constraint_comparison() { + let config = IndexedTraceAccessTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "IndexedTraceAccess AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_indexed_trace_access_air_constraint_comparison_random_inputs() { + let config = IndexedTraceAccessTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_indexed_trace_access_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("IndexedTraceAccess AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 87f037200..c5aff10b5 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -12,3 +12,4 @@ mod constants; mod constraint_comprehension; mod cross_module_constants; mod fibonacci; +mod indexed_trace_access; diff --git a/air-script/src/tests/indexed_trace_access/mod.rs b/air-script/src/tests/indexed_trace_access/mod.rs index f23106386..e2ad370f1 100644 --- a/air-script/src/tests/indexed_trace_access/mod.rs +++ b/air-script/src/tests/indexed_trace_access/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod indexed_trace_access; +pub mod indexed_trace_access; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod indexed_trace_access_plonky3; +pub mod indexed_trace_access_plonky3; mod test_air_plonky3; mod test_air_winterfell; From 79ce4e09fb1b80689f16f2c8da22015a91d8c351 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 18:13:53 +0100 Subject: [PATCH 54/83] tests(compare-wf-p3): evaluators test --- air-script/src/tests/comparison/evaluators.rs | 116 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/evaluators/mod.rs | 4 +- 3 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/evaluators.rs diff --git a/air-script/src/tests/comparison/evaluators.rs b/air-script/src/tests/comparison/evaluators.rs new file mode 100644 index 000000000..4fb6be479 --- /dev/null +++ b/air-script/src/tests/comparison/evaluators.rs @@ -0,0 +1,116 @@ +//! Cross-backend comparison test for the Evaluators AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the Evaluators AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::evaluators::{ + evaluators::{EvaluatorsAir as WinterfellEvaluatorsAir, PublicInputs}, + evaluators_plonky3::EvaluatorsAir as Plonky3EvaluatorsAir, + }, +}; + +/// Configuration for Evaluators AIR cross-backend comparison tests. +struct EvaluatorsTestConfig { + /// The trace length. + trace_length: usize, +} + +impl EvaluatorsTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + let col0 = vec![Felt::ZERO; length]; + let col1 = vec![Felt::ZERO; length]; + let col2 = vec![Felt::ZERO; length]; + let col3 = vec![Felt::ZERO; length]; + let col4 = vec![Felt::ZERO; length]; + let col5 = vec![Felt::ONE; length]; + let col6 = vec![Felt::new(4); length]; + + vec![col0, col1, col2, col3, col4, col5, col6] + } +} + +impl CrossBackendTestConfig for EvaluatorsTestConfig { + type WinterfellAir = WinterfellEvaluatorsAir; + type Plonky3Air = Plonky3EvaluatorsAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 7 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellEvaluatorsAir { + WinterfellEvaluatorsAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3EvaluatorsAir { + Plonky3EvaluatorsAir + } +} + +#[test] +fn test_evaluators_air_constraint_comparison() { + let config = EvaluatorsTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "Evaluators AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_evaluators_air_constraint_comparison_random_inputs() { + let config = EvaluatorsTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_evaluators_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("Evaluators AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index c5aff10b5..4c5ede9c6 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -11,5 +11,6 @@ mod constant_in_range; mod constants; mod constraint_comprehension; mod cross_module_constants; +mod evaluators; mod fibonacci; mod indexed_trace_access; diff --git a/air-script/src/tests/evaluators/mod.rs b/air-script/src/tests/evaluators/mod.rs index 8513a7ba3..7f39d2e67 100644 --- a/air-script/src/tests/evaluators/mod.rs +++ b/air-script/src/tests/evaluators/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod evaluators; +pub mod evaluators; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod evaluators_plonky3; +pub mod evaluators_plonky3; mod test_air_plonky3; mod test_air_winterfell; From aebfacb54e23178e60baee08ba3a6bd321ecedfb Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 18:24:50 +0100 Subject: [PATCH 55/83] tests(compare-wf-p3): evaluators_slice test --- .../src/tests/comparison/evaluators_slice.rs | 108 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/evaluators/mod.rs | 16 +++ 3 files changed, 125 insertions(+) create mode 100644 air-script/src/tests/comparison/evaluators_slice.rs diff --git a/air-script/src/tests/comparison/evaluators_slice.rs b/air-script/src/tests/comparison/evaluators_slice.rs new file mode 100644 index 000000000..7dbcf613c --- /dev/null +++ b/air-script/src/tests/comparison/evaluators_slice.rs @@ -0,0 +1,108 @@ +//! Cross-backend comparison test for the EvaluatorsSlice AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the EvaluatorsSlice AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::evaluators::{ + evaluators_slice::{EvaluatorsSliceAir as WinterfellEvaluatorsSliceAir, PublicInputs}, + evaluators_slice_plonky3::EvaluatorsSliceAir as Plonky3EvaluatorsSliceAir, + }, +}; + +/// Configuration for EvaluatorsSlice AIR cross-backend comparison tests. +struct EvaluatorsSliceTestConfig { + /// The trace length. + trace_length: usize, +} + +impl EvaluatorsSliceTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + vec![vec![Felt::ZERO; length]; 20] + } +} + +impl CrossBackendTestConfig for EvaluatorsSliceTestConfig { + type WinterfellAir = WinterfellEvaluatorsSliceAir; + type Plonky3Air = Plonky3EvaluatorsSliceAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 20 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellEvaluatorsSliceAir { + WinterfellEvaluatorsSliceAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3EvaluatorsSliceAir { + Plonky3EvaluatorsSliceAir + } +} + +#[test] +fn test_evaluators_slice_air_constraint_comparison() { + let config = EvaluatorsSliceTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "EvaluatorsSlice AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_evaluators_slice_air_constraint_comparison_random_inputs() { + let config = EvaluatorsSliceTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_evaluators_slice_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("EvaluatorsSlice AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 4c5ede9c6..22dba23bb 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -12,5 +12,6 @@ mod constants; mod constraint_comprehension; mod cross_module_constants; mod evaluators; +mod evaluators_slice; mod fibonacci; mod indexed_trace_access; diff --git a/air-script/src/tests/evaluators/mod.rs b/air-script/src/tests/evaluators/mod.rs index 7f39d2e67..a804821f2 100644 --- a/air-script/src/tests/evaluators/mod.rs +++ b/air-script/src/tests/evaluators/mod.rs @@ -6,5 +6,21 @@ pub mod evaluators; #[allow(unused_imports)] pub mod evaluators_plonky3; +#[rustfmt::skip] +#[allow(clippy::all)] +pub mod evaluators_slice; +#[rustfmt::skip] +#[allow(clippy::all)] +pub mod evaluators_nested_slice_call; + +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +pub mod evaluators_slice_plonky3; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +pub mod evaluators_nested_slice_call_plonky3; + mod test_air_plonky3; mod test_air_winterfell; From 86a78bd3da4e32953464de8f06f5a8dcb2df6570 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 18:45:00 +0100 Subject: [PATCH 56/83] tests(compare-wf-p3): evaluators_nested_slice_call test --- .../evaluators_nested_slice_call.rs | 110 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + 2 files changed, 111 insertions(+) create mode 100644 air-script/src/tests/comparison/evaluators_nested_slice_call.rs diff --git a/air-script/src/tests/comparison/evaluators_nested_slice_call.rs b/air-script/src/tests/comparison/evaluators_nested_slice_call.rs new file mode 100644 index 000000000..99cb7f05b --- /dev/null +++ b/air-script/src/tests/comparison/evaluators_nested_slice_call.rs @@ -0,0 +1,110 @@ +//! Cross-backend comparison test for the EvaluatorsNestedSliceCall AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the EvaluatorsNestedSliceCall AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::evaluators::{ + evaluators_nested_slice_call::{ + EvaluatorsSliceAir as WinterfellEvaluatorsNestedSliceCallAir, PublicInputs, + }, + evaluators_nested_slice_call_plonky3::EvaluatorsSliceAir as Plonky3EvaluatorsNestedSliceCallAir, + }, +}; + +/// Configuration for EvaluatorsNestedSliceCall AIR cross-backend comparison tests. +struct EvaluatorsNestedSliceCallTestConfig { + /// The trace length. + trace_length: usize, +} + +impl EvaluatorsNestedSliceCallTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + vec![vec![Felt::ZERO; length]; 20] + } +} + +impl CrossBackendTestConfig for EvaluatorsNestedSliceCallTestConfig { + type WinterfellAir = WinterfellEvaluatorsNestedSliceCallAir; + type Plonky3Air = Plonky3EvaluatorsNestedSliceCallAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 20 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellEvaluatorsNestedSliceCallAir { + WinterfellEvaluatorsNestedSliceCallAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3EvaluatorsNestedSliceCallAir { + Plonky3EvaluatorsNestedSliceCallAir + } +} + +#[test] +fn test_evaluators_nested_slice_call_air_constraint_comparison() { + let config = EvaluatorsNestedSliceCallTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "EvaluatorsNestedSliceCall AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_evaluators_nested_slice_call_air_constraint_comparison_random_inputs() { + let config = EvaluatorsNestedSliceCallTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_evaluators_nested_slice_call_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("EvaluatorsNestedSliceCall AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 22dba23bb..23b9a35fc 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -12,6 +12,7 @@ mod constants; mod constraint_comprehension; mod cross_module_constants; mod evaluators; +mod evaluators_nested_slice_call; mod evaluators_slice; mod fibonacci; mod indexed_trace_access; From 57364c6da1b77490da8a75cf22f31e29cbf8f799 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 18:48:05 +0100 Subject: [PATCH 57/83] tests(compare-wf-p3): functions_simple test --- .../src/tests/comparison/functions_simple.rs | 108 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/functions/mod.rs | 4 +- 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/functions_simple.rs diff --git a/air-script/src/tests/comparison/functions_simple.rs b/air-script/src/tests/comparison/functions_simple.rs new file mode 100644 index 000000000..d9a762fb8 --- /dev/null +++ b/air-script/src/tests/comparison/functions_simple.rs @@ -0,0 +1,108 @@ +//! Cross-backend comparison test for the FunctionsSimple AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the FunctionsSimple AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::functions::{ + functions_simple::{FunctionsAir as WinterfellFunctionsSimpleAir, PublicInputs}, + functions_simple_plonky3::FunctionsAir as Plonky3FunctionsSimpleAir, + }, +}; + +/// Configuration for FunctionsSimple AIR cross-backend comparison tests. +struct FunctionsSimpleTestConfig { + /// The trace length. + trace_length: usize, +} + +impl FunctionsSimpleTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + vec![vec![Felt::ZERO; length]; 9] + } +} + +impl CrossBackendTestConfig for FunctionsSimpleTestConfig { + type WinterfellAir = WinterfellFunctionsSimpleAir; + type Plonky3Air = Plonky3FunctionsSimpleAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 9 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellFunctionsSimpleAir { + WinterfellFunctionsSimpleAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3FunctionsSimpleAir { + Plonky3FunctionsSimpleAir + } +} + +#[test] +fn test_functions_simple_air_constraint_comparison() { + let config = FunctionsSimpleTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "FunctionsSimple AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_functions_simple_air_constraint_comparison_random_inputs() { + let config = FunctionsSimpleTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_functions_simple_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("FunctionsSimple AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 23b9a35fc..91a1ff4ff 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -15,4 +15,5 @@ mod evaluators; mod evaluators_nested_slice_call; mod evaluators_slice; mod fibonacci; +mod functions_simple; mod indexed_trace_access; diff --git a/air-script/src/tests/functions/mod.rs b/air-script/src/tests/functions/mod.rs index af284a496..ec4469371 100644 --- a/air-script/src/tests/functions/mod.rs +++ b/air-script/src/tests/functions/mod.rs @@ -3,7 +3,7 @@ mod functions_complex; #[rustfmt::skip] #[allow(clippy::all)] -mod functions_simple; +pub mod functions_simple; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] @@ -11,7 +11,7 @@ mod functions_complex_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod functions_simple_plonky3; +pub mod functions_simple_plonky3; mod test_air_plonky3; mod test_air_winterfell; From 8414a8aa3f70b26e157c97944a4151775d189256 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 18:54:06 +0100 Subject: [PATCH 58/83] tests(compare-wf-p3): functions_complex test --- .../src/tests/comparison/functions_complex.rs | 113 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/functions/mod.rs | 4 +- 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/functions_complex.rs diff --git a/air-script/src/tests/comparison/functions_complex.rs b/air-script/src/tests/comparison/functions_complex.rs new file mode 100644 index 000000000..7a7ab5a89 --- /dev/null +++ b/air-script/src/tests/comparison/functions_complex.rs @@ -0,0 +1,113 @@ +//! Cross-backend comparison test for the FunctionsComplex AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the FunctionsComplex AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::functions::{ + functions_complex::{FunctionsAir as WinterfellFunctionsComplexAir, PublicInputs}, + functions_complex_plonky3::FunctionsAir as Plonky3FunctionsComplexAir, + }, +}; + +/// Configuration for FunctionsComplex AIR cross-backend comparison tests. +struct FunctionsComplexTestConfig { + /// The trace length. + trace_length: usize, +} + +impl FunctionsComplexTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 17]; + + for row in 1..length { + trace[3][row] = Felt::new(2); + } + + trace + } +} + +impl CrossBackendTestConfig for FunctionsComplexTestConfig { + type WinterfellAir = WinterfellFunctionsComplexAir; + type Plonky3Air = Plonky3FunctionsComplexAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 17 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellFunctionsComplexAir { + WinterfellFunctionsComplexAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3FunctionsComplexAir { + Plonky3FunctionsComplexAir + } +} + +#[test] +fn test_functions_complex_air_constraint_comparison() { + let config = FunctionsComplexTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "FunctionsComplex AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_functions_complex_air_constraint_comparison_random_inputs() { + let config = FunctionsComplexTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_functions_complex_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("FunctionsComplex AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 91a1ff4ff..589649d77 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -15,5 +15,6 @@ mod evaluators; mod evaluators_nested_slice_call; mod evaluators_slice; mod fibonacci; +mod functions_complex; mod functions_simple; mod indexed_trace_access; diff --git a/air-script/src/tests/functions/mod.rs b/air-script/src/tests/functions/mod.rs index ec4469371..3f4aa7dc7 100644 --- a/air-script/src/tests/functions/mod.rs +++ b/air-script/src/tests/functions/mod.rs @@ -1,13 +1,13 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod functions_complex; +pub mod functions_complex; #[rustfmt::skip] #[allow(clippy::all)] pub mod functions_simple; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod functions_complex_plonky3; +pub mod functions_complex_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] From ce9bd14b026f34409dbef07fb29947b5a6057971 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 18:57:59 +0100 Subject: [PATCH 59/83] tests(compare-wf-p3): inlined_functions_simple test --- .../comparison/inlined_functions_simple.rs | 110 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + 2 files changed, 111 insertions(+) create mode 100644 air-script/src/tests/comparison/inlined_functions_simple.rs diff --git a/air-script/src/tests/comparison/inlined_functions_simple.rs b/air-script/src/tests/comparison/inlined_functions_simple.rs new file mode 100644 index 000000000..c51b4b856 --- /dev/null +++ b/air-script/src/tests/comparison/inlined_functions_simple.rs @@ -0,0 +1,110 @@ +//! Cross-backend comparison test for the InlinedFunctionsSimple AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the inlined FunctionsSimple AIR at every row of the trace. +//! +//! NOTE: The inlined variant shares generated outputs with `functions_simple`. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::functions::{ + functions_simple::{FunctionsAir as WinterfellFunctionsSimpleAir, PublicInputs}, + functions_simple_plonky3::FunctionsAir as Plonky3FunctionsSimpleAir, + }, +}; + +/// Configuration for inlined FunctionsSimple AIR cross-backend comparison tests. +struct InlinedFunctionsSimpleTestConfig { + /// The trace length. + trace_length: usize, +} + +impl InlinedFunctionsSimpleTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + vec![vec![Felt::ZERO; length]; 9] + } +} + +impl CrossBackendTestConfig for InlinedFunctionsSimpleTestConfig { + type WinterfellAir = WinterfellFunctionsSimpleAir; + type Plonky3Air = Plonky3FunctionsSimpleAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 9 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellFunctionsSimpleAir { + WinterfellFunctionsSimpleAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3FunctionsSimpleAir { + Plonky3FunctionsSimpleAir + } +} + +#[test] +fn test_inlined_functions_simple_air_constraint_comparison() { + let config = InlinedFunctionsSimpleTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "InlinedFunctionsSimple AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_inlined_functions_simple_air_constraint_comparison_random_inputs() { + let config = InlinedFunctionsSimpleTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_inlined_functions_simple_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("InlinedFunctionsSimple AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 589649d77..a9402fe5a 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -18,3 +18,4 @@ mod fibonacci; mod functions_complex; mod functions_simple; mod indexed_trace_access; +mod inlined_functions_simple; From 1bc51e37e9d19f6623a13c97a3abfc4f87a6616a Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:02:05 +0100 Subject: [PATCH 60/83] tests(compare-wf-p3): list_comprehension test --- .../tests/comparison/list_comprehension.rs | 119 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + .../src/tests/list_comprehension/mod.rs | 4 +- 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/list_comprehension.rs diff --git a/air-script/src/tests/comparison/list_comprehension.rs b/air-script/src/tests/comparison/list_comprehension.rs new file mode 100644 index 000000000..4bda71695 --- /dev/null +++ b/air-script/src/tests/comparison/list_comprehension.rs @@ -0,0 +1,119 @@ +//! Cross-backend comparison test for the ListComprehension AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the ListComprehension AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::list_comprehension::{ + list_comprehension::{ + ListComprehensionAir as WinterfellListComprehensionAir, PublicInputs, + }, + list_comprehension_plonky3::ListComprehensionAir as Plonky3ListComprehensionAir, + }, +}; + +/// Configuration for ListComprehension AIR cross-backend comparison tests. +struct ListComprehensionTestConfig { + /// The trace length. + trace_length: usize, +} + +impl ListComprehensionTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 16]; + + for row in 0..length { + trace[1][row] = Felt::new(20); + trace[14][row] = Felt::new(10); + if row > 0 { + trace[3][row] = Felt::new(2); + } + } + + trace + } +} + +impl CrossBackendTestConfig for ListComprehensionTestConfig { + type WinterfellAir = WinterfellListComprehensionAir; + type Plonky3Air = Plonky3ListComprehensionAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 16 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellListComprehensionAir { + WinterfellListComprehensionAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3ListComprehensionAir { + Plonky3ListComprehensionAir + } +} + +#[test] +fn test_list_comprehension_air_constraint_comparison() { + let config = ListComprehensionTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "ListComprehension AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_list_comprehension_air_constraint_comparison_random_inputs() { + let config = ListComprehensionTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_list_comprehension_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("ListComprehension AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index a9402fe5a..6848f09a8 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -19,3 +19,4 @@ mod functions_complex; mod functions_simple; mod indexed_trace_access; mod inlined_functions_simple; +mod list_comprehension; diff --git a/air-script/src/tests/list_comprehension/mod.rs b/air-script/src/tests/list_comprehension/mod.rs index e33f216df..6677bc7e3 100644 --- a/air-script/src/tests/list_comprehension/mod.rs +++ b/air-script/src/tests/list_comprehension/mod.rs @@ -7,11 +7,11 @@ mod list_comprehension_nested; mod list_comprehension_nested_plonky3; #[rustfmt::skip] #[allow(clippy::all)] -mod list_comprehension; +pub mod list_comprehension; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod list_comprehension_plonky3; +pub mod list_comprehension_plonky3; mod test_air_plonky3; mod test_air_winterfell; From 789aa75052d58898f298d6a3f1ebce1d58740479 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:05:01 +0100 Subject: [PATCH 61/83] tests(compare-wf-p3): list_comprehension_nested test --- .../comparison/list_comprehension_nested.rs | 114 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + .../src/tests/list_comprehension/mod.rs | 4 +- 3 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/list_comprehension_nested.rs diff --git a/air-script/src/tests/comparison/list_comprehension_nested.rs b/air-script/src/tests/comparison/list_comprehension_nested.rs new file mode 100644 index 000000000..b1b6e7c69 --- /dev/null +++ b/air-script/src/tests/comparison/list_comprehension_nested.rs @@ -0,0 +1,114 @@ +//! Cross-backend comparison test for the ListComprehensionNested AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the ListComprehensionNested AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::list_comprehension::{ + list_comprehension_nested::{ + ListComprehensionAir as WinterfellListComprehensionNestedAir, PublicInputs, + }, + list_comprehension_nested_plonky3::ListComprehensionAir as Plonky3ListComprehensionNestedAir, + }, +}; + +/// Configuration for ListComprehensionNested AIR cross-backend comparison tests. +/// +/// Note: Based on the AIR, it is apparently unsatisfiable at row 0 (boundary enforces +/// a0 = 0 while integrity equations imply a0 = 1, a1 = 1). This comparison test checks +/// that both backends evaluate constraints identically, not that the trace satisfies them. +struct ListComprehensionNestedTestConfig { + /// The trace length. + trace_length: usize, +} + +impl ListComprehensionNestedTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + vec![vec![Felt::ZERO; length]; 2] + } +} + +impl CrossBackendTestConfig for ListComprehensionNestedTestConfig { + type WinterfellAir = WinterfellListComprehensionNestedAir; + type Plonky3Air = Plonky3ListComprehensionNestedAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 2 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 1]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellListComprehensionNestedAir { + WinterfellListComprehensionNestedAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3ListComprehensionNestedAir { + Plonky3ListComprehensionNestedAir + } +} + +#[test] +fn test_list_comprehension_nested_air_constraint_comparison() { + let config = ListComprehensionNestedTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "ListComprehensionNested AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_list_comprehension_nested_air_constraint_comparison_random_inputs() { + let config = ListComprehensionNestedTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_list_comprehension_nested_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("ListComprehensionNested AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 6848f09a8..2cad9c231 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -20,3 +20,4 @@ mod functions_simple; mod indexed_trace_access; mod inlined_functions_simple; mod list_comprehension; +mod list_comprehension_nested; diff --git a/air-script/src/tests/list_comprehension/mod.rs b/air-script/src/tests/list_comprehension/mod.rs index 6677bc7e3..919b3164f 100644 --- a/air-script/src/tests/list_comprehension/mod.rs +++ b/air-script/src/tests/list_comprehension/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod list_comprehension_nested; +pub mod list_comprehension_nested; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod list_comprehension_nested_plonky3; +pub mod list_comprehension_nested_plonky3; #[rustfmt::skip] #[allow(clippy::all)] pub mod list_comprehension; From 08831bad1bf93aa1daacaf3132efa02b9ee82371 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:12:03 +0100 Subject: [PATCH 62/83] tests(compare-wf-p3): list_folding test --- .../src/tests/comparison/list_folding.rs | 113 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/list_folding/mod.rs | 4 +- 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/list_folding.rs diff --git a/air-script/src/tests/comparison/list_folding.rs b/air-script/src/tests/comparison/list_folding.rs new file mode 100644 index 000000000..265d03565 --- /dev/null +++ b/air-script/src/tests/comparison/list_folding.rs @@ -0,0 +1,113 @@ +//! Cross-backend comparison test for the ListFolding AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the ListFolding AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::list_folding::{ + list_folding::{ListFoldingAir as WinterfellListFoldingAir, PublicInputs}, + list_folding_plonky3::ListFoldingAir as Plonky3ListFoldingAir, + }, +}; + +/// Configuration for ListFolding AIR cross-backend comparison tests. +struct ListFoldingTestConfig { + /// The trace length. + trace_length: usize, +} + +impl ListFoldingTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 17]; + + for row in 1..length { + trace[3][row] = Felt::new(2); + } + + trace + } +} + +impl CrossBackendTestConfig for ListFoldingTestConfig { + type WinterfellAir = WinterfellListFoldingAir; + type Plonky3Air = Plonky3ListFoldingAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 17 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellListFoldingAir { + WinterfellListFoldingAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3ListFoldingAir { + Plonky3ListFoldingAir + } +} + +#[test] +fn test_list_folding_air_constraint_comparison() { + let config = ListFoldingTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "ListFolding AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_list_folding_air_constraint_comparison_random_inputs() { + let config = ListFoldingTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_list_folding_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("ListFolding AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 2cad9c231..5e6dba364 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -21,3 +21,4 @@ mod indexed_trace_access; mod inlined_functions_simple; mod list_comprehension; mod list_comprehension_nested; +mod list_folding; diff --git a/air-script/src/tests/list_folding/mod.rs b/air-script/src/tests/list_folding/mod.rs index 0339dce98..c2663530b 100644 --- a/air-script/src/tests/list_folding/mod.rs +++ b/air-script/src/tests/list_folding/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod list_folding; +pub mod list_folding; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod list_folding_plonky3; +pub mod list_folding_plonky3; mod test_air_plonky3; mod test_air_winterfell; From 27107aa3c4fb0e33bd3af48ea96ab0f5463198ba Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:13:28 +0100 Subject: [PATCH 63/83] tests(compare-wf-p3): pub_inputs test --- air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/comparison/pub_inputs.rs | 117 ++++++++++++++++++ air-script/src/tests/pub_inputs/mod.rs | 4 +- 3 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/pub_inputs.rs diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 5e6dba364..d780692fb 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -22,3 +22,4 @@ mod inlined_functions_simple; mod list_comprehension; mod list_comprehension_nested; mod list_folding; +mod pub_inputs; diff --git a/air-script/src/tests/comparison/pub_inputs.rs b/air-script/src/tests/comparison/pub_inputs.rs new file mode 100644 index 000000000..99a6b71f0 --- /dev/null +++ b/air-script/src/tests/comparison/pub_inputs.rs @@ -0,0 +1,117 @@ +//! Cross-backend comparison test for the PubInputs AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the PubInputs AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::pub_inputs::{ + pub_inputs::{PubInputsAir as WinterfellPubInputsAir, PublicInputs}, + pub_inputs_plonky3::PubInputsAir as Plonky3PubInputsAir, + }, +}; + +/// Configuration for PubInputs AIR cross-backend comparison tests. +struct PubInputsTestConfig { + /// The trace length. + trace_length: usize, +} + +impl PubInputsTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 4]; + + for row in 0..length { + trace[1][row] = Felt::new(2); + trace[2][row] = Felt::new(3); + if row > 0 { + trace[0][row] = Felt::new(5); + } + } + + trace + } +} + +impl CrossBackendTestConfig for PubInputsTestConfig { + type WinterfellAir = WinterfellPubInputsAir; + type Plonky3Air = Plonky3PubInputsAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 4 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 4], [Felt::ZERO; 4], [Felt::ZERO; 4], [Felt::ZERO; 20]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellPubInputsAir { + WinterfellPubInputsAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3PubInputsAir { + Plonky3PubInputsAir + } +} + +#[test] +fn test_pub_inputs_air_constraint_comparison() { + let config = PubInputsTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "PubInputs AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_pub_inputs_air_constraint_comparison_random_inputs() { + let config = PubInputsTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_pub_inputs_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("PubInputs AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/pub_inputs/mod.rs b/air-script/src/tests/pub_inputs/mod.rs index 139601bc2..d1ebe9c58 100644 --- a/air-script/src/tests/pub_inputs/mod.rs +++ b/air-script/src/tests/pub_inputs/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod pub_inputs; +pub mod pub_inputs; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod pub_inputs_plonky3; +pub mod pub_inputs_plonky3; mod test_air_plonky3; mod test_air_winterfell; From f37f14391a19ffbc70b7b725f2fc615e0f4c27a2 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:14:57 +0100 Subject: [PATCH 64/83] tests(compare-wf-p3): selectors test --- air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/comparison/selectors.rs | 113 +++++++++++++++++++ air-script/src/tests/selectors/mod.rs | 8 +- 3 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 air-script/src/tests/comparison/selectors.rs diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index d780692fb..9eafb4e7b 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -23,3 +23,4 @@ mod list_comprehension; mod list_comprehension_nested; mod list_folding; mod pub_inputs; +mod selectors; diff --git a/air-script/src/tests/comparison/selectors.rs b/air-script/src/tests/comparison/selectors.rs new file mode 100644 index 000000000..61fe0fb7e --- /dev/null +++ b/air-script/src/tests/comparison/selectors.rs @@ -0,0 +1,113 @@ +//! Cross-backend comparison test for the Selectors AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the Selectors AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::selectors::{ + selectors::{PublicInputs, SelectorsAir as WinterfellSelectorsAir}, + selectors_plonky3::SelectorsAir as Plonky3SelectorsAir, + }, +}; + +/// Configuration for Selectors AIR cross-backend comparison tests. +struct SelectorsTestConfig { + /// The trace length. + trace_length: usize, +} + +impl SelectorsTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 4]; + + for row in 1..length { + trace[3][row] = Felt::ONE; + } + + trace + } +} + +impl CrossBackendTestConfig for SelectorsTestConfig { + type WinterfellAir = WinterfellSelectorsAir; + type Plonky3Air = Plonky3SelectorsAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 4 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellSelectorsAir { + WinterfellSelectorsAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3SelectorsAir { + Plonky3SelectorsAir + } +} + +#[test] +fn test_selectors_air_constraint_comparison() { + let config = SelectorsTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "Selectors AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_selectors_air_constraint_comparison_random_inputs() { + let config = SelectorsTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_selectors_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("Selectors AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/selectors/mod.rs b/air-script/src/tests/selectors/mod.rs index a8007ac23..dfe926ff4 100644 --- a/air-script/src/tests/selectors/mod.rs +++ b/air-script/src/tests/selectors/mod.rs @@ -1,6 +1,6 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod selectors; +pub mod selectors; #[rustfmt::skip] #[allow(clippy::all)] mod selectors_combine_simple; @@ -12,11 +12,11 @@ mod selectors_combine_complex; mod selectors_combine_with_list_comprehensions; #[rustfmt::skip] #[allow(clippy::all)] -mod selectors_with_evaluators; +pub mod selectors_with_evaluators; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod selectors_plonky3; +pub mod selectors_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] @@ -28,7 +28,7 @@ mod selectors_combine_complex_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod selectors_with_evaluators_plonky3; +pub mod selectors_with_evaluators_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] From ad56f49917cc3c5164ba8128212b654ccf7caf23 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:17:05 +0100 Subject: [PATCH 65/83] tests(compare-wf-p3): selectors_with_evaluators test --- air-script/src/tests/comparison/mod.rs | 1 + .../comparison/selectors_with_evaluators.rs | 115 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 air-script/src/tests/comparison/selectors_with_evaluators.rs diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 9eafb4e7b..33b99eb10 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -24,3 +24,4 @@ mod list_comprehension_nested; mod list_folding; mod pub_inputs; mod selectors; +mod selectors_with_evaluators; diff --git a/air-script/src/tests/comparison/selectors_with_evaluators.rs b/air-script/src/tests/comparison/selectors_with_evaluators.rs new file mode 100644 index 000000000..126c43b24 --- /dev/null +++ b/air-script/src/tests/comparison/selectors_with_evaluators.rs @@ -0,0 +1,115 @@ +//! Cross-backend comparison test for the SelectorsWithEvaluators AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the SelectorsWithEvaluators AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::selectors::{ + selectors_with_evaluators::{ + PublicInputs, SelectorsAir as WinterfellSelectorsWithEvaluatorsAir, + }, + selectors_with_evaluators_plonky3::SelectorsAir as Plonky3SelectorsWithEvaluatorsAir, + }, +}; + +/// Configuration for SelectorsWithEvaluators AIR cross-backend comparison tests. +struct SelectorsWithEvaluatorsTestConfig { + /// The trace length. + trace_length: usize, +} + +impl SelectorsWithEvaluatorsTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 4]; + + for row in 1..length { + trace[3][row] = Felt::ONE; + } + + trace + } +} + +impl CrossBackendTestConfig for SelectorsWithEvaluatorsTestConfig { + type WinterfellAir = WinterfellSelectorsWithEvaluatorsAir; + type Plonky3Air = Plonky3SelectorsWithEvaluatorsAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 4 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellSelectorsWithEvaluatorsAir { + WinterfellSelectorsWithEvaluatorsAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3SelectorsWithEvaluatorsAir { + Plonky3SelectorsWithEvaluatorsAir + } +} + +#[test] +fn test_selectors_with_evaluators_air_constraint_comparison() { + let config = SelectorsWithEvaluatorsTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "SelectorsWithEvaluators AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_selectors_with_evaluators_air_constraint_comparison_random_inputs() { + let config = SelectorsWithEvaluatorsTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_selectors_with_evaluators_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("SelectorsWithEvaluators AIR random comparison passed for all 51 iterations (0-50)"); +} From be78e776f15dc7c1d8aede3d34bf566eebfe5d87 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:19:01 +0100 Subject: [PATCH 66/83] tests(compare-wf-p3): selectors_combine_simple test --- air-script/src/tests/comparison/mod.rs | 1 + .../comparison/selectors_combine_simple.rs | 115 ++++++++++++++++++ air-script/src/tests/selectors/mod.rs | 4 +- 3 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/selectors_combine_simple.rs diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 33b99eb10..02d5716b2 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -24,4 +24,5 @@ mod list_comprehension_nested; mod list_folding; mod pub_inputs; mod selectors; +mod selectors_combine_simple; mod selectors_with_evaluators; diff --git a/air-script/src/tests/comparison/selectors_combine_simple.rs b/air-script/src/tests/comparison/selectors_combine_simple.rs new file mode 100644 index 000000000..e57437235 --- /dev/null +++ b/air-script/src/tests/comparison/selectors_combine_simple.rs @@ -0,0 +1,115 @@ +//! Cross-backend comparison test for the SelectorsCombineSimple AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the SelectorsCombineSimple AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::selectors::{ + selectors_combine_simple::{ + PublicInputs, SelectorsAir as WinterfellSelectorsCombineSimpleAir, + }, + selectors_combine_simple_plonky3::SelectorsAir as Plonky3SelectorsCombineSimpleAir, + }, +}; + +/// Configuration for SelectorsCombineSimple AIR cross-backend comparison tests. +struct SelectorsCombineSimpleTestConfig { + /// The trace length. + trace_length: usize, +} + +impl SelectorsCombineSimpleTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 4]; + + for row in 1..length { + trace[3][row] = Felt::ONE; + } + + trace + } +} + +impl CrossBackendTestConfig for SelectorsCombineSimpleTestConfig { + type WinterfellAir = WinterfellSelectorsCombineSimpleAir; + type Plonky3Air = Plonky3SelectorsCombineSimpleAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 4 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 1]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellSelectorsCombineSimpleAir { + WinterfellSelectorsCombineSimpleAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3SelectorsCombineSimpleAir { + Plonky3SelectorsCombineSimpleAir + } +} + +#[test] +fn test_selectors_combine_simple_air_constraint_comparison() { + let config = SelectorsCombineSimpleTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "SelectorsCombineSimple AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_selectors_combine_simple_air_constraint_comparison_random_inputs() { + let config = SelectorsCombineSimpleTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_selectors_combine_simple_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("SelectorsCombineSimple AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/selectors/mod.rs b/air-script/src/tests/selectors/mod.rs index dfe926ff4..a785352e9 100644 --- a/air-script/src/tests/selectors/mod.rs +++ b/air-script/src/tests/selectors/mod.rs @@ -3,7 +3,7 @@ pub mod selectors; #[rustfmt::skip] #[allow(clippy::all)] -mod selectors_combine_simple; +pub mod selectors_combine_simple; #[rustfmt::skip] #[allow(clippy::all)] mod selectors_combine_complex; @@ -20,7 +20,7 @@ pub mod selectors_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod selectors_combine_simple_plonky3; +pub mod selectors_combine_simple_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] From 1ee4d5a621e578a585d629e432bc3627307ecb0e Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:21:14 +0100 Subject: [PATCH 67/83] tests(compare-wf-p3): selectors_combine_with_list_comprehensions test --- air-script/src/tests/comparison/mod.rs | 1 + ...ectors_combine_with_list_comprehensions.rs | 120 ++++++++++++++++++ air-script/src/tests/selectors/mod.rs | 4 +- 3 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/selectors_combine_with_list_comprehensions.rs diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 02d5716b2..34e0f01cb 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -25,4 +25,5 @@ mod list_folding; mod pub_inputs; mod selectors; mod selectors_combine_simple; +mod selectors_combine_with_list_comprehensions; mod selectors_with_evaluators; diff --git a/air-script/src/tests/comparison/selectors_combine_with_list_comprehensions.rs b/air-script/src/tests/comparison/selectors_combine_with_list_comprehensions.rs new file mode 100644 index 000000000..9e5733541 --- /dev/null +++ b/air-script/src/tests/comparison/selectors_combine_with_list_comprehensions.rs @@ -0,0 +1,120 @@ +//! Cross-backend comparison test for the SelectorsCombineWithListComprehensions AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the SelectorsCombineWithListComprehensions AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::selectors::{ + selectors_combine_with_list_comprehensions::{ + PublicInputs, SelectorsAir as WinterfellSelectorsCombineWithListComprehensionsAir, + }, + selectors_combine_with_list_comprehensions_plonky3::SelectorsAir as Plonky3SelectorsCombineWithListComprehensionsAir, + }, +}; + +/// Configuration for SelectorsCombineWithListComprehensions AIR cross-backend comparison tests. +struct SelectorsCombineWithListComprehensionsTestConfig { + /// The trace length. + trace_length: usize, +} + +impl SelectorsCombineWithListComprehensionsTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 6]; + + for row in 0..length { + trace[4][row] = Felt::new(8); + if row > 0 { + trace[5][row] = Felt::new(8); + } + } + + trace + } +} + +impl CrossBackendTestConfig for SelectorsCombineWithListComprehensionsTestConfig { + type WinterfellAir = WinterfellSelectorsCombineWithListComprehensionsAir; + type Plonky3Air = Plonky3SelectorsCombineWithListComprehensionsAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 6 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 1]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellSelectorsCombineWithListComprehensionsAir { + WinterfellSelectorsCombineWithListComprehensionsAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3SelectorsCombineWithListComprehensionsAir { + Plonky3SelectorsCombineWithListComprehensionsAir + } +} + +#[test] +fn test_selectors_combine_with_list_comprehensions_air_constraint_comparison() { + let config = SelectorsCombineWithListComprehensionsTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "SelectorsCombineWithListComprehensions AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_selectors_combine_with_list_comprehensions_air_constraint_comparison_random_inputs() { + let config = SelectorsCombineWithListComprehensionsTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_selectors_combine_with_list_comprehensions_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!( + "SelectorsCombineWithListComprehensions AIR random comparison passed for all 51 iterations (0-50)" + ); +} diff --git a/air-script/src/tests/selectors/mod.rs b/air-script/src/tests/selectors/mod.rs index a785352e9..0cc8ac436 100644 --- a/air-script/src/tests/selectors/mod.rs +++ b/air-script/src/tests/selectors/mod.rs @@ -9,7 +9,7 @@ pub mod selectors_combine_simple; mod selectors_combine_complex; #[rustfmt::skip] #[allow(clippy::all)] -mod selectors_combine_with_list_comprehensions; +pub mod selectors_combine_with_list_comprehensions; #[rustfmt::skip] #[allow(clippy::all)] pub mod selectors_with_evaluators; @@ -32,7 +32,7 @@ pub mod selectors_with_evaluators_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod selectors_combine_with_list_comprehensions_plonky3; +pub mod selectors_combine_with_list_comprehensions_plonky3; mod test_air_plonky3; mod test_air_winterfell; From 4cc4535f48fbfa631a47b90d66031c5c97ad618f Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:25:13 +0100 Subject: [PATCH 68/83] tests(compare-wf-p3): system test --- air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/comparison/system.rs | 113 ++++++++++++++++++++++ air-script/src/tests/system/mod.rs | 4 +- 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/system.rs diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 34e0f01cb..6e3d83374 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -27,3 +27,4 @@ mod selectors; mod selectors_combine_simple; mod selectors_combine_with_list_comprehensions; mod selectors_with_evaluators; +mod system; diff --git a/air-script/src/tests/comparison/system.rs b/air-script/src/tests/comparison/system.rs new file mode 100644 index 000000000..8d212e3c4 --- /dev/null +++ b/air-script/src/tests/comparison/system.rs @@ -0,0 +1,113 @@ +//! Cross-backend comparison test for the System AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the System AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::system::{ + system::{PublicInputs, SystemAir as WinterfellSystemAir}, + system_plonky3::SystemAir as Plonky3SystemAir, + }, +}; + +/// Configuration for System AIR cross-backend comparison tests. +struct SystemTestConfig { + /// The trace length. + trace_length: usize, +} + +impl SystemTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 3]; + + for row in 1..length { + trace[0][row] = trace[0][row - 1] + Felt::ONE; + } + + trace + } +} + +impl CrossBackendTestConfig for SystemTestConfig { + type WinterfellAir = WinterfellSystemAir; + type Plonky3Air = Plonky3SystemAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 3 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellSystemAir { + WinterfellSystemAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3SystemAir { + Plonky3SystemAir + } +} + +#[test] +fn test_system_air_constraint_comparison() { + let config = SystemTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "System AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_system_air_constraint_comparison_random_inputs() { + let config = SystemTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_system_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("System AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/system/mod.rs b/air-script/src/tests/system/mod.rs index 7d8522c99..782561e32 100644 --- a/air-script/src/tests/system/mod.rs +++ b/air-script/src/tests/system/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod system; +pub mod system; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod system_plonky3; +pub mod system_plonky3; mod test_air_plonky3; mod test_air_winterfell; From 17760ac294d634c6f9780a7ad1c0884505c72d2f Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:34:03 +0100 Subject: [PATCH 69/83] tests(compare-wf-p3): trace_col_groups test --- air-script/src/tests/comparison/mod.rs | 1 + .../src/tests/comparison/trace_col_groups.rs | 114 ++++++++++++++++++ air-script/src/tests/trace_col_groups/mod.rs | 4 +- 3 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/trace_col_groups.rs diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 6e3d83374..d890255e8 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -28,3 +28,4 @@ mod selectors_combine_simple; mod selectors_combine_with_list_comprehensions; mod selectors_with_evaluators; mod system; +mod trace_col_groups; diff --git a/air-script/src/tests/comparison/trace_col_groups.rs b/air-script/src/tests/comparison/trace_col_groups.rs new file mode 100644 index 000000000..e1d692415 --- /dev/null +++ b/air-script/src/tests/comparison/trace_col_groups.rs @@ -0,0 +1,114 @@ +//! Cross-backend comparison test for the TraceColGroups AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the TraceColGroups AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::trace_col_groups::{ + trace_col_groups::{PublicInputs, TraceColGroupAir as WinterfellTraceColGroupAir}, + trace_col_groups_plonky3::TraceColGroupAir as Plonky3TraceColGroupAir, + }, +}; + +/// Configuration for TraceColGroups AIR cross-backend comparison tests. +struct TraceColGroupsTestConfig { + /// The trace length. + trace_length: usize, +} + +impl TraceColGroupsTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 9]; + + for row in 1..length { + trace[1][row] = trace[1][row - 1] - Felt::ONE; + trace[2][row] = trace[2][row - 1] + Felt::ONE; + } + + trace + } +} + +impl CrossBackendTestConfig for TraceColGroupsTestConfig { + type WinterfellAir = WinterfellTraceColGroupAir; + type Plonky3Air = Plonky3TraceColGroupAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 9 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellTraceColGroupAir { + WinterfellTraceColGroupAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3TraceColGroupAir { + Plonky3TraceColGroupAir + } +} + +#[test] +fn test_trace_col_groups_air_constraint_comparison() { + let config = TraceColGroupsTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "TraceColGroups AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_trace_col_groups_air_constraint_comparison_random_inputs() { + let config = TraceColGroupsTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_trace_col_groups_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("TraceColGroups AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/trace_col_groups/mod.rs b/air-script/src/tests/trace_col_groups/mod.rs index c2bb1aa0a..52792d516 100644 --- a/air-script/src/tests/trace_col_groups/mod.rs +++ b/air-script/src/tests/trace_col_groups/mod.rs @@ -3,8 +3,8 @@ mod test_air_winterfell; #[rustfmt::skip] #[allow(clippy::all)] -mod trace_col_groups; +pub mod trace_col_groups; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod trace_col_groups_plonky3; +pub mod trace_col_groups_plonky3; From fec85728982cd2495504635e67b6a7be8c4fbf29 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:38:28 +0100 Subject: [PATCH 70/83] tests(compare-wf-p3): variables test --- air-script/src/tests/comparison/mod.rs | 1 + air-script/src/tests/comparison/variables.rs | 120 +++++++++++++++++++ air-script/src/tests/variables/mod.rs | 4 +- 3 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/variables.rs diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index d890255e8..2c42a8d1f 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -29,3 +29,4 @@ mod selectors_combine_with_list_comprehensions; mod selectors_with_evaluators; mod system; mod trace_col_groups; +mod variables; diff --git a/air-script/src/tests/comparison/variables.rs b/air-script/src/tests/comparison/variables.rs new file mode 100644 index 000000000..4e9b02571 --- /dev/null +++ b/air-script/src/tests/comparison/variables.rs @@ -0,0 +1,120 @@ +//! Cross-backend comparison test for the Variables AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the Variables AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::variables::{ + variables::{PublicInputs, VariablesAir as WinterfellVariablesAir}, + variables_plonky3::VariablesAir as Plonky3VariablesAir, + }, +}; + +/// Configuration for Variables AIR cross-backend comparison tests. +struct VariablesTestConfig { + /// The trace length. + trace_length: usize, +} + +impl VariablesTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 4]; + + for row in 0..length { + trace[0][row] = Felt::ONE; + if row > 0 { + trace[1][row] = Felt::ONE; + } + } + + trace + } +} + +impl CrossBackendTestConfig for VariablesTestConfig { + type WinterfellAir = WinterfellVariablesAir; + type Plonky3Air = Plonky3VariablesAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 4 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16], [Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellVariablesAir { + WinterfellVariablesAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3VariablesAir { + Plonky3VariablesAir + } + + fn periodic_column_values(&self) -> Vec> { + vec![vec![1, 1, 1, 1, 1, 1, 1, 0]] + } +} + +#[test] +fn test_variables_air_constraint_comparison() { + let config = VariablesTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "Variables AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_variables_air_constraint_comparison_random_inputs() { + let config = VariablesTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_variables_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("Variables AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/variables/mod.rs b/air-script/src/tests/variables/mod.rs index 98685f537..a28c595ac 100644 --- a/air-script/src/tests/variables/mod.rs +++ b/air-script/src/tests/variables/mod.rs @@ -2,8 +2,8 @@ mod test_air_plonky3; mod test_air_winterfell; #[rustfmt::skip] #[allow(clippy::all)] -mod variables; +pub mod variables; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod variables_plonky3; +pub mod variables_plonky3; From f83c649b999e95128444dbe59707cbff7bde1dd3 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:43:50 +0100 Subject: [PATCH 71/83] tests(compare-wf-p3): cc_with_evaluators test --- .../tests/comparison/cc_with_evaluators.rs | 138 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + 2 files changed, 139 insertions(+) create mode 100644 air-script/src/tests/comparison/cc_with_evaluators.rs diff --git a/air-script/src/tests/comparison/cc_with_evaluators.rs b/air-script/src/tests/comparison/cc_with_evaluators.rs new file mode 100644 index 000000000..b57f81877 --- /dev/null +++ b/air-script/src/tests/comparison/cc_with_evaluators.rs @@ -0,0 +1,138 @@ +//! Cross-backend comparison test for the ConstraintComprehension AIR (evaluator variant). +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the evaluator-based constraint comprehension AIR. +//! +//! Note: This AIR shares generated outputs with `constraint_comprehension`. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::constraint_comprehension::{ + constraint_comprehension::{ + ConstraintComprehensionAir as WinterfellConstraintComprehensionAir, PublicInputs, + }, + constraint_comprehension_plonky3::ConstraintComprehensionAir as Plonky3ConstraintComprehensionAir, + }, +}; + +/// Configuration for ConstraintComprehension (with evaluators) cross-backend comparison tests. +struct ConstraintComprehensionWithEvaluatorsTestConfig { + /// The trace length. + trace_length: usize, +} + +impl ConstraintComprehensionWithEvaluatorsTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace: Vec> = vec![vec![Felt::ZERO; length]; 14]; + + for row in 0..length { + trace[0][row] = Felt::new(row as u64); + trace[1][row] = Felt::new(row as u64 * 2); + trace[2][row] = Felt::new(row as u64 * 3); + trace[3][row] = Felt::new(100); + trace[4][row] = Felt::new(row as u64 + 10); + trace[5][row] = Felt::new(row as u64 + 20); + + trace[6][row] = Felt::new(row as u64 + 1); + trace[7][row] = Felt::new(row as u64 + 2); + trace[8][row] = if row == 0 { + Felt::ZERO + } else { + Felt::new(row as u64 + 3) + }; + trace[9][row] = Felt::new(row as u64 + 4); + + trace[10][row] = trace[6][row]; + trace[11][row] = trace[7][row]; + trace[12][row] = trace[8][row]; + trace[13][row] = trace[9][row]; + } + + trace + } +} + +impl CrossBackendTestConfig for ConstraintComprehensionWithEvaluatorsTestConfig { + type WinterfellAir = WinterfellConstraintComprehensionAir; + type Plonky3Air = Plonky3ConstraintComprehensionAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 14 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellConstraintComprehensionAir { + WinterfellConstraintComprehensionAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3ConstraintComprehensionAir { + Plonky3ConstraintComprehensionAir + } +} + +#[test] +fn test_cc_with_evaluators_air_constraint_comparison() { + let config = ConstraintComprehensionWithEvaluatorsTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "ConstraintComprehension (with evaluators) comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_cc_with_evaluators_air_constraint_comparison_random_inputs() { + let config = ConstraintComprehensionWithEvaluatorsTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_cc_with_evaluators_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!( + "ConstraintComprehension (with evaluators) random comparison passed for all 51 iterations (0-50)" + ); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 2c42a8d1f..0aeeda125 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -5,6 +5,7 @@ mod binary; mod bitwise; +mod cc_with_evaluators; mod computed_indices_complex; mod computed_indices_simple; mod constant_in_range; From 31a82f082c91e11d4c54fb3332f68e94b63d1412 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:51:42 +0100 Subject: [PATCH 72/83] tests(compare-wf-p3): periodic_columns test --- air-script/src/tests/comparison/mod.rs | 1 + .../src/tests/comparison/periodic_columns.rs | 112 ++++++++++++++++++ air-script/src/tests/periodic_columns/mod.rs | 4 +- 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/periodic_columns.rs diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 0aeeda125..7dce3d79c 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -23,6 +23,7 @@ mod inlined_functions_simple; mod list_comprehension; mod list_comprehension_nested; mod list_folding; +mod periodic_columns; mod pub_inputs; mod selectors; mod selectors_combine_simple; diff --git a/air-script/src/tests/comparison/periodic_columns.rs b/air-script/src/tests/comparison/periodic_columns.rs new file mode 100644 index 000000000..2f49d1cda --- /dev/null +++ b/air-script/src/tests/comparison/periodic_columns.rs @@ -0,0 +1,112 @@ +//! Cross-backend comparison test for the PeriodicColumns AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the PeriodicColumns AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::periodic_columns::{ + periodic_columns::{PeriodicColumnsAir as WinterfellPeriodicColumnsAir, PublicInputs}, + periodic_columns_plonky3::PeriodicColumnsAir as Plonky3PeriodicColumnsAir, + }, +}; + +/// Configuration for PeriodicColumns AIR cross-backend comparison tests. +struct PeriodicColumnsTestConfig { + /// The trace length. + trace_length: usize, +} + +impl PeriodicColumnsTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + vec![vec![Felt::ZERO; length]; 3] + } +} + +impl CrossBackendTestConfig for PeriodicColumnsTestConfig { + type WinterfellAir = WinterfellPeriodicColumnsAir; + type Plonky3Air = Plonky3PeriodicColumnsAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 3 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 16]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellPeriodicColumnsAir { + WinterfellPeriodicColumnsAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3PeriodicColumnsAir { + Plonky3PeriodicColumnsAir + } + + fn periodic_column_values(&self) -> Vec> { + vec![vec![1, 0, 0, 0], vec![1, 1, 1, 1, 1, 1, 1, 0]] + } +} + +#[test] +fn test_periodic_columns_air_constraint_comparison() { + let config = PeriodicColumnsTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "PeriodicColumns AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_periodic_columns_air_constraint_comparison_random_inputs() { + let config = PeriodicColumnsTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_periodic_columns_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("PeriodicColumns AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/periodic_columns/mod.rs b/air-script/src/tests/periodic_columns/mod.rs index 0053e1aa8..8103805f7 100644 --- a/air-script/src/tests/periodic_columns/mod.rs +++ b/air-script/src/tests/periodic_columns/mod.rs @@ -1,10 +1,10 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod periodic_columns; +pub mod periodic_columns; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod periodic_columns_plonky3; +pub mod periodic_columns_plonky3; mod test_air_plonky3; mod test_air_winterfell; From ebae749b046844112a07f2239bbf95edc126ccd4 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 19:57:37 +0100 Subject: [PATCH 73/83] tests(compare-wf-p3): comprehension_periodic_binding test --- .../comprehension_periodic_binding.rs | 117 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + .../comprehension_periodic_binding/mod.rs | 6 +- 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 air-script/src/tests/comparison/comprehension_periodic_binding.rs diff --git a/air-script/src/tests/comparison/comprehension_periodic_binding.rs b/air-script/src/tests/comparison/comprehension_periodic_binding.rs new file mode 100644 index 000000000..256dff924 --- /dev/null +++ b/air-script/src/tests/comparison/comprehension_periodic_binding.rs @@ -0,0 +1,117 @@ +//! Cross-backend comparison test for the ComprehensionPeriodicBinding AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the ComprehensionPeriodicBinding AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::comprehension_periodic_binding::{ + comprehension_periodic_binding::{ + ComprehensionPeriodicBindingTest as WinterfellComprehensionPeriodicBindingTest, + PublicInputs, + }, + comprehension_periodic_binding_plonky3::ComprehensionPeriodicBindingTest as Plonky3ComprehensionPeriodicBindingTest, + }, +}; + +/// Configuration for ComprehensionPeriodicBinding AIR cross-backend comparison tests. +struct ComprehensionPeriodicBindingTestConfig { + /// The trace length. + trace_length: usize, +} + +impl ComprehensionPeriodicBindingTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + vec![vec![Felt::ZERO; length]; 2] + } +} + +impl CrossBackendTestConfig for ComprehensionPeriodicBindingTestConfig { + type WinterfellAir = WinterfellComprehensionPeriodicBindingTest; + type Plonky3Air = Plonky3ComprehensionPeriodicBindingTest; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 2 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 1]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellComprehensionPeriodicBindingTest { + WinterfellComprehensionPeriodicBindingTest::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3ComprehensionPeriodicBindingTest { + Plonky3ComprehensionPeriodicBindingTest + } + + fn periodic_column_values(&self) -> Vec> { + vec![vec![1, 2], vec![3, 4]] + } +} + +#[test] +fn test_comprehension_periodic_binding_air_constraint_comparison() { + let config = ComprehensionPeriodicBindingTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "ComprehensionPeriodicBinding AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_comprehension_periodic_binding_air_constraint_comparison_random_inputs() { + let config = ComprehensionPeriodicBindingTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_comprehension_periodic_binding_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!( + "ComprehensionPeriodicBinding AIR random comparison passed for all 51 iterations (0-50)" + ); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 7dce3d79c..7e5e40ec6 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -6,6 +6,7 @@ mod binary; mod bitwise; mod cc_with_evaluators; +mod comprehension_periodic_binding; mod computed_indices_complex; mod computed_indices_simple; mod constant_in_range; diff --git a/air-script/src/tests/comprehension_periodic_binding/mod.rs b/air-script/src/tests/comprehension_periodic_binding/mod.rs index 83244b98e..8003c776d 100644 --- a/air-script/src/tests/comprehension_periodic_binding/mod.rs +++ b/air-script/src/tests/comprehension_periodic_binding/mod.rs @@ -1,3 +1,7 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod comprehension_periodic_binding; +pub mod comprehension_periodic_binding; +#[rustfmt::skip] +#[allow(clippy::all)] +#[allow(unused_imports)] +pub mod comprehension_periodic_binding_plonky3; From c2a1ae533a618fa0fcbb8e0ed47e8104dfed5d9b Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 23:00:58 +0100 Subject: [PATCH 74/83] tests(compare-wf-p3): add support for aux trace in test harness --- .../test_utils/cross_backend_comparison.rs | 334 +++++++++++++++++- 1 file changed, 317 insertions(+), 17 deletions(-) diff --git a/air-script/src/test_utils/cross_backend_comparison.rs b/air-script/src/test_utils/cross_backend_comparison.rs index 8abdc11c6..d4b5fffa1 100644 --- a/air-script/src/test_utils/cross_backend_comparison.rs +++ b/air-script/src/test_utils/cross_backend_comparison.rs @@ -30,8 +30,8 @@ use p3_miden_air::{MidenAir, MidenAirBuilder}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; use winter_air::{ - Air, BatchingMethod, EvaluationFrame, FieldExtension, ProofOptions as WinterProofOptions, - TraceInfo, + Air, AuxRandElements, BatchingMethod, EvaluationFrame, FieldExtension, + ProofOptions as WinterProofOptions, TraceInfo, }; use winter_math::{FieldElement, ToElements, fields::f64::BaseElement as WinterfellFelt}; use winter_utils::Serializable; @@ -210,8 +210,10 @@ where // Create evaluation frame let frame = EvaluationFrame::from_rows(current, next); - // Allocate result buffer based on number of transition constraints - let num_constraints = air.context().num_transition_constraints(); + // Allocate result buffer based on main transition constraints only. + // Winterfell counts main + aux constraints together in the context, but + // evaluate_transition only writes main constraints. + let num_constraints = air.context().num_main_transition_constraints(); let mut result = vec![WinterfellFelt::ZERO; num_constraints]; // Evaluate transition constraints with periodic values @@ -228,6 +230,58 @@ where result.iter().map(|e| (is_transition * *e).to_canonical_u64()).collect() } +/// Evaluates Winterfell auxiliary transition constraints at a specific row. +/// +/// This uses the aux trace and verifier randomness, and applies the same +/// is_transition selector as main constraints. +pub fn evaluate_winterfell_aux_transition( + air: &A, + main_trace: &[Vec], + aux_trace: &[Vec], + row: usize, + num_rows: usize, + periodic_values: &[WinterfellFelt], + aux_rand_elements: &AuxRandElements, +) -> Vec +where + A: Air, +{ + let main_width = main_trace.len(); + let aux_width = aux_trace.len(); + let trace_length = main_trace[0].len(); + + let main_current: Vec = + (0..main_width).map(|col| main_trace[col][row]).collect(); + let next_row = (row + 1) % trace_length; + let main_next: Vec = + (0..main_width).map(|col| main_trace[col][next_row]).collect(); + let main_frame = EvaluationFrame::from_rows(main_current, main_next); + + let aux_current: Vec = (0..aux_width).map(|col| aux_trace[col][row]).collect(); + let aux_next: Vec = + (0..aux_width).map(|col| aux_trace[col][next_row]).collect(); + let aux_frame = EvaluationFrame::from_rows(aux_current, aux_next); + + let num_constraints = air.context().num_aux_transition_constraints(); + let mut result = vec![WinterfellFelt::ZERO; num_constraints]; + + air.evaluate_aux_transition( + &main_frame, + &aux_frame, + periodic_values, + aux_rand_elements, + &mut result, + ); + + let is_transition = if row < num_rows - 1 { + WinterfellFelt::ONE + } else { + WinterfellFelt::ZERO + }; + + result.iter().map(|e| (is_transition * *e).to_canonical_u64()).collect() +} + /// Evaluates periodic column values at a specific row. /// /// Periodic columns repeat with a given period. At row `r`, the value is @@ -323,6 +377,31 @@ where .collect() } +/// Gets Winterfell aux boundary constraint info. +/// Returns (column, row, expected_value) for each aux assertion. +pub fn get_winterfell_aux_boundary_assertions( + air: &A, + aux_rand_elements: &AuxRandElements, +) -> Vec<(usize, usize, u64)> +where + A: Air, +{ + air.get_aux_assertions(aux_rand_elements) + .iter() + .map(|assertion| { + let col = assertion.column(); + let step = assertion.first_step(); + let values = assertion.values(); + let expected = if !values.is_empty() { + values[0].to_canonical_u64() + } else { + 0 + }; + (col, step, expected) + }) + .collect() +} + /// A view into two consecutive rows of the trace matrix for constraint evaluation. pub struct TwoRowMatrixView { current_row: Vec, @@ -360,6 +439,8 @@ impl Matrix for TwoRowMatrixView { pub struct ConstraintCapturingBuilder { /// View of current and next rows main_view: TwoRowMatrixView, + /// View of current and next aux rows (if any) + aux_view: Option>, /// Current row index being evaluated current_row: usize, /// Total number of rows in the trace @@ -368,6 +449,10 @@ pub struct ConstraintCapturingBuilder { public_values: Vec, /// Periodic column evaluations (empty for simple AIRs) periodic_values: Vec, + /// Verifier randomness for aux constraints + randomness: Vec, + /// Aux bus boundary values + aux_bus_boundary_values: Vec, /// Captured constraint evaluations captured_constraints: Vec, } @@ -376,9 +461,12 @@ impl ConstraintCapturingBuilder { /// Creates a new constraint capturing builder for a specific row. pub fn new( trace: &RowMajorMatrix, + aux_trace: Option<&RowMajorMatrix>, row: usize, public_values: Vec, periodic_values: Vec, + randomness: Vec, + aux_bus_boundary_values: Vec, ) -> Self { let num_rows = trace.height(); let width = trace.width(); @@ -398,12 +486,31 @@ impl ConstraintCapturingBuilder { let main_view = TwoRowMatrixView::new(current_row, next_row); + let aux_view = aux_trace.map(|aux| { + let aux_width = aux.width(); + let aux_current: Vec = aux + .row_slice(row) + .map(|s| s.iter().cloned().collect()) + .unwrap_or_else(|| vec![F::ZERO; aux_width]); + + let aux_next_idx = (row + 1) % num_rows; + let aux_next: Vec = aux + .row_slice(aux_next_idx) + .map(|s| s.iter().cloned().collect()) + .unwrap_or_else(|| vec![F::ZERO; aux_width]); + + TwoRowMatrixView::new(aux_current, aux_next) + }); + Self { main_view, + aux_view, current_row: row, num_rows, public_values, periodic_values, + randomness, + aux_bus_boundary_values, captured_constraints: Vec::new(), } } @@ -490,15 +597,23 @@ impl MidenAirBuilder for ConstraintC } fn permutation(&self) -> Self::MP { - self.main() + match &self.aux_view { + Some(view) => TwoRowMatrixView::new(view.current_row.clone(), view.next_row.clone()), + None => { + // Plonky3 expects a permutation matrix even when aux width is 0. + // We return an empty view to keep the builder generic without + // special-casing non-aux AIRs in generated code paths. + TwoRowMatrixView::new(Vec::new(), Vec::new()) + }, + } } fn permutation_randomness(&self) -> &[Self::RandomVar] { - &[] + &self.randomness } fn aux_bus_boundary_values(&self) -> &[Self::VarEF] { - &[] + &self.aux_bus_boundary_values } } @@ -728,6 +843,44 @@ pub trait CrossBackendTestConfig { vec![] } + /// Builds verifier-supplied randomness for auxiliary constraints. + /// + /// Randomness is always generated when `num_randomness > 0`. If the length is 0, + /// this returns an empty vector. + fn build_aux_randomness( + &self, + seed_name: &str, + iteration: u64, + num_randomness: usize, + ) -> Vec { + if num_randomness == 0 { + return vec![]; + } + + let seed = generate_test_seed(seed_name, iteration); + let mut rng = ChaCha8Rng::seed_from_u64(seed); + + (0..num_randomness) + .map(|_| 1 + rng.random_range(0..(GOLDILOCKS_MODULUS - 1))) + .collect() + } + + /// Builds a Plonky3 auxiliary trace. Defaults to None; run_comparison will + /// supply a zeroed aux trace if aux width > 0 and no aux trace is provided. + fn build_plonky3_aux_trace( + &self, + _plonky3_air: &Self::Plonky3Air, + _main_trace: &RowMajorMatrix, + _randomness: &[Goldilocks], + ) -> Option> { + None + } + + /// Returns aux bus boundary values (empty by default). + fn aux_bus_boundary_values(&self) -> Vec { + vec![] + } + /// Builds a random trace in Winterfell format (column-major) using a seeded RNG. /// /// The seed is generated from the test name and iteration number using @@ -812,12 +965,12 @@ pub fn run_comparison(config: &C, source: TraceSource<'_>) -> ComparisonResul where C: CrossBackendTestConfig, { - let winterfell_trace = match source { - TraceSource::Default => config.build_winterfell_trace(), + let (winterfell_trace, seed_name, seed_iteration) = match source { + TraceSource::Default => (config.build_winterfell_trace(), std::any::type_name::(), 0), TraceSource::Random { test_name, iteration } => { - config.build_random_winterfell_trace(test_name, iteration) + (config.build_random_winterfell_trace(test_name, iteration), test_name, iteration) }, - TraceSource::Custom(trace) => trace.to_vec(), + TraceSource::Custom(trace) => (trace.to_vec(), std::any::type_name::(), 0), }; let trace_length = config.trace_length(); @@ -829,21 +982,71 @@ where let winterfell_pub_inputs = config.build_winterfell_public_inputs(); let plonky3_pub_inputs = config.build_plonky3_public_inputs(); - // Create Winterfell AIR - let trace_info = TraceInfo::new(config.trace_width(), trace_length); + // Create Plonky3 AIR first so we can determine aux trace parameters. + let plonky3_air = config.create_plonky3_air(); + let aux_width = plonky3_air.aux_width(); + let num_randomness = plonky3_air.num_randomness(); + + // Create Winterfell AIR with appropriate trace info. + let trace_info = if aux_width > 0 || num_randomness > 0 { + TraceInfo::new_multi_segment( + config.trace_width(), + aux_width, + num_randomness, + trace_length, + vec![], + ) + } else { + TraceInfo::new(config.trace_width(), trace_length) + }; let proof_options = default_proof_options(); let winterfell_air = config.create_winterfell_air(trace_info, winterfell_pub_inputs, proof_options); - // Create Plonky3 AIR - let plonky3_air = config.create_plonky3_air(); - // Get the last_step for Winterfell (where last-row boundary constraints apply) let last_step = trace_length - winterfell_air.context().num_transition_exemptions(); // Get periodic column definitions let periodic_columns = config.periodic_column_values(); + // Aux trace support is optional; when aux width is 0 there is no aux data. + let aux_randomness_u64 = config.build_aux_randomness(seed_name, seed_iteration, num_randomness); + let plonky3_randomness: Vec = + aux_randomness_u64.iter().map(|val| Goldilocks::from_u64(*val)).collect(); + let winterfell_randomness: Vec = + aux_randomness_u64.iter().map(|val| WinterfellFelt::new(*val)).collect(); + let aux_rand_elements = AuxRandElements::new(winterfell_randomness); + + let aux_trace_plonky3: Option> = if aux_width > 0 { + let custom_aux = + config.build_plonky3_aux_trace(&plonky3_air, &plonky3_trace, &plonky3_randomness); + custom_aux + .or_else(|| plonky3_air.build_aux_trace(&plonky3_trace, &plonky3_randomness)) + .or_else(|| { + // Default to an all-zero aux trace when the width is > 0 but no + // aux trace is provided. This keeps aux support optional for + // tests that don't exercise auxiliary constraints. + Some(RowMajorMatrix::new( + vec![Goldilocks::ZERO; trace_length * aux_width], + aux_width, + )) + }) + } else { + None + }; + let aux_trace_winterfell = aux_trace_plonky3.as_ref().map(plonky3_trace_to_winterfell); + + let mut aux_boundary_values = config.aux_bus_boundary_values(); + if aux_width > 0 && aux_boundary_values.is_empty() { + aux_boundary_values = vec![0; aux_width]; + } + let plonky3_aux_boundary_values: Vec = + aux_boundary_values.iter().map(|val| Goldilocks::from_u64(*val)).collect(); + + // Use Winterfell's assertion count to split Plonky3 boundary vs transition + // constraints since Plonky3 does not report boundary counts separately. + let main_boundary_count = winterfell_air.get_assertions().len(); + // Evaluate constraints at each row where transition constraints are enforced. // Rows >= last_step have transition exemptions in Winterfell, so we skip them // to ensure both backends are compared on rows with the same constraint semantics. @@ -865,6 +1068,18 @@ where last_step, ); + let w_aux_boundary = match aux_trace_winterfell.as_ref() { + Some(aux_trace) => evaluate_winterfell_aux_boundary( + &winterfell_air, + aux_trace, + row, + trace_length, + last_step, + &aux_rand_elements, + ), + None => Vec::new(), + }; + // Winterfell: evaluate transition constraints with is_transition selector // and periodic values let w_transition = evaluate_winterfell_transition( @@ -875,17 +1090,46 @@ where &winterfell_periodic, ); - // Combine: boundary constraints first, then transition constraints + let w_aux_transition = match aux_trace_winterfell.as_ref() { + Some(aux_trace) => evaluate_winterfell_aux_transition( + &winterfell_air, + &winterfell_trace, + aux_trace, + row, + trace_length, + &winterfell_periodic, + &aux_rand_elements, + ), + None => Vec::new(), + }; + + // Combine: to follow the order of emitted constraints in Plonky3, we inject: + // - main boundary constraints + // - aux boundary constraints (Winterfell-only: Plonky3 eval does not emit + // explicit aux boundary assertions; bus boundary values are handled outside eval) + // - main transition constraints + // - aux transition constraints + // TODO: Best guess for now: Plonky3 eval does not emit aux boundary assertions, + // so we are not comparing those constraints independently across backends. + // We replicate Winterfell's aux boundary evaluations into the Plonky3 result + // to keep constraint ordering consistent. Follow-up: emit aux boundary + // assertions in Plonky3 codegen so both backends produce them directly, + // then remove this injection. let mut w_all = w_boundary; + w_all.extend(w_aux_boundary.clone()); w_all.extend(w_transition); + w_all.extend(w_aux_transition); winterfell_results.push(w_all); // Plonky3: create a capturing builder for this row with periodic values let mut builder = ConstraintCapturingBuilder::new( &plonky3_trace, + aux_trace_plonky3.as_ref(), row, plonky3_pub_inputs.clone(), plonky3_periodic, + plonky3_randomness.clone(), + plonky3_aux_boundary_values.clone(), ); // Evaluate the Plonky3 AIR @@ -893,6 +1137,21 @@ where // Get captured constraints let p_all = builder.get_captured_constraints(); + + let p_all = if !w_aux_boundary.is_empty() && p_all.len() >= main_boundary_count { + let (p_boundary, p_transition) = p_all.split_at(main_boundary_count); + let mut combined = Vec::with_capacity(p_all.len() + w_aux_boundary.len()); + combined.extend_from_slice(p_boundary); + // Plonky3 generated AIRs do not emit explicit aux boundary assertions, + // so we evaluate them from the Winterfell AIR and insert them here + // to compare all constraints in a consistent order. + combined.extend_from_slice(&w_aux_boundary); + combined.extend_from_slice(p_transition); + combined + } else { + p_all + }; + plonky3_results.push(p_all); } @@ -957,3 +1216,44 @@ where results } + +/// Evaluates auxiliary boundary constraints at a specific row for Winterfell. +pub fn evaluate_winterfell_aux_boundary( + air: &A, + aux_trace: &[Vec], + row: usize, + num_rows: usize, + last_step: usize, + aux_rand_elements: &AuxRandElements, +) -> Vec +where + A: Air, +{ + let assertions = get_winterfell_aux_boundary_assertions(air, aux_rand_elements); + let mut results = Vec::new(); + + for (col, assertion_row, expected) in assertions { + let actual = aux_trace[col][row].to_canonical_u64(); + + let is_first_row = if row == 0 { 1u64 } else { 0u64 }; + let is_last_row = if row == num_rows - 1 { 1u64 } else { 0u64 }; + + if assertion_row == 0 { + let actual_felt = WinterfellFelt::new(actual); + let expected_felt = WinterfellFelt::new(expected); + let is_first_felt = WinterfellFelt::new(is_first_row); + let diff = actual_felt - expected_felt; + let result = is_first_felt * diff; + results.push(result.to_canonical_u64()); + } else if assertion_row == last_step { + let actual_felt = WinterfellFelt::new(actual); + let expected_felt = WinterfellFelt::new(expected); + let is_last_felt = WinterfellFelt::new(is_last_row); + let diff = actual_felt - expected_felt; + let result = is_last_felt * diff; + results.push(result.to_canonical_u64()); + } + } + + results +} From e08f4d44ad4f684170c1a41a1bb5811a10a7db97 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 23:38:47 +0100 Subject: [PATCH 75/83] tests(compare-wf-p3): buses_simple test: TODO: follow-up needed TODO: can't evaluate without constraints on winterfell TODO: aux width mismatch in build_aux_trace --- air-script/src/tests/buses/mod.rs | 4 +- .../src/tests/comparison/buses_simple.rs | 134 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + 3 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/buses_simple.rs diff --git a/air-script/src/tests/buses/mod.rs b/air-script/src/tests/buses/mod.rs index 5c0ca958c..5d7541d5c 100644 --- a/air-script/src/tests/buses/mod.rs +++ b/air-script/src/tests/buses/mod.rs @@ -3,7 +3,7 @@ mod buses_complex; #[rustfmt::skip] #[allow(clippy::all)] -mod buses_simple; +pub mod buses_simple; #[rustfmt::skip] #[allow(clippy::all)] mod buses_varlen_boundary_both; @@ -21,7 +21,7 @@ mod buses_complex_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod buses_simple_plonky3; +pub mod buses_simple_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] diff --git a/air-script/src/tests/comparison/buses_simple.rs b/air-script/src/tests/comparison/buses_simple.rs new file mode 100644 index 000000000..032d7048d --- /dev/null +++ b/air-script/src/tests/comparison/buses_simple.rs @@ -0,0 +1,134 @@ +//! Cross-backend comparison test for the BusesSimple AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the BusesSimple AIR at every row of the trace. + +use p3_field::PrimeCharacteristicRing; +use p3_goldilocks::Goldilocks; +use p3_matrix::{Matrix, dense::RowMajorMatrix}; +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::buses::{ + buses_simple::{BusesAir as WinterfellBusesSimpleAir, PublicInputs}, + buses_simple_plonky3::BusesAir as Plonky3BusesSimpleAir, + }, +}; + +/// Configuration for BusesSimple AIR cross-backend comparison tests. +struct BusesSimpleTestConfig { + /// The trace length. + trace_length: usize, +} + +impl BusesSimpleTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut col0 = vec![Felt::ZERO; length]; + + for row in 0..length { + col0[row] = if row % 2 == 0 { Felt::ZERO } else { Felt::ONE }; + } + + vec![col0] + } +} + +impl CrossBackendTestConfig for BusesSimpleTestConfig { + type WinterfellAir = WinterfellBusesSimpleAir; + type Plonky3Air = Plonky3BusesSimpleAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 1 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 2]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellBusesSimpleAir { + WinterfellBusesSimpleAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3BusesSimpleAir { + Plonky3BusesSimpleAir + } + + fn build_plonky3_aux_trace( + &self, + _plonky3_air: &Plonky3BusesSimpleAir, + main_trace: &RowMajorMatrix, + _randomness: &[Goldilocks], + ) -> Option> { + let aux_width = crate::tests::buses::buses_simple_plonky3::AUX_WIDTH; + let num_rows = main_trace.height(); + + // TODO: Follow up to fix codegen + goldens for buses_simple_plonky3: + // AUX_WIDTH is 2 but buses_initial_values() returns a single element, + // which panics inside the generated build_aux_trace. Provide a zeroed + // aux trace here so both backends evaluate with consistent aux inputs. + Some(RowMajorMatrix::new(vec![Goldilocks::ZERO; aux_width * num_rows], aux_width)) + } +} + +#[test] +#[ignore = "TODO: Winterfell codegen uses empty main degrees/assertions; fix goldens or codegen to satisfy AirContext::new_multi_segment"] +fn test_buses_simple_air_constraint_comparison() { + let config = BusesSimpleTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "BusesSimple AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +#[ignore = "TODO: Winterfell codegen uses empty main degrees/assertions; fix goldens or codegen to satisfy AirContext::new_multi_segment"] +fn test_buses_simple_air_constraint_comparison_random_inputs() { + let config = BusesSimpleTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_buses_simple_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("BusesSimple AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 7e5e40ec6..65c1a886e 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -5,6 +5,7 @@ mod binary; mod bitwise; +mod buses_simple; mod cc_with_evaluators; mod comprehension_periodic_binding; mod computed_indices_complex; From 9504df6a7a1861373b9c7d693975fa92419aac22 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Fri, 30 Jan 2026 23:43:19 +0100 Subject: [PATCH 76/83] tests(compare-wf-p3): buses_complex test --- air-script/src/tests/buses/mod.rs | 4 +- .../src/tests/comparison/buses_complex.rs | 119 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/buses_complex.rs diff --git a/air-script/src/tests/buses/mod.rs b/air-script/src/tests/buses/mod.rs index 5d7541d5c..c7bc772af 100644 --- a/air-script/src/tests/buses/mod.rs +++ b/air-script/src/tests/buses/mod.rs @@ -1,6 +1,6 @@ #[rustfmt::skip] #[allow(clippy::all)] -mod buses_complex; +pub mod buses_complex; #[rustfmt::skip] #[allow(clippy::all)] pub mod buses_simple; @@ -17,7 +17,7 @@ mod buses_varlen_boundary_last; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod buses_complex_plonky3; +pub mod buses_complex_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] diff --git a/air-script/src/tests/comparison/buses_complex.rs b/air-script/src/tests/comparison/buses_complex.rs new file mode 100644 index 000000000..3448df838 --- /dev/null +++ b/air-script/src/tests/comparison/buses_complex.rs @@ -0,0 +1,119 @@ +//! Cross-backend comparison test for the BusesComplex AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the BusesComplex AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::buses::{ + buses_complex::{BusesAir as WinterfellBusesComplexAir, PublicInputs}, + buses_complex_plonky3::BusesAir as Plonky3BusesComplexAir, + }, +}; + +/// Configuration for BusesComplex AIR cross-backend comparison tests. +struct BusesComplexTestConfig { + /// The trace length. + trace_length: usize, +} + +impl BusesComplexTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 7]; + + for row in 1..length { + trace[0][row] = Felt::ONE - trace[0][row - 1]; + trace[1][row] = Felt::ONE - trace[1][row - 1]; + trace[2][row] = Felt::ONE - trace[2][row - 1]; + trace[3][row] = Felt::ONE - trace[3][row - 1]; + trace[4][row] = Felt::ONE - trace[4][row - 1]; + trace[5][row] = trace[4][row - 1]; + trace[6][row] = trace[4][row - 1]; + } + + trace + } +} + +impl CrossBackendTestConfig for BusesComplexTestConfig { + type WinterfellAir = WinterfellBusesComplexAir; + type Plonky3Air = Plonky3BusesComplexAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 7 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 2]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellBusesComplexAir { + WinterfellBusesComplexAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3BusesComplexAir { + Plonky3BusesComplexAir + } +} + +#[test] +fn test_buses_complex_air_constraint_comparison() { + let config = BusesComplexTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "BusesComplex AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_buses_complex_air_constraint_comparison_random_inputs() { + let config = BusesComplexTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_buses_complex_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("BusesComplex AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 65c1a886e..4c050ae62 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -5,6 +5,7 @@ mod binary; mod bitwise; +mod buses_complex; mod buses_simple; mod cc_with_evaluators; mod comprehension_periodic_binding; From 0c9a9d319f96778db051a1b5dc45a6a8c5f791fe Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Sat, 31 Jan 2026 00:08:19 +0100 Subject: [PATCH 77/83] tests(compare-wf-p3): selectors_combine_complex test --- air-script/src/tests/comparison/mod.rs | 1 + .../comparison/selectors_combine_complex.rs | 110 ++++++++++++++++++ air-script/src/tests/selectors/mod.rs | 4 +- 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/selectors_combine_complex.rs diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 4c050ae62..cb3c028ef 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -29,6 +29,7 @@ mod list_folding; mod periodic_columns; mod pub_inputs; mod selectors; +mod selectors_combine_complex; mod selectors_combine_simple; mod selectors_combine_with_list_comprehensions; mod selectors_with_evaluators; diff --git a/air-script/src/tests/comparison/selectors_combine_complex.rs b/air-script/src/tests/comparison/selectors_combine_complex.rs new file mode 100644 index 000000000..9ceba9785 --- /dev/null +++ b/air-script/src/tests/comparison/selectors_combine_complex.rs @@ -0,0 +1,110 @@ +//! Cross-backend comparison test for the SelectorsCombineComplex AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the SelectorsCombineComplex AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::selectors::{ + selectors_combine_complex::{ + PublicInputs, SelectorsAir as WinterfellSelectorsCombineComplexAir, + }, + selectors_combine_complex_plonky3::SelectorsAir as Plonky3SelectorsCombineComplexAir, + }, +}; + +/// Configuration for SelectorsCombineComplex AIR cross-backend comparison tests. +struct SelectorsCombineComplexTestConfig { + /// The trace length. + trace_length: usize, +} + +impl SelectorsCombineComplexTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + + vec![vec![Felt::ZERO; length]; 6] + } +} + +impl CrossBackendTestConfig for SelectorsCombineComplexTestConfig { + type WinterfellAir = WinterfellSelectorsCombineComplexAir; + type Plonky3Air = Plonky3SelectorsCombineComplexAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 6 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 1]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellSelectorsCombineComplexAir { + WinterfellSelectorsCombineComplexAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3SelectorsCombineComplexAir { + Plonky3SelectorsCombineComplexAir + } +} + +#[test] +fn test_selectors_combine_complex_air_constraint_comparison() { + let config = SelectorsCombineComplexTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "SelectorsCombineComplex AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +fn test_selectors_combine_complex_air_constraint_comparison_random_inputs() { + let config = SelectorsCombineComplexTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_selectors_combine_complex_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("SelectorsCombineComplex AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/selectors/mod.rs b/air-script/src/tests/selectors/mod.rs index 0cc8ac436..eb01696d5 100644 --- a/air-script/src/tests/selectors/mod.rs +++ b/air-script/src/tests/selectors/mod.rs @@ -6,7 +6,7 @@ pub mod selectors; pub mod selectors_combine_simple; #[rustfmt::skip] #[allow(clippy::all)] -mod selectors_combine_complex; +pub mod selectors_combine_complex; #[rustfmt::skip] #[allow(clippy::all)] pub mod selectors_combine_with_list_comprehensions; @@ -24,7 +24,7 @@ pub mod selectors_combine_simple_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod selectors_combine_complex_plonky3; +pub mod selectors_combine_complex_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] From ecb7e6de359ec4ac43b31d298811b29c61d0ec67 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Sat, 31 Jan 2026 00:12:40 +0100 Subject: [PATCH 78/83] tests(compare-wf-p3): buses_simple_with_evaluators test: TODO: follow-up needed TODO: can't evaluate without constraints on winterfell --- .../buses_simple_with_evaluators.rs | 136 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + 2 files changed, 137 insertions(+) create mode 100644 air-script/src/tests/comparison/buses_simple_with_evaluators.rs diff --git a/air-script/src/tests/comparison/buses_simple_with_evaluators.rs b/air-script/src/tests/comparison/buses_simple_with_evaluators.rs new file mode 100644 index 000000000..cb2d013dc --- /dev/null +++ b/air-script/src/tests/comparison/buses_simple_with_evaluators.rs @@ -0,0 +1,136 @@ +//! Cross-backend comparison test for the BusesSimpleWithEvaluators AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the evaluator-based buses AIR at every row of the trace. +//! +//! Note: This AIR shares generated outputs with `buses_simple`. + +use p3_field::PrimeCharacteristicRing; +use p3_goldilocks::Goldilocks; +use p3_matrix::{Matrix, dense::RowMajorMatrix}; +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::buses::{ + buses_simple::{BusesAir as WinterfellBusesSimpleAir, PublicInputs}, + buses_simple_plonky3::BusesAir as Plonky3BusesSimpleAir, + }, +}; + +/// Configuration for BusesSimpleWithEvaluators AIR cross-backend comparison tests. +struct BusesSimpleWithEvaluatorsTestConfig { + /// The trace length. + trace_length: usize, +} + +impl BusesSimpleWithEvaluatorsTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut col0 = vec![Felt::ZERO; length]; + + for row in 0..length { + col0[row] = if row % 2 == 0 { Felt::ZERO } else { Felt::ONE }; + } + + vec![col0] + } +} + +impl CrossBackendTestConfig for BusesSimpleWithEvaluatorsTestConfig { + type WinterfellAir = WinterfellBusesSimpleAir; + type Plonky3Air = Plonky3BusesSimpleAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 1 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new([Felt::ZERO; 2]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellBusesSimpleAir { + WinterfellBusesSimpleAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3BusesSimpleAir { + Plonky3BusesSimpleAir + } + + fn build_plonky3_aux_trace( + &self, + _plonky3_air: &Plonky3BusesSimpleAir, + main_trace: &RowMajorMatrix, + _randomness: &[Goldilocks], + ) -> Option> { + let aux_width = crate::tests::buses::buses_simple_plonky3::AUX_WIDTH; + let num_rows = main_trace.height(); + + // TODO: Follow up to fix codegen + goldens for buses_simple_plonky3: + // AUX_WIDTH is 2 but buses_initial_values() returns a single element, + // which panics inside the generated build_aux_trace. Provide a zeroed + // aux trace here so both backends evaluate with consistent aux inputs. + Some(RowMajorMatrix::new(vec![Goldilocks::ZERO; aux_width * num_rows], aux_width)) + } +} + +#[test] +#[ignore = "TODO: Winterfell codegen uses empty main degrees/assertions; fix goldens or codegen to satisfy AirContext::new_multi_segment"] +fn test_buses_simple_with_evaluators_air_constraint_comparison() { + let config = BusesSimpleWithEvaluatorsTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "BusesSimpleWithEvaluators AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +#[ignore = "TODO: Winterfell codegen uses empty main degrees/assertions; fix goldens or codegen to satisfy AirContext::new_multi_segment"] +fn test_buses_simple_with_evaluators_air_constraint_comparison_random_inputs() { + let config = BusesSimpleWithEvaluatorsTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_buses_simple_with_evaluators_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("BusesSimpleWithEvaluators AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index cb3c028ef..5ab4e64a4 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -7,6 +7,7 @@ mod binary; mod bitwise; mod buses_complex; mod buses_simple; +mod buses_simple_with_evaluators; mod cc_with_evaluators; mod comprehension_periodic_binding; mod computed_indices_complex; From 68b39c400a05e45e8f030fc584961a93ee647674 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Sat, 31 Jan 2026 00:21:13 +0100 Subject: [PATCH 79/83] tests(compare-wf-p3): buses_varlen_boundary_first test: TODO: follow-up needed TODO: can't evaluate without constraints on winterfell --- air-script/src/tests/buses/mod.rs | 4 +- .../comparison/buses_varlen_boundary_first.rs | 117 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + 3 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/buses_varlen_boundary_first.rs diff --git a/air-script/src/tests/buses/mod.rs b/air-script/src/tests/buses/mod.rs index c7bc772af..60dca8964 100644 --- a/air-script/src/tests/buses/mod.rs +++ b/air-script/src/tests/buses/mod.rs @@ -9,7 +9,7 @@ pub mod buses_simple; mod buses_varlen_boundary_both; #[rustfmt::skip] #[allow(clippy::all)] -mod buses_varlen_boundary_first; +pub mod buses_varlen_boundary_first; #[rustfmt::skip] #[allow(clippy::all)] mod buses_varlen_boundary_last; @@ -29,7 +29,7 @@ mod buses_varlen_boundary_both_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod buses_varlen_boundary_first_plonky3; +pub mod buses_varlen_boundary_first_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] diff --git a/air-script/src/tests/comparison/buses_varlen_boundary_first.rs b/air-script/src/tests/comparison/buses_varlen_boundary_first.rs new file mode 100644 index 000000000..7a6ca2381 --- /dev/null +++ b/air-script/src/tests/comparison/buses_varlen_boundary_first.rs @@ -0,0 +1,117 @@ +//! Cross-backend comparison test for the BusesVarlenBoundaryFirst AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the BusesVarlenBoundaryFirst AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::buses::{ + buses_varlen_boundary_first::{ + BusesAir as WinterfellBusesVarlenBoundaryFirstAir, PublicInputs, + }, + buses_varlen_boundary_first_plonky3::BusesAir as Plonky3BusesVarlenBoundaryFirstAir, + }, +}; + +/// Configuration for BusesVarlenBoundaryFirst AIR cross-backend comparison tests. +struct BusesVarlenBoundaryFirstTestConfig { + /// The trace length. + trace_length: usize, +} + +impl BusesVarlenBoundaryFirstTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut col0 = vec![Felt::ZERO; length]; + + for row in 0..length { + col0[row] = if row % 2 == 0 { Felt::ZERO } else { Felt::ONE }; + } + + vec![col0] + } +} + +impl CrossBackendTestConfig for BusesVarlenBoundaryFirstTestConfig { + type WinterfellAir = WinterfellBusesVarlenBoundaryFirstAir; + type Plonky3Air = Plonky3BusesVarlenBoundaryFirstAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 1 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new(vec![[Felt::new(2), Felt::ZERO]]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellBusesVarlenBoundaryFirstAir { + WinterfellBusesVarlenBoundaryFirstAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3BusesVarlenBoundaryFirstAir { + Plonky3BusesVarlenBoundaryFirstAir + } +} + +#[test] +#[ignore = "TODO: Winterfell codegen uses empty main degrees/assertions; fix goldens or codegen to satisfy AirContext::new_multi_segment"] +fn test_buses_varlen_boundary_first_air_constraint_comparison() { + let config = BusesVarlenBoundaryFirstTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "BusesVarlenBoundaryFirst AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +#[ignore = "TODO: Winterfell codegen uses empty main degrees/assertions; fix goldens or codegen to satisfy AirContext::new_multi_segment"] +fn test_buses_varlen_boundary_first_air_constraint_comparison_random_inputs() { + let config = BusesVarlenBoundaryFirstTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_buses_varlen_boundary_first_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("BusesVarlenBoundaryFirst AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 5ab4e64a4..6a4842da6 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -8,6 +8,7 @@ mod bitwise; mod buses_complex; mod buses_simple; mod buses_simple_with_evaluators; +mod buses_varlen_boundary_first; mod cc_with_evaluators; mod comprehension_periodic_binding; mod computed_indices_complex; From e1550cdc8bf995cccb6011bc8f70c46dd6688fd7 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Sat, 31 Jan 2026 00:29:03 +0100 Subject: [PATCH 80/83] tests(compare-wf-p3): buses_varlen_boundary_both test: TODO: follow-up needed TODO: can't evaluate without constraints on winterfell --- air-script/src/tests/buses/mod.rs | 4 +- .../comparison/buses_varlen_boundary_both.rs | 120 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + 3 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/buses_varlen_boundary_both.rs diff --git a/air-script/src/tests/buses/mod.rs b/air-script/src/tests/buses/mod.rs index 60dca8964..b0c67ad1d 100644 --- a/air-script/src/tests/buses/mod.rs +++ b/air-script/src/tests/buses/mod.rs @@ -6,7 +6,7 @@ pub mod buses_complex; pub mod buses_simple; #[rustfmt::skip] #[allow(clippy::all)] -mod buses_varlen_boundary_both; +pub mod buses_varlen_boundary_both; #[rustfmt::skip] #[allow(clippy::all)] pub mod buses_varlen_boundary_first; @@ -25,7 +25,7 @@ pub mod buses_simple_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod buses_varlen_boundary_both_plonky3; +pub mod buses_varlen_boundary_both_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] diff --git a/air-script/src/tests/comparison/buses_varlen_boundary_both.rs b/air-script/src/tests/comparison/buses_varlen_boundary_both.rs new file mode 100644 index 000000000..3e96d2dca --- /dev/null +++ b/air-script/src/tests/comparison/buses_varlen_boundary_both.rs @@ -0,0 +1,120 @@ +//! Cross-backend comparison test for the BusesVarlenBoundaryBoth AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the BusesVarlenBoundaryBoth AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::buses::{ + buses_varlen_boundary_both::{ + BusesAir as WinterfellBusesVarlenBoundaryBothAir, PublicInputs, + }, + buses_varlen_boundary_both_plonky3::BusesAir as Plonky3BusesVarlenBoundaryBothAir, + }, +}; + +/// Configuration for BusesVarlenBoundaryBoth AIR cross-backend comparison tests. +struct BusesVarlenBoundaryBothTestConfig { + /// The trace length. + trace_length: usize, +} + +impl BusesVarlenBoundaryBothTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut col0 = vec![Felt::ZERO; length]; + + for row in 0..length { + col0[row] = if row % 2 == 0 { Felt::ZERO } else { Felt::ONE }; + } + + vec![col0] + } +} + +impl CrossBackendTestConfig for BusesVarlenBoundaryBothTestConfig { + type WinterfellAir = WinterfellBusesVarlenBoundaryBothAir; + type Plonky3Air = Plonky3BusesVarlenBoundaryBothAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 1 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new( + vec![[Felt::new(4), Felt::ZERO, Felt::ZERO, Felt::ZERO]], + vec![[Felt::new(2), Felt::ZERO]], + ) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellBusesVarlenBoundaryBothAir { + WinterfellBusesVarlenBoundaryBothAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3BusesVarlenBoundaryBothAir { + Plonky3BusesVarlenBoundaryBothAir + } +} + +#[test] +#[ignore = "TODO: Winterfell codegen uses empty main degrees/assertions; fix goldens or codegen to satisfy AirContext::new_multi_segment"] +fn test_buses_varlen_boundary_both_air_constraint_comparison() { + let config = BusesVarlenBoundaryBothTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "BusesVarlenBoundaryBoth AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +#[ignore = "TODO: Winterfell codegen uses empty main degrees/assertions; fix goldens or codegen to satisfy AirContext::new_multi_segment"] +fn test_buses_varlen_boundary_both_air_constraint_comparison_random_inputs() { + let config = BusesVarlenBoundaryBothTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_buses_varlen_boundary_both_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("BusesVarlenBoundaryBoth AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 6a4842da6..7931d1aca 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -8,6 +8,7 @@ mod bitwise; mod buses_complex; mod buses_simple; mod buses_simple_with_evaluators; +mod buses_varlen_boundary_both; mod buses_varlen_boundary_first; mod cc_with_evaluators; mod comprehension_periodic_binding; From e62fa0f5b3dad51d690526352198388a5bf78c00 Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Sat, 31 Jan 2026 00:31:57 +0100 Subject: [PATCH 81/83] tests(compare-wf-p3): buses_varlen_boundary_last test: TODO: follow-up needed TODO: can't evaluate without constraints on winterfell --- air-script/src/tests/buses/mod.rs | 4 +- .../comparison/buses_varlen_boundary_last.rs | 122 ++++++++++++++++++ air-script/src/tests/comparison/mod.rs | 1 + 3 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 air-script/src/tests/comparison/buses_varlen_boundary_last.rs diff --git a/air-script/src/tests/buses/mod.rs b/air-script/src/tests/buses/mod.rs index b0c67ad1d..19d9b2e4d 100644 --- a/air-script/src/tests/buses/mod.rs +++ b/air-script/src/tests/buses/mod.rs @@ -12,7 +12,7 @@ pub mod buses_varlen_boundary_both; pub mod buses_varlen_boundary_first; #[rustfmt::skip] #[allow(clippy::all)] -mod buses_varlen_boundary_last; +pub mod buses_varlen_boundary_last; #[rustfmt::skip] #[allow(clippy::all)] @@ -33,7 +33,7 @@ pub mod buses_varlen_boundary_first_plonky3; #[rustfmt::skip] #[allow(clippy::all)] #[allow(unused_imports)] -mod buses_varlen_boundary_last_plonky3; +pub mod buses_varlen_boundary_last_plonky3; mod test_air_plonky3; mod test_air_plonky3_varlen_boundary_last; diff --git a/air-script/src/tests/comparison/buses_varlen_boundary_last.rs b/air-script/src/tests/comparison/buses_varlen_boundary_last.rs new file mode 100644 index 000000000..61cebdbc8 --- /dev/null +++ b/air-script/src/tests/comparison/buses_varlen_boundary_last.rs @@ -0,0 +1,122 @@ +//! Cross-backend comparison test for the BusesVarlenBoundaryLast AIR. +//! +//! This test verifies that Winterfell and Plonky3 produce equivalent +//! constraint evaluations for the BusesVarlenBoundaryLast AIR at every row of the trace. + +use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; +use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; + +use crate::{ + test_utils::cross_backend_comparison::{CrossBackendTestConfig, TraceSource, run_comparison}, + tests::buses::{ + buses_varlen_boundary_last::{ + BusesAir as WinterfellBusesVarlenBoundaryLastAir, PublicInputs, + }, + buses_varlen_boundary_last_plonky3::BusesAir as Plonky3BusesVarlenBoundaryLastAir, + }, +}; + +/// Configuration for BusesVarlenBoundaryLast AIR cross-backend comparison tests. +struct BusesVarlenBoundaryLastTestConfig { + /// The trace length. + trace_length: usize, +} + +impl BusesVarlenBoundaryLastTestConfig { + fn new(trace_length: usize) -> Self { + Self { trace_length } + } + + fn build_trace(&self) -> Vec> { + let length = self.trace_length; + let mut trace = vec![vec![Felt::ZERO; length]; 5]; + + trace[0][0] = Felt::ONE; + for row in 1..length { + trace[0][row] = Felt::ONE; + trace[1][row] = if row > 3 && row < 8 { Felt::ONE } else { Felt::ZERO }; + trace[2][row] = if row > 3 && row < 7 { Felt::ONE } else { Felt::ZERO }; + trace[3][row] = if row > 4 && row < 10 { Felt::ONE } else { Felt::ZERO }; + trace[4][row] = if row > 5 && row < 10 { Felt::new(2) } else { Felt::ZERO }; + } + + trace + } +} + +impl CrossBackendTestConfig for BusesVarlenBoundaryLastTestConfig { + type WinterfellAir = WinterfellBusesVarlenBoundaryLastAir; + type Plonky3Air = Plonky3BusesVarlenBoundaryLastAir; + type WinterfellPublicInputs = PublicInputs; + + fn trace_width(&self) -> usize { + 5 + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn build_winterfell_trace(&self) -> Vec> { + self.build_trace() + } + + fn build_winterfell_public_inputs(&self) -> PublicInputs { + PublicInputs::new(vec![[Felt::new(2), Felt::ZERO]], vec![[Felt::new(2), Felt::ZERO]]) + } + + fn create_winterfell_air( + &self, + trace_info: TraceInfo, + pub_inputs: PublicInputs, + options: WinterProofOptions, + ) -> WinterfellBusesVarlenBoundaryLastAir { + WinterfellBusesVarlenBoundaryLastAir::new(trace_info, pub_inputs, options) + } + + fn create_plonky3_air(&self) -> Plonky3BusesVarlenBoundaryLastAir { + Plonky3BusesVarlenBoundaryLastAir + } +} + +#[test] +#[ignore = "TODO: Winterfell codegen uses empty main degrees/assertions; fix goldens or codegen to satisfy AirContext::new_multi_segment"] +fn test_buses_varlen_boundary_last_air_constraint_comparison() { + let config = BusesVarlenBoundaryLastTestConfig::new(64); + let result = run_comparison(&config, TraceSource::Default); + + if !result.is_ok() { + panic!("Constraint evaluation comparison failed!\n\n{}", result.format_report()); + } + + println!( + "BusesVarlenBoundaryLast AIR comparison passed: {} constraints checked across {} rows", + result.total_constraints_checked, result.total_rows + ); +} + +#[test] +#[ignore = "TODO: Winterfell codegen uses empty main degrees/assertions; fix goldens or codegen to satisfy AirContext::new_multi_segment"] +fn test_buses_varlen_boundary_last_air_constraint_comparison_random_inputs() { + let config = BusesVarlenBoundaryLastTestConfig::new(64); + + for iteration in 0u64..=50 { + let result = run_comparison( + &config, + TraceSource::Random { + test_name: "test_buses_varlen_boundary_last_air_constraint_comparison_random_inputs", + iteration, + }, + ); + + if !result.is_ok() { + panic!( + "Random constraint evaluation comparison failed (iteration={})!\n\n{}", + iteration, + result.format_report() + ); + } + } + + println!("BusesVarlenBoundaryLast AIR random comparison passed for all 51 iterations (0-50)"); +} diff --git a/air-script/src/tests/comparison/mod.rs b/air-script/src/tests/comparison/mod.rs index 7931d1aca..7baf2c8e7 100644 --- a/air-script/src/tests/comparison/mod.rs +++ b/air-script/src/tests/comparison/mod.rs @@ -10,6 +10,7 @@ mod buses_simple; mod buses_simple_with_evaluators; mod buses_varlen_boundary_both; mod buses_varlen_boundary_first; +mod buses_varlen_boundary_last; mod cc_with_evaluators; mod comprehension_periodic_binding; mod computed_indices_complex; From df6f4051375bddffcb4d36d45e8ff551d2e58d2b Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Mon, 2 Feb 2026 14:32:52 +0100 Subject: [PATCH 82/83] chores(compare-wf-p3): cargo fmt --- air-script/src/test_utils/cross_backend_comparison.rs | 4 ++-- .../comparison/selectors_combine_with_list_comprehensions.rs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/air-script/src/test_utils/cross_backend_comparison.rs b/air-script/src/test_utils/cross_backend_comparison.rs index d4b5fffa1..8ed90aed1 100644 --- a/air-script/src/test_utils/cross_backend_comparison.rs +++ b/air-script/src/test_utils/cross_backend_comparison.rs @@ -1105,8 +1105,8 @@ where // Combine: to follow the order of emitted constraints in Plonky3, we inject: // - main boundary constraints - // - aux boundary constraints (Winterfell-only: Plonky3 eval does not emit - // explicit aux boundary assertions; bus boundary values are handled outside eval) + // - aux boundary constraints (Winterfell-only: Plonky3 eval does not emit explicit aux + // boundary assertions; bus boundary values are handled outside eval) // - main transition constraints // - aux transition constraints // TODO: Best guess for now: Plonky3 eval does not emit aux boundary assertions, diff --git a/air-script/src/tests/comparison/selectors_combine_with_list_comprehensions.rs b/air-script/src/tests/comparison/selectors_combine_with_list_comprehensions.rs index 9e5733541..ed977e582 100644 --- a/air-script/src/tests/comparison/selectors_combine_with_list_comprehensions.rs +++ b/air-script/src/tests/comparison/selectors_combine_with_list_comprehensions.rs @@ -1,7 +1,8 @@ //! Cross-backend comparison test for the SelectorsCombineWithListComprehensions AIR. //! //! This test verifies that Winterfell and Plonky3 produce equivalent -//! constraint evaluations for the SelectorsCombineWithListComprehensions AIR at every row of the trace. +//! constraint evaluations for the SelectorsCombineWithListComprehensions AIR at every row of the +//! trace. use winter_air::{Air, ProofOptions as WinterProofOptions, TraceInfo}; use winter_math::{FieldElement, fields::f64::BaseElement as Felt}; From 5ebcb50f5ae03f04467236fed8a8bf98361ba85c Mon Sep 17 00:00:00 2001 From: Thybault Alabarbe Date: Mon, 2 Feb 2026 14:35:40 +0100 Subject: [PATCH 83/83] chores: update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7a140b8e..3508e7f3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - In Plonky3 codegen, use MidenAir and MidenAirBuilder from 0xMiden's Plonky3 fork instead of AirScriptAir and AirScriptBuilder (#515). - In Plonky3 codegen, use prove/verify workflow for tests (#523). - Fix MIR inlining loop on deeply nested calls (#524). +- Compare evaluations between Winterfell and Plonky3 codegen (#533). ## 0.4.0 (2025-06-20)