Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
batch_inversion.test.cpp
Go to the documentation of this file.
2
5
6#include <array>
7#include <gtest/gtest.h>
8#include <vector>
9
10namespace {
11
12using bb::fq;
15
17
18fq random_nonzero()
19{
20 fq v;
21 do {
22 v = fq::random_element(&engine);
23 } while (v.is_zero());
24 return v;
25}
26
27// Invert the product once, recover each element's inverse, and check against a direct per-element
28// invert(). Exercised across a few widths to confirm the helper is width-generic.
29template <size_t W> void check_compute_lane_inverses()
30{
32 fq product = fq::one();
33 for (auto& v : a) {
34 v = fq::random_element(&engine);
35 product *= v;
36 }
37 const fq inv_product = product.invert();
38 const std::array<fq, W> inv = bb::compute_lane_inverses<fq, W>(a, inv_product);
39 for (size_t k = 0; k < W; ++k) {
40 EXPECT_EQ(inv[k], a[k].invert());
41 EXPECT_EQ(inv[k] * a[k], fq::one());
42 }
43}
44
45TEST(BatchInversion, ComputeLaneInversesWidth5)
46{
47 check_compute_lane_inverses<5>();
48}
49
50TEST(BatchInversion, ComputeLaneInversesWidth1)
51{
52 check_compute_lane_inverses<1>();
53}
54
55TEST(BatchInversion, ComputeLaneInversesWidth8)
56{
57 check_compute_lane_inverses<8>();
58}
59
60// batch_invert over a VectorFieldPushSpan: every element's inverse, computed with one field inversion,
61// must equal a direct per-element invert(). Checked across boundary sizes (empty, partial, ragged).
62TEST(BatchInversion, BatchInvertSpan)
63{
64 constexpr size_t W = PushSpanFq::W;
65 for (size_t n :
66 { size_t{ 0 }, size_t{ 1 }, size_t{ 4 }, size_t{ 5 }, size_t{ 6 }, size_t{ 9 }, size_t{ 25 }, size_t{ 31 } }) {
67 std::vector<fq> src(n);
68 for (auto& v : src) {
69 v = random_nonzero();
70 }
71 std::vector<VecFq> in_backing((n / W) + 1);
72 std::vector<VecFq> out_backing((n / W) + 1);
73 PushSpanFq in{ std::span<VecFq>(in_backing) };
74 PushSpanFq out{ std::span<VecFq>(out_backing) };
75 for (const auto& v : src) {
76 in.push(v);
77 }
78
80
81 ASSERT_EQ(out.size(), n);
82 for (size_t i = 0; i < n; ++i) {
83 const fq got = (i < out.num_full_vectors() * W) ? out[i / W].to_array()[i % W] : out.tail_data()[i % W];
84 EXPECT_EQ(got, src[i].invert()) << "n=" << n << " i=" << i;
85 EXPECT_EQ(got * src[i], fq::one());
86 // in is read again in the backward pass, so it must come back unchanged.
87 const fq in_i = (i < in.num_full_vectors() * W) ? in[i / W].to_array()[i % W] : in.tail_data()[i % W];
88 EXPECT_EQ(in_i, src[i]) << "in mutated at n=" << n << " i=" << i;
89 }
90 }
91}
92
93// A zero anywhere makes the whole product zero, which batch_invert must refuse rather than feed to
94// invert(). Checked with the zero in a full VectorField and in the tail.
95// Skipped in WASM builds: death tests (EXPECT_DEATH) aren't supported under WASI.
96#ifndef __wasm__
97TEST(BatchInversionDeath, ZeroInputAborts)
98{
99 constexpr size_t W = PushSpanFq::W;
100 for (size_t zero_at : { size_t{ 2 }, size_t{ 6 } }) { // full-vector lane, then tail
101 const size_t n = 7;
102 std::vector<VecFq> in_backing((n / W) + 1);
103 std::vector<VecFq> out_backing((n / W) + 1);
104 PushSpanFq in{ std::span<VecFq>(in_backing) };
105 PushSpanFq out{ std::span<VecFq>(out_backing) };
106 for (size_t i = 0; i < n; ++i) {
107 in.push(i == zero_at ? fq::zero() : random_nonzero());
108 }
109 EXPECT_DEATH(bb::batch_invert(in, out), "invert zero");
110 }
111}
112
113// out must not alias in (batch_invert reads in while writing prefixes into out); passing
114// one span as both must trip the BB_ASSERT(!in.aliases(out)) guard.
115TEST(BatchInversionDeath, AliasedOutputAborts)
116{
117 constexpr size_t W = PushSpanFq::W;
118 const size_t n = 7;
119 std::vector<VecFq> backing((n / W) + 1);
120 PushSpanFq span{ std::span<VecFq>(backing) };
121 for (size_t i = 0; i < n; ++i) {
122 span.push(random_nonzero());
123 }
124 EXPECT_DEATH(bb::batch_invert(span, span), "aliases");
125}
126#endif
127
128} // namespace
TEST(acir_formal_proofs, uint_terms_add)
Tests 128-bit unsigned addition Verifies that the ACIR implementation of addition is correct Executio...
FF a
numeric::RNG & engine
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:245
field< Bn254FqParams > fq
Definition fq.hpp:153
void batch_invert(const VectorFieldPushSpan< Params > &in, VectorFieldPushSpan< Params > &out) noexcept
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
bb::VectorAffineElementPushSpan< BaseParams > out
constexpr field invert() const noexcept