From f0af0c18c30ec82bfe2858929205011c7870bc48 Mon Sep 17 00:00:00 2001 From: Georg Wiese Date: Tue, 2 Jul 2024 15:02:59 +0200 Subject: [PATCH] Remove all top level declarations (#1512) Includes #1511, but also removes the declarations of the challenges in the `permutation` and `lookup` modules. With these changes, we never declare a column or challenge outside a machine namespace. This greatly simplifies #1470. --------- Co-authored-by: schaeff Co-authored-by: chriseth --- pipeline/tests/powdr_std.rs | 2 +- std/mod.asm | 1 + std/protocols/lookup.asm | 28 +++------------- std/protocols/permutation.asm | 32 ++----------------- std/well_known.asm | 1 + test_data/asm/second_phase_hint.asm | 2 +- test_data/std/lookup_via_challenges.asm | 9 +++++- test_data/std/lookup_via_challenges_ext.asm | 15 +++++++-- .../std/lookup_via_challenges_ext_simple.asm | 16 ++++++++-- test_data/std/permutation_via_challenges.asm | 9 +++++- .../std/permutation_via_challenges_ext.asm | 15 +++++++-- 11 files changed, 68 insertions(+), 62 deletions(-) create mode 100644 std/well_known.asm diff --git a/pipeline/tests/powdr_std.rs b/pipeline/tests/powdr_std.rs index f7e54b8c4..21c487ae3 100644 --- a/pipeline/tests/powdr_std.rs +++ b/pipeline/tests/powdr_std.rs @@ -93,7 +93,7 @@ fn permutation_via_challenges_bn() { } #[test] -#[should_panic = "Error reducing expression to constraint:\nExpression: std::protocols::permutation::permutation([main.z], main.permutation_constraint)\nError: FailedAssertion(\"The Goldilocks field is too small and needs to move to the extension field. Pass two accumulators instead!\")"] +#[should_panic = "Error reducing expression to constraint:\nExpression: std::protocols::permutation::permutation(main.is_first, [main.z], main.alpha, main.beta, main.permutation_constraint)\nError: FailedAssertion(\"The Goldilocks field is too small and needs to move to the extension field. Pass two accumulators instead!\""] fn permutation_via_challenges_gl() { let f = "std/permutation_via_challenges.asm"; Pipeline::::default() diff --git a/std/mod.asm b/std/mod.asm index 61151c6dd..26a295822 100644 --- a/std/mod.asm +++ b/std/mod.asm @@ -10,3 +10,4 @@ mod prelude; mod protocols; mod prover; mod utils; +mod well_known; diff --git a/std/protocols/lookup.asm b/std/protocols/lookup.asm index 68ca77404..069f03736 100644 --- a/std/protocols/lookup.asm +++ b/std/protocols/lookup.asm @@ -1,4 +1,3 @@ -use std::prover::challenge; use std::array::fold; use std::utils::unwrap_or_else; use std::array::len; @@ -18,16 +17,6 @@ use std::math::fp2::eval_ext; use std::math::fp2::from_base; use std::math::fp2::constrain_eq_ext; - -let is_first: col = |i| if i == 0 { 1 } else { 0 }; - -// challenges to be used in polynomial evaluation and folding different columns -let alpha1: expr = challenge(0, 1); -let alpha2: expr = challenge(0, 2); - -let beta1: expr = challenge(0, 3); -let beta2: expr = challenge(0, 4); - let unpack_lookup_constraint: Constr -> (expr, expr[], expr, expr[]) = |lookup_constraint| match lookup_constraint { Constr::Lookup((lhs_selector, rhs_selector), values) => ( unwrap_or_else(lhs_selector, || 1), @@ -54,16 +43,8 @@ let compress_expression_array: T[], Fp2 -> Fp2 ); // Compute z' = z + 1/(beta-a_i) * lhs_selector - m_i/(beta-b_i) * rhs_selector, using extension field arithmetic -let compute_next_z: Fp2, Constr, expr -> fe[] = query |acc, lookup_constraint, multiplicities| { +let compute_next_z: Fp2, Fp2, Fp2, Constr, expr -> fe[] = query |acc, alpha, beta, lookup_constraint, multiplicities| { let (lhs_selector, lhs, rhs_selector, rhs) = unpack_lookup_constraint(lookup_constraint); - let alpha = if len(lhs) > 1 { - Fp2::Fp2(alpha1, alpha2) - } else { - // The optimizer will have removed alpha, but the compression function - // still accesses it (to multiply by 0 in this case) - from_base(0) - }; - let beta = Fp2::Fp2(beta1, beta2); let lhs_denom = sub_ext(beta, compress_expression_array(lhs, alpha)); let rhs_denom = sub_ext(beta, compress_expression_array(rhs, alpha)); @@ -88,11 +69,14 @@ let compute_next_z: Fp2, Constr, expr -> fe[] = query |acc, lookup_constra // Adds constraints that enforce that rhs is the lookup for lhs // Arguments: +// - is_first: A column that is 1 for the first row and 0 for the rest +// - alpha: A challenge used to compress the LHS and RHS values +// - beta: A challenge used to update the accumulator // - acc: A phase-2 witness column to be used as the accumulator. If 2 are provided, computations // are done on the F_{p^2} extension field. // - lookup_constraint: The lookup constraint // - multiplicities: The multiplicities which shows how many times each RHS value appears in the LHS -let lookup: expr[], Constr, expr -> Constr[] = |acc, lookup_constraint, multiplicities| { +let lookup: expr, expr[], Fp2, Fp2, Constr, expr -> Constr[] = |is_first, acc, alpha, beta, lookup_constraint, multiplicities| { let (lhs_selector, lhs, rhs_selector, rhs) = unpack_lookup_constraint(lookup_constraint); @@ -113,8 +97,6 @@ let lookup: expr[], Constr, expr -> Constr[] = |acc, lookup_constraint, multipli // in which case the operations below effectively only operate on the first component. let fp2_from_array = |arr| if with_extension { Fp2::Fp2(arr[0], arr[1]) } else { from_base(arr[0]) }; let acc_ext = fp2_from_array(acc); - let alpha = fp2_from_array([alpha1, alpha2]); - let beta = fp2_from_array([beta1, beta2]); let lhs_denom = sub_ext(beta, compress_expression_array(lhs, alpha)); let rhs_denom = sub_ext(beta, compress_expression_array(rhs, alpha)); diff --git a/std/protocols/permutation.asm b/std/protocols/permutation.asm index 8a20be57b..86159d70c 100644 --- a/std/protocols/permutation.asm +++ b/std/protocols/permutation.asm @@ -1,4 +1,3 @@ -use std::prover::challenge; use std::array::fold; use std::array::map; use std::utils::unwrap_or_else; @@ -18,20 +17,6 @@ use std::math::fp2::eval_ext; use std::math::fp2::from_base; use std::math::fp2::constrain_eq_ext; -let is_first: col = |i| if i == 0 { 1 } else { 0 }; - -/// Get two phase-2 challenges to use in all permutation arguments. -/// Note that this assumes that globally no other challenge of these IDs is used, -/// and that challenges for multiple permutation arguments are re-used. -/// We declare two components for each challenge here, in case we need to operate -/// on the extension field. If we don't, we won't end up needing it and the optimizer -/// will remove it. -let alpha1: expr = challenge(0, 1); -let alpha2: expr = challenge(0, 2); - -let beta1: expr = challenge(0, 3); -let beta2: expr = challenge(0, 4); - let unpack_permutation_constraint: Constr -> (expr, expr[], expr, expr[]) = |permutation_constraint| match permutation_constraint { Constr::Permutation((lhs_selector, rhs_selector), values) => ( unwrap_or_else(lhs_selector, || 1), @@ -65,18 +50,9 @@ let selected_or_one: T, Fp2 -> Fp2 = |se /// the provided permutation constraint). /// This is intended to be used as a hint in the extension field case; for the base case /// automatic witgen is smart enough to figure out the value of the accumulator. -let compute_next_z: Fp2, Constr -> fe[] = query |acc, permutation_constraint| { +let compute_next_z: Fp2, Fp2, Fp2, Constr -> fe[] = query |acc, alpha, beta, permutation_constraint| { let (lhs_selector, lhs, rhs_selector, rhs) = unpack_permutation_constraint(permutation_constraint); - - let alpha = if len(lhs) > 1 { - Fp2::Fp2(alpha1, alpha2) - } else { - // The optimizer will have removed alpha, but the compression function - // still accesses it (to multiply by 0 in this case) - from_base(0) - }; - let beta = Fp2::Fp2(beta1, beta2); let lhs_folded = selected_or_one(lhs_selector, sub_ext(beta, compress_expression_array(lhs, alpha))); let rhs_folded = selected_or_one(rhs_selector, sub_ext(beta, compress_expression_array(rhs, alpha))); @@ -117,7 +93,7 @@ let compute_next_z: Fp2, Constr -> fe[] = query |acc, permutation_constrai /// the wrapping behavior: The first accumulator is constrained to be 1, and the last /// accumulator is the same as the first one, because of wrapping. /// For small fields, this computation should happen in the extension field. -let permutation: expr[], Constr -> Constr[] = |acc, permutation_constraint| { +let permutation: expr, expr[], Fp2, Fp2, Constr -> Constr[] = |is_first, acc, alpha, beta, permutation_constraint| { let (lhs_selector, lhs, rhs_selector, rhs) = unpack_permutation_constraint(permutation_constraint); @@ -131,14 +107,12 @@ let permutation: expr[], Constr -> Constr[] = |acc, permutation_constraint| { let _ = if !with_extension { assert(!needs_extension(), || "The Goldilocks field is too small and needs to move to the extension field. Pass two accumulators instead!") } else { () }; - + // On the extension field, we'll need two field elements to represent the challenge. // If we don't need an extension field, we can simply set the second component to 0, // in which case the operations below effectively only operate on the first component. let fp2_from_array = |arr| if with_extension { Fp2::Fp2(arr[0], arr[1]) } else { from_base(arr[0]) }; let acc_ext = fp2_from_array(acc); - let alpha = fp2_from_array([alpha1, alpha2]); - let beta = fp2_from_array([beta1, beta2]); // If the selector is 1, contribute a factor of `beta - compress_expression_array(lhs)` to accumulator. // If the selector is 0, contribute a factor of 1 to the accumulator. diff --git a/std/well_known.asm b/std/well_known.asm new file mode 100644 index 000000000..3ce7ac0c3 --- /dev/null +++ b/std/well_known.asm @@ -0,0 +1 @@ +let is_first: int -> int = |i| if i == 0 { 1 } else { 0 }; \ No newline at end of file diff --git a/test_data/asm/second_phase_hint.asm b/test_data/asm/second_phase_hint.asm index cc0d6c21c..06c2cad7d 100644 --- a/test_data/asm/second_phase_hint.asm +++ b/test_data/asm/second_phase_hint.asm @@ -3,7 +3,7 @@ use std::prover::eval; use std::prover::challenge; machine Main with degree: 8 { - col fixed is_first = [1] + [0]*; + let is_first: col = std::well_known::is_first; let count; diff --git a/test_data/std/lookup_via_challenges.asm b/test_data/std/lookup_via_challenges.asm index 59ba20b32..6ea16cf59 100644 --- a/test_data/std/lookup_via_challenges.asm +++ b/test_data/std/lookup_via_challenges.asm @@ -1,8 +1,14 @@ use std::prover::Query; use std::convert::fe; use std::protocols::lookup::lookup; +use std::math::fp2::from_base; +use std::prover::challenge; machine Main with degree: 8 { + + let alpha = from_base(challenge(0, 1)); + let beta = from_base(challenge(0, 2)); + col fixed random_six = [1, 1, 1, 0, 1, 1, 1, 0]; col fixed first_seven = [1, 1, 1, 1, 1, 1, 1, 0]; @@ -22,6 +28,7 @@ machine Main with degree: 8 { // TODO: Functions currently cannot add witness columns at later stages, // so we have to manually create it here and pass it to permutation(). col witness stage(1) z; - lookup([z], lookup_constraint, m); + let is_first: col = std::well_known::is_first; + lookup(is_first, [z], alpha, beta, lookup_constraint, m); } \ No newline at end of file diff --git a/test_data/std/lookup_via_challenges_ext.asm b/test_data/std/lookup_via_challenges_ext.asm index 01b24cfeb..f55378d1c 100644 --- a/test_data/std/lookup_via_challenges_ext.asm +++ b/test_data/std/lookup_via_challenges_ext.asm @@ -3,8 +3,17 @@ use std::convert::fe; use std::protocols::lookup::lookup; use std::protocols::lookup::compute_next_z; use std::math::fp2::Fp2; +use std::prover::challenge; machine Main with degree: 8 { + + let alpha1: expr = challenge(0, 1); + let alpha2: expr = challenge(0, 2); + let beta1: expr = challenge(0, 3); + let beta2: expr = challenge(0, 4); + let alpha = Fp2::Fp2(alpha1, alpha2); + let beta = Fp2::Fp2(beta1, beta2); + col fixed a_sel = [0, 1, 1, 1, 0, 1, 0, 0]; col fixed b_sel = [1, 1, 0, 1, 1, 1, 1, 0]; @@ -25,11 +34,13 @@ machine Main with degree: 8 { // so we have to manually create it here and pass it to lookup(). col witness stage(1) z1; col witness stage(1) z2; + let z = Fp2::Fp2(z1, z2); - lookup([z1, z2], lookup_constraint, m); + let is_first: col = std::well_known::is_first; + lookup(is_first, [z1, z2], alpha, beta, lookup_constraint, m); // TODO: Helper columns, because we can't access the previous row in hints - let hint = query |i| Query::Hint(compute_next_z(Fp2::Fp2(z1, z2), lookup_constraint, m)[i]); + let hint = query |i| Query::Hint(compute_next_z(z, alpha, beta, lookup_constraint, m)[i]); col witness stage(1) z1_next(i) query hint(0); col witness stage(1) z2_next(i) query hint(1); diff --git a/test_data/std/lookup_via_challenges_ext_simple.asm b/test_data/std/lookup_via_challenges_ext_simple.asm index 033d62213..c0e35c7a5 100644 --- a/test_data/std/lookup_via_challenges_ext_simple.asm +++ b/test_data/std/lookup_via_challenges_ext_simple.asm @@ -3,8 +3,18 @@ use std::convert::fe; use std::protocols::lookup::lookup; use std::protocols::lookup::compute_next_z; use std::math::fp2::Fp2; +use std::prover::challenge; machine Main with degree: 8 { + + // We don't need an alpha here, because we only "fold" one element. + // Therefore, the optimizer will remove it, but the hint still accesses it... + let alpha = Fp2::Fp2(0, 0); + + let beta1: expr = challenge(0, 3); + let beta2: expr = challenge(0, 4); + let beta = Fp2::Fp2(beta1, beta2); + col fixed a = [1, 1, 4, 1, 1, 2, 1, 1]; col witness b(i) query Query::Hint(fe(i+1)); col fixed m = [6, 1, 0, 1, 0, 0, 0, 0]; @@ -18,11 +28,13 @@ machine Main with degree: 8 { // so we have to manually create it here and pass it to lookup(). col witness stage(1) z1; col witness stage(1) z2; + let z = Fp2::Fp2(z1, z2); - lookup([z1, z2], lookup_constraint, m); + let is_first: col = std::well_known::is_first; + lookup(is_first, [z1, z2], alpha, beta, lookup_constraint, m); // TODO: Helper columns, because we can't access the previous row in hints - let hint = query |i| Query::Hint(compute_next_z(Fp2::Fp2(z1, z2), lookup_constraint, m)[i]); + let hint = query |i| Query::Hint(compute_next_z(z, alpha, beta, lookup_constraint, m)[i]); col witness stage(1) z1_next(i) query hint(0); col witness stage(1) z2_next(i) query hint(1); diff --git a/test_data/std/permutation_via_challenges.asm b/test_data/std/permutation_via_challenges.asm index 75df8ff93..3a08d3a9f 100644 --- a/test_data/std/permutation_via_challenges.asm +++ b/test_data/std/permutation_via_challenges.asm @@ -1,8 +1,14 @@ use std::prover::Query; use std::convert::fe; use std::protocols::permutation::permutation; +use std::math::fp2::from_base; +use std::prover::challenge; machine Main with degree: 8 { + + let alpha = from_base(challenge(0, 1)); + let beta = from_base(challenge(0, 2)); + col fixed first_four = [1, 1, 1, 1, 0, 0, 0, 0]; // Two pairs of witness columns, claimed to be permutations of one another @@ -20,6 +26,7 @@ machine Main with degree: 8 { // TODO: Functions currently cannot add witness columns at later stages, // so we have to manually create it here and pass it to permutation(). col witness stage(1) z; - permutation([z], permutation_constraint); + let is_first: col = std::well_known::is_first; + permutation(is_first, [z], alpha, beta, permutation_constraint); } diff --git a/test_data/std/permutation_via_challenges_ext.asm b/test_data/std/permutation_via_challenges_ext.asm index a0ea6b822..d398a37e8 100644 --- a/test_data/std/permutation_via_challenges_ext.asm +++ b/test_data/std/permutation_via_challenges_ext.asm @@ -3,8 +3,17 @@ use std::convert::fe; use std::protocols::permutation::permutation; use std::protocols::permutation::compute_next_z; use std::math::fp2::Fp2; +use std::prover::challenge; machine Main with degree: 8 { + + let alpha1: expr = challenge(0, 1); + let alpha2: expr = challenge(0, 2); + let beta1: expr = challenge(0, 3); + let beta2: expr = challenge(0, 4); + let alpha = Fp2::Fp2(alpha1, alpha2); + let beta = Fp2::Fp2(beta1, beta2); + col fixed first_four = [1, 1, 1, 1, 0, 0, 0, 0]; // Two pairs of witness columns, claimed to be permutations of one another @@ -23,10 +32,12 @@ machine Main with degree: 8 { // so we have to manually create it here and pass it to permutation(). col witness stage(1) z1; col witness stage(1) z2; - permutation([z1, z2], permutation_constraint); + let z = Fp2::Fp2(z1, z2); + let is_first: col = std::well_known::is_first; + permutation(is_first, [z1, z2], alpha, beta, permutation_constraint); // TODO: Helper columns, because we can't access the previous row in hints - let hint = query |i| Query::Hint(compute_next_z(Fp2::Fp2(z1, z2), permutation_constraint)[i]); + let hint = query |i| Query::Hint(compute_next_z(z, alpha, beta, permutation_constraint)[i]); col witness stage(1) z1_next(i) query hint(0); col witness stage(1) z2_next(i) query hint(1);