Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
batch_inversion.hpp
Go to the documentation of this file.
1#pragma once
2
6
7#include <array>
8#include <cstddef>
9
10namespace bb {
11
12// Given W field elements and the inverse of their product, return each element's own inverse using
13// only multiplications (no further inversions). Inversion is expensive, so the caller inverts the
14// product once and passes it in as `running_inv` (= 1 / (acc_lanes[0] * ... * acc_lanes[W-1])); this
15// then recovers 1 / acc_lanes[k] for every k. It is the cross-lane step of batch_invert, where each
16// acc_lanes[k] is one lane's accumulated product. Width-generic, so a SIMD target whose group size
17// differs from 5 reuses it unchanged.
18template <typename Field, size_t W>
19inline std::array<Field, W> compute_lane_inverses(const std::array<Field, W>& acc_lanes, Field running_inv) noexcept
20{
21 // prefix[k] = acc_lanes[0] * ... * acc_lanes[k]; only prefix[0..W-2] are needed below.
22 std::array<Field, W> prefix;
23 prefix[0] = acc_lanes[0];
24 for (size_t k = 1; k < W - 1; ++k) {
25 prefix[k] = prefix[k - 1] * acc_lanes[k];
26 }
27 std::array<Field, W> inv_lanes;
28 for (size_t k = W; k-- > 1;) { // k = W-1, W-2, ..., 1
29 inv_lanes[k] = running_inv * prefix[k - 1];
30 running_inv = running_inv * acc_lanes[k];
31 }
32 inv_lanes[0] = running_inv;
33 return inv_lanes;
34}
35
36// Invert every element of `in` into `out` using exactly one field inversion (Montgomery's trick).
37// This is the single sequential step in the affine-add pipeline; the prep and finish passes around it
38// run over each element independently.
39//
40// Why one inversion suffices: each full VectorField holds W elements, one per lane, and the lanes are
41// W independent product chains. A forward pass walks the VectorFields once, leaving each lane's total
42// product in `acc`; the final fewer-than-W elements (the tail) accumulate their own product. All W
43// lane products and the tail product fold into a single value, which is inverted once. The unwind
44// then reverses this: compute_lane_inverses splits the inverse back across the W lanes, and a
45// backward walk threads each lane's inverse through the VectorFields. The prefix products the
46// backward walk needs were stashed in `out` during the forward pass, and are overwritten in place
47// with the final inverses — so no extra scratch buffer is needed.
48//
49// Preconditions: `out` must NOT alias `in`, and out's backing must hold in.num_full_vectors()
50// VectorFields. A zero element (which makes the whole product zero) aborts.
51template <typename Params>
53{
55 using Field = typename Vec::Field;
56 constexpr size_t W = Vec::SIZE;
57
58 BB_ASSERT(!in.aliases(out));
59
60 // Forward prefix products: out[i] holds the running prefix; the returned accumulators are each
61 // lane's product (bulk) and the tail's product.
62 auto [acc, tail_acc] = map_accumulate<Direction::Forward>(
63 in, out, Vec::broadcast(Field::one()), Field::one(), [](auto& a, const auto& in_e, auto& out_e) {
64 out_e = a;
65 a = a * in_e;
66 });
67
68 // Pivot: fold all W lane products and the tail product into one value and invert it once.
69 const std::array<Field, W> acc_lanes = acc.to_array();
70 Field bulk_product = acc_lanes[0];
71 for (size_t j = 1; j < W; ++j) {
72 bulk_product = bulk_product * acc_lanes[j];
73 }
74 const Field total = bulk_product * tail_acc;
75 if (total.is_zero()) {
76 throw_or_abort("batch_invert: attempted to invert zero");
77 }
78 const Field inv_total = total.invert();
79 const Field inv_bulk_product = inv_total * tail_acc; // 1 / (product of all full-group elements)
80 const Field inv_tail_acc = inv_total * bulk_product; // 1 / (tail product)
81 const std::array<Field, W> lane_inv = compute_lane_inverses<Field, W>(acc_lanes, inv_bulk_product);
82
83 // Backward: thread each lane's inverse back through out's stored prefixes.
84 map_accumulate<Direction::Backward>(
85 in, out, Vec(lane_inv), inv_tail_acc, [](auto& state, const auto& in_e, auto& out_e) {
86 out_e = state * out_e;
87 state = state * in_e;
88 });
89 // map_accumulate already adopted out's cursor from in, so out reports the same size() / tail().
90}
91
92} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
FF a
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
std::array< Field, W > compute_lane_inverses(const std::array< Field, W > &acc_lanes, Field running_inv) noexcept
void batch_invert(const VectorFieldPushSpan< Params > &in, VectorFieldPushSpan< Params > &out) noexcept
bb::VectorAffineElementPushSpan< BaseParams > out
field< Params > Field
static constexpr size_t SIZE
static VectorField broadcast(const Field &s) noexcept
void throw_or_abort(std::string const &err)
bb::VectorField< bb::Bn254FrParams > Vec