Skip to content

Commit c20b355

Browse files
authored
Simplify permutation using prover functions similiar to lookups (#1826)
Similar to #1802, we can also simplify permutation in PIL using prover functions.
1 parent d0d5170 commit c20b355

File tree

4 files changed

+33
-47
lines changed

4 files changed

+33
-47
lines changed

pipeline/tests/powdr_std.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ fn permutation_via_challenges_bn() {
119119
}
120120

121121
#[test]
122-
#[should_panic = "Error reducing expression to constraint:\nExpression: std::protocols::permutation::permutation([main::z], main::alpha, main::beta, main::permutation_constraint)\nError: FailedAssertion(\"The field is too small and needs to move to the extension field. Pass two elements instead!\")"]
123122
fn permutation_via_challenges_gl() {
124123
let f = "std/permutation_via_challenges.asm";
125124
make_simple_prepared_pipeline::<GoldilocksField>(f);

std/protocols/permutation.asm

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::array::map;
2-
use std::array::len;
32
use std::check::assert;
43
use std::check::panic;
54
use std::math::fp2::Fp2;
@@ -14,6 +13,8 @@ use std::math::fp2::eval_ext;
1413
use std::math::fp2::from_base;
1514
use std::math::fp2::fp2_from_array;
1615
use std::math::fp2::constrain_eq_ext;
16+
use std::math::fp2::required_extension_size;
17+
use std::math::fp2::needs_extension;
1718
use std::protocols::fingerprint::fingerprint;
1819
use std::utils::unwrap_or_else;
1920

@@ -53,16 +54,9 @@ let compute_next_z: Fp2<expr>, Fp2<expr>, Fp2<expr>, Constr -> fe[] = query |acc
5354
};
5455

5556
/// Returns constraints that enforce that lhs is a permutation of rhs
56-
///
57-
/// # Arguments:
58-
/// - acc: A phase-2 witness column to be used as the accumulator. If 2 are provided, computations
59-
/// are done on the F_{p^2} extension field.
60-
/// - alpha: A challenge used to compress the LHS and RHS values
61-
/// - beta: A challenge used to update the accumulator
62-
/// - permutation_constraint: The permutation constraint
63-
///
64-
/// # Returns:
65-
/// - Constraints to be added to enforce the permutation
57+
/// WARNING: This function can currently not be used multiple times since
58+
/// the used challenges would overlap
59+
/// TODO: Implement this for an array of constraints
6660
///
6761
/// # Implementation:
6862
/// This function implements a permutation argument described e.g. in
@@ -79,7 +73,12 @@ let compute_next_z: Fp2<expr>, Fp2<expr>, Fp2<expr>, Constr -> fe[] = query |acc
7973
/// the wrapping behavior: The first accumulator is constrained to be 1, and the last
8074
/// accumulator is the same as the first one, because of wrapping.
8175
/// For small fields, this computation should happen in the extension field.
82-
let permutation: expr[], Fp2<expr>, Fp2<expr>, Constr -> () = constr |acc, alpha, beta, permutation_constraint| {
76+
let permutation: Constr -> () = constr |permutation_constraint| {
77+
std::check::assert(required_extension_size() <= 2, || "Invalid extension size");
78+
// Alpha is used to compress the LHS and RHS arrays
79+
let alpha = fp2_from_array(std::array::new(required_extension_size(), |i| challenge(0, i + 1)));
80+
// Beta is used to update the accumulator
81+
let beta = fp2_from_array(std::array::new(required_extension_size(), |i| challenge(0, i + 3)));
8382

8483
let (lhs_selector, lhs, rhs_selector, rhs) = unpack_permutation_constraint(permutation_constraint);
8584

@@ -88,6 +87,8 @@ let permutation: expr[], Fp2<expr>, Fp2<expr>, Constr -> () = constr |acc, alpha
8887
// Implemented as: folded = selector * (beta - fingerprint(values) - 1) + 1;
8988
let lhs_folded = selected_or_one(lhs_selector, sub_ext(beta, fingerprint(lhs, alpha)));
9089
let rhs_folded = selected_or_one(rhs_selector, sub_ext(beta, fingerprint(rhs, alpha)));
90+
91+
let acc = std::array::new(required_extension_size(), |i| std::prover::new_witness_col_at_stage("acc", 1));
9192
let acc_ext = fp2_from_array(acc);
9293
let next_acc = next_ext(acc_ext);
9394

@@ -108,4 +109,21 @@ let permutation: expr[], Fp2<expr>, Fp2<expr>, Constr -> () = constr |acc, alpha
108109
is_first * (acc_1 - 1) = 0;
109110
is_first * acc_2 = 0;
110111
constrain_eq_ext(update_expr, from_base(0));
112+
113+
// In the extension field, we need a prover function for the accumulator.
114+
if needs_extension() {
115+
// TODO: Helper columns, because we can't access the previous row in hints
116+
let acc_next_col = std::array::map(acc, |_| std::prover::new_witness_col_at_stage("acc_next", 1));
117+
query |i| {
118+
let _ = std::array::zip(
119+
acc_next_col,
120+
compute_next_z(acc_ext, alpha, beta, permutation_constraint),
121+
|acc_next, hint_val| std::prover::provide_value(acc_next, i, hint_val)
122+
);
123+
};
124+
std::array::zip(acc, acc_next_col, |acc_col, acc_next| {
125+
acc_col' = acc_next
126+
});
127+
} else {
128+
}
111129
};

test_data/std/permutation_via_challenges.asm

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ use std::prover::challenge;
66

77
machine Main with degree: 8 {
88

9-
let alpha = from_base(challenge(0, 1));
10-
let beta = from_base(challenge(0, 2));
11-
129
col fixed first_four = [1, 1, 1, 1, 0, 0, 0, 0];
1310

1411
// Two pairs of witness columns, claimed to be permutations of one another
@@ -23,8 +20,5 @@ machine Main with degree: 8 {
2320

2421
let permutation_constraint = first_four $ [a1, a2] is (1 - first_four) $ [b1, b2];
2522

26-
// TODO: Functions currently cannot add witness columns at later stages,
27-
// so we have to manually create it here and pass it to permutation().
28-
col witness stage(1) z;
29-
permutation([z], alpha, beta, permutation_constraint);
23+
permutation(permutation_constraint);
3024
}

test_data/std/permutation_via_challenges_ext.asm

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,6 @@ use std::prover::challenge;
77

88
machine Main with degree: 8 {
99

10-
let alpha1: expr = challenge(0, 1);
11-
let alpha2: expr = challenge(0, 2);
12-
let beta1: expr = challenge(0, 3);
13-
let beta2: expr = challenge(0, 4);
14-
let alpha = Fp2::Fp2(alpha1, alpha2);
15-
let beta = Fp2::Fp2(beta1, beta2);
16-
1710
col fixed first_four = [1, 1, 1, 1, 0, 0, 0, 0];
1811

1912
// Two pairs of witness columns, claimed to be permutations of one another
@@ -27,24 +20,6 @@ machine Main with degree: 8 {
2720
};
2821

2922
let permutation_constraint = first_four $ [a1, a2] is (1 - first_four) $ [b1, b2];
30-
31-
// TODO: Functions currently cannot add witness columns at later stages,
32-
// so we have to manually create it here and pass it to permutation().
33-
col witness stage(1) z1;
34-
col witness stage(1) z2;
35-
let z = Fp2::Fp2(z1, z2);
36-
37-
permutation([z1, z2], alpha, beta, permutation_constraint);
38-
39-
// TODO: Helper columns, because we can't access the previous row in hints
40-
col witness stage(1) z1_next;
41-
col witness stage(1) z2_next;
42-
query |i| {
43-
let hint = compute_next_z(z, alpha, beta, permutation_constraint);
44-
std::prover::provide_value(z1_next, i, hint[0]);
45-
std::prover::provide_value(z2_next, i, hint[1]);
46-
};
47-
48-
z1' = z1_next;
49-
z2' = z2_next;
23+
24+
permutation(permutation_constraint);
5025
}

0 commit comments

Comments
 (0)