barretenberg
Loading...
Searching...
No Matches
grand_product_library.hpp
1#pragma once
2#include "barretenberg/common/constexpr_utils.hpp"
3#include "barretenberg/common/thread.hpp"
4#include "barretenberg/common/zip_view.hpp"
5#include "barretenberg/plonk/proof_system/proving_key/proving_key.hpp"
6#include "barretenberg/polynomials/polynomial.hpp"
7#include "barretenberg/relations/relation_parameters.hpp"
8#include <typeinfo>
9
10namespace proof_system::honk::grand_product_library {
11
12// TODO(luke): This contains utilities for grand product computation and is not specific to the permutation grand
13// product. Update comments accordingly.
50template <typename Flavor, typename GrandProdRelation>
51void compute_grand_product(const size_t circuit_size,
52 typename Flavor::ProverPolynomials& full_polynomials,
54{
55 using FF = typename Flavor::FF;
56 using Polynomial = typename Flavor::Polynomial;
57 using Accumulator = std::tuple_element_t<0, typename GrandProdRelation::SumcheckArrayOfValuesOverSubrelations>;
58
59 // Allocate numerator/denominator polynomials that will serve as scratch space
60 // TODO(zac) we can re-use the permutation polynomial as the numerator polynomial. Reduces readability
61 Polynomial numerator{ circuit_size };
62 Polynomial denominator{ circuit_size };
63
64 // Step (1)
65 // Populate `numerator` and `denominator` with the algebra described by Relation
66 const size_t num_threads = circuit_size >= get_num_cpus_pow2() ? get_num_cpus_pow2() : 1;
67 const size_t block_size = circuit_size / num_threads;
68 auto full_polynomials_view = full_polynomials.get_all();
69 parallel_for(num_threads, [&](size_t thread_idx) {
70 const size_t start = thread_idx * block_size;
71 const size_t end = (thread_idx + 1) * block_size;
72 for (size_t i = start; i < end; ++i) {
73 typename Flavor::AllValues evaluations;
74 for (auto [eval, full_poly] : zip_view(evaluations.get_all(), full_polynomials_view)) {
75 eval = full_poly.size() > i ? full_poly[i] : 0;
76 }
77 numerator[i] = GrandProdRelation::template compute_grand_product_numerator<Accumulator>(
78 evaluations, relation_parameters);
79 denominator[i] = GrandProdRelation::template compute_grand_product_denominator<Accumulator>(
80 evaluations, relation_parameters);
81 }
82 });
83
84 // Step (2)
85 // Compute the accumulating product of the numerator and denominator terms.
86 // This step is split into three parts for efficient multithreading:
87 // (i) compute ∏ A(j), ∏ B(j) subproducts for each thread
88 // (ii) compute scaling factor required to convert each subproduct into a single running product
89 // (ii) combine subproducts into a single running product
90 //
91 // For example, consider 4 threads and a size-8 numerator { a0, a1, a2, a3, a4, a5, a6, a7 }
92 // (i) Each thread computes 1 element of N = {{ a0, a0a1 }, { a2, a2a3 }, { a4, a4a5 }, { a6, a6a7 }}
93 // (ii) Take partial products P = { 1, a0a1, a2a3, a4a5 }
94 // (iii) Each thread j computes N[i][j]*P[j]=
95 // {{a0,a0a1},{a0a1a2,a0a1a2a3},{a0a1a2a3a4,a0a1a2a3a4a5},{a0a1a2a3a4a5a6,a0a1a2a3a4a5a6a7}}
96 std::vector<FF> partial_numerators(num_threads);
97 std::vector<FF> partial_denominators(num_threads);
98
99 parallel_for(num_threads, [&](size_t thread_idx) {
100 const size_t start = thread_idx * block_size;
101 const size_t end = (thread_idx + 1) * block_size;
102 for (size_t i = start; i < end - 1; ++i) {
103 numerator[i + 1] *= numerator[i];
104 denominator[i + 1] *= denominator[i];
105 }
106 partial_numerators[thread_idx] = numerator[end - 1];
107 partial_denominators[thread_idx] = denominator[end - 1];
108 });
109
110 parallel_for(num_threads, [&](size_t thread_idx) {
111 const size_t start = thread_idx * block_size;
112 const size_t end = (thread_idx + 1) * block_size;
113 if (thread_idx > 0) {
114 FF numerator_scaling = 1;
115 FF denominator_scaling = 1;
116
117 for (size_t j = 0; j < thread_idx; ++j) {
118 numerator_scaling *= partial_numerators[j];
119 denominator_scaling *= partial_denominators[j];
120 }
121 for (size_t i = start; i < end; ++i) {
122 numerator[i] *= numerator_scaling;
123 denominator[i] *= denominator_scaling;
124 }
125 }
126
127 // Final step: invert denominator
128 FF::batch_invert(std::span{ &denominator[start], block_size });
129 });
130
131 // Step (3) Compute z_perm[i] = numerator[i] / denominator[i]
132 auto& grand_product_polynomial = GrandProdRelation::get_grand_product_polynomial(full_polynomials);
133 grand_product_polynomial[0] = 0;
134 parallel_for(num_threads, [&](size_t thread_idx) {
135 const size_t start = thread_idx * block_size;
136 const size_t end = (thread_idx == num_threads - 1) ? circuit_size - 1 : (thread_idx + 1) * block_size;
137 for (size_t i = start; i < end; ++i) {
138 grand_product_polynomial[i + 1] = numerator[i] * denominator[i];
139 }
140 });
141}
142
143template <typename Flavor>
144void compute_grand_products(std::shared_ptr<typename Flavor::ProvingKey>& key,
145 typename Flavor::ProverPolynomials& full_polynomials,
147{
148 using GrandProductRelations = typename Flavor::GrandProductRelations;
149 using FF = typename Flavor::FF;
150
151 constexpr size_t NUM_RELATIONS = std::tuple_size<GrandProductRelations>{};
152 barretenberg::constexpr_for<0, NUM_RELATIONS, 1>([&]<size_t i>() {
153 using GrandProdRelation = typename std::tuple_element<i, GrandProductRelations>::type;
154
155 // Assign the grand product polynomial to the relevant std::span member of `full_polynomials` (and its shift)
156 // For example, for UltraPermutationRelation, this will be `full_polynomials.z_perm`
157 // For example, for LookupRelation, this will be `full_polynomials.z_lookup`
158 barretenberg::Polynomial<FF>& full_polynomial =
159 GrandProdRelation::get_grand_product_polynomial(full_polynomials);
160 auto& key_polynomial = GrandProdRelation::get_grand_product_polynomial(*key);
161 full_polynomial = key_polynomial.share();
162
163 compute_grand_product<Flavor, GrandProdRelation>(key->circuit_size, full_polynomials, relation_parameters);
164 barretenberg::Polynomial<FF>& full_polynomial_shift =
165 GrandProdRelation::get_shifted_grand_product_polynomial(full_polynomials);
166 full_polynomial_shift = key_polynomial.shifted();
167 });
168}
169
170} // namespace proof_system::honk::grand_product_library
Definition: polynomial.hpp:12
Polynomial shifted() const
Returns an std::span of the left-shift of self.
Definition: polynomial.cpp:323
Polynomial share() const
Definition: polynomial.cpp:141
A container for the prover polynomials handles.
Definition: AvmMini_flavor.hpp:263
Definition: zip_view.hpp:159
Container for parameters used by the grand product (permutation, lookup) Honk relations.
Definition: relation_parameters.hpp:12