Skip to content

Commit

Permalink
Bus: Remove acc_next + materialize folded tuple (#2201)
Browse files Browse the repository at this point in the history
This PR:
- Removes the `acc_next` columns, which were only needed because of a
limitation of prover functions. The prover function that existed is now
removed entirely, because we use the hand written witgen anyway, see
#2191.
- Also this PR materializes the folded tuple. This lowers the degree of
the constraints if the tuples being sent have a degree > 1. It also
enables next references in the tuple being sent.

As a result, we can now generate Plonky3 proofs with a bus!

```bash
cargo run -r --features plonky3 --bin powdr-rs compile riscv/tests/riscv_data/keccak -o output --max-degree-log 18 --field gl
cargo run -r --features plonky3 pil output/keccak.asm -o output -f --field gl --prove-with plonky3 --linker-mode bus
```

The proof generation takes 8.32s (of which 394ms are spent on generating
the second-stage witness). This compares to 2.07s proof time without a
bus.
  • Loading branch information
georgwiese authored Dec 9, 2024
1 parent 576bdd7 commit e286da4
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 47 deletions.
45 changes: 16 additions & 29 deletions executor/src/witgen/bus_accumulator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,7 @@ impl<'a, T: FieldElement> BusAccumulatorGenerator<'a, T> {
let accumulators = self
.bus_interactions
.par_iter()
.flat_map(|bus_interaction| {
let (acc1, acc2) = self.interaction_columns(bus_interaction);
let next1 = next(&acc1);
let next2 = next(&acc2);

// We assume that the second-stage witness columns are in this order,
// for each bus interaction.
[acc1, acc2, next1, next2]
})
.flat_map(|bus_interaction| self.interaction_columns(bus_interaction))
.collect::<Vec<_>>();

self.pil
Expand All @@ -107,11 +99,13 @@ impl<'a, T: FieldElement> BusAccumulatorGenerator<'a, T> {
fn interaction_columns(
&self,
bus_interaction: &PhantomBusInteractionIdentity<T>,
) -> (Vec<T>, Vec<T>) {
) -> Vec<Vec<T>> {
let intermediate_definitions = self.pil.intermediate_definitions();
let empty_challenges = BTreeMap::new();

let size = self.trace_values.height();
let mut folded1 = vec![T::zero(); size];
let mut folded2 = vec![T::zero(); size];
let mut acc1 = vec![T::zero(); size];
let mut acc2 = vec![T::zero(); size];

Expand All @@ -128,26 +122,26 @@ impl<'a, T: FieldElement> BusAccumulatorGenerator<'a, T> {
};
let multiplicity = evaluator.evaluate(&bus_interaction.multiplicity);

let tuple = bus_interaction
.tuple
.0
.iter()
.map(|r| evaluator.evaluate(r))
.collect::<Vec<_>>();
let folded = self.beta - self.fingerprint(&tuple);

let new_acc = match multiplicity.is_zero() {
true => current_acc,
false => {
let tuple = bus_interaction
.tuple
.0
.iter()
.map(|r| evaluator.evaluate(r))
.collect::<Vec<_>>();

let fingerprint = self.beta - self.fingerprint(&tuple);
current_acc + fingerprint.inverse() * multiplicity
}
false => current_acc + folded.inverse() * multiplicity,
};

folded1[i] = folded.0;
folded2[i] = folded.1;
acc1[i] = new_acc.0;
acc2[i] = new_acc.1;
}

(acc1, acc2)
vec![folded1, folded2, acc1, acc2]
}

/// Fingerprints a tuples of field elements, using the pre-computed powers of alpha.
Expand All @@ -170,10 +164,3 @@ fn powers_of_alpha<T: FieldElement>(alpha: Fp2<T>, n: usize) -> Vec<Fp2<T>> {
})
.collect::<Vec<_>>()
}

/// Rotates a column to the left.
fn next<T: Clone>(column: &[T]) -> Vec<T> {
let mut result = column.to_vec();
result.rotate_left(1);
result
}
44 changes: 26 additions & 18 deletions std/protocols/bus.asm
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ use std::protocols::fingerprint::fingerprint_with_id;
use std::protocols::fingerprint::fingerprint_with_id_inter;
use std::math::fp2::required_extension_size;
use std::prover::eval;
use std::field::known_field;
use std::field::KnownField;
use std::check::panic;

/// Sends the tuple (id, tuple...) to the bus by adding
/// `multiplicity / (beta - fingerprint(id, tuple...))` to `acc`
Expand All @@ -40,7 +43,29 @@ let bus_interaction: expr, expr[], expr -> () = constr |id, tuple, multiplicity|
let beta = fp2_from_array(array::new(required_extension_size(), |i| challenge(0, i + 3)));

// Implemented as: folded = (beta - fingerprint(id, tuple...));
let folded = sub_ext(beta, fingerprint_with_id_inter(id, tuple, alpha));
let folded = match known_field() {
Option::Some(KnownField::Goldilocks) => {
// Materialized as a witness column for two reasons:
// - It makes sure the constraint degree is independent of the input tuple.
// - We can access folded', even if the tuple contains next references.
// Note that if all expressions are degree-1 and there is no next reference,
// this is wasteful, but we can't check that here.
let folded = fp2_from_array(
array::new(required_extension_size(),
|i| std::prover::new_witness_col_at_stage("folded", 1))
);
constrain_eq_ext(folded, sub_ext(beta, fingerprint_with_id_inter(id, tuple, alpha)));
folded
},
// The case above triggers our hand-written witness generation, but on Bn254, we'd not be
// on the extension field and use the automatic witness generation.
// However, it does not work with a materialized folded tuple. At the same time, Halo2
// (the only prover that supports BN254) does not have a hard degree bound. So, we can
// in-line the expression here.
Option::Some(KnownField::BN254) => sub_ext(beta, fingerprint_with_id_inter(id, tuple, alpha)),
_ => panic("Unexpected field!")
};

let folded_next = next_ext(folded);

let m_ext = from_base(multiplicity);
Expand All @@ -62,23 +87,6 @@ let bus_interaction: expr, expr[], expr -> () = constr |id, tuple, multiplicity|
);

constrain_eq_ext(update_expr, from_base(0));

// In the extension field, we need a prover function for the accumulator.
if needs_extension() {
// TODO: Helper columns, because we can't access the previous row in hints
let acc_next_col = std::array::map(acc, |_| std::prover::new_witness_col_at_stage("acc_next", 1));
query |i| {
let _ = std::array::zip(
acc_next_col,
compute_next_z(is_first, id, tuple, multiplicity, acc_ext, alpha, beta),
|acc_next, hint_val| std::prover::provide_value(acc_next, i, hint_val)
);
};
std::array::zip(acc, acc_next_col, |acc_col, acc_next| {
acc_col' = acc_next
});
} else {
}
};

/// Compute acc' = acc * (1 - is_first') + multiplicity' / fingerprint(id, tuple...),
Expand Down

0 comments on commit e286da4

Please sign in to comment.