3
3
#include " barretenberg/common/debug_log.hpp"
4
4
#include " barretenberg/common/thread.hpp"
5
5
#include " barretenberg/common/zip_view.hpp"
6
+ #include " barretenberg/flavor/flavor.hpp"
6
7
#include " barretenberg/plonk/proof_system/proving_key/proving_key.hpp"
7
8
#include " barretenberg/relations/relation_parameters.hpp"
9
+ #include " barretenberg/trace_to_polynomials/trace_to_polynomials.hpp"
8
10
#include < typeinfo>
9
11
10
12
namespace bb {
@@ -47,74 +49,68 @@ namespace bb {
47
49
*
48
50
* Note: Step (3) utilizes Montgomery batch inversion to replace n-many inversions with
49
51
*
52
+ * @note This method makes use of the fact that there are at most as many unique entries in the grand product as active
53
+ * rows in the execution trace to efficiently compute the grand product when a structured trace is in use. I.e. the
54
+ * computation peformed herein is proportional to the number of active rows in the trace and the constant values in the
55
+ * inactive regions are simply populated from known values on the last step.
56
+ *
50
57
* @tparam Flavor
51
58
* @tparam GrandProdRelation
52
59
* @param full_polynomials
53
60
* @param relation_parameters
54
61
* @param size_override optional size of the domain; otherwise based on dyadic polynomial domain
62
+ * @param active_region_data optional specification of active region of execution trace
55
63
*/
56
64
template <typename Flavor, typename GrandProdRelation>
57
65
void compute_grand_product (typename Flavor::ProverPolynomials& full_polynomials,
58
66
bb::RelationParameters<typename Flavor::FF>& relation_parameters,
59
67
size_t size_override = 0 ,
60
- std::vector<std::pair< size_t , size_t >> active_block_ranges = {})
68
+ const ActiveRegionData& active_region_data = ActiveRegionData {})
61
69
{
62
70
PROFILE_THIS_NAME (" compute_grand_product" );
63
71
64
72
using FF = typename Flavor::FF;
65
73
using Polynomial = typename Flavor::Polynomial;
66
74
using Accumulator = std::tuple_element_t <0 , typename GrandProdRelation::SumcheckArrayOfValuesOverSubrelations>;
67
75
76
+ const bool has_active_ranges = active_region_data.size () > 0 ;
77
+
68
78
// Set the domain over which the grand product must be computed. This may be less than the dyadic circuit size, e.g
69
79
// the permutation grand product does not need to be computed beyond the index of the last active wire
70
80
size_t domain_size = size_override == 0 ? full_polynomials.get_polynomial_size () : size_override;
71
81
72
- const size_t num_threads = domain_size >= get_num_cpus_pow2 () ? get_num_cpus_pow2 () : 1 ;
73
- const size_t block_size = domain_size / num_threads;
74
- const size_t final_idx = domain_size - 1 ;
75
-
76
- // Cumpute the index bounds for each thread for reuse in the computations below
77
- std::vector<std::pair<size_t , size_t >> idx_bounds;
78
- idx_bounds.reserve (num_threads);
79
- for (size_t thread_idx = 0 ; thread_idx < num_threads; ++thread_idx) {
80
- const size_t start = thread_idx * block_size;
81
- const size_t end = (thread_idx == num_threads - 1 ) ? final_idx : (thread_idx + 1 ) * block_size;
82
- idx_bounds.push_back (std::make_pair (start, end));
83
- }
82
+ // Returns the ith active index if specified, otherwise acts as the identity map on the input
83
+ auto get_active_range_poly_idx = [&](size_t i) { return has_active_ranges ? active_region_data.get_idx (i) : i; };
84
+
85
+ size_t active_domain_size = has_active_ranges ? active_region_data.size () : domain_size;
86
+
87
+ // The size of the iteration domain is one less than the number of active rows since the final value of the
88
+ // grand product is constructed only in the relation and not explicitly in the polynomial
89
+ const MultithreadData active_range_thread_data = calculate_thread_data (active_domain_size - 1 );
84
90
85
91
// Allocate numerator/denominator polynomials that will serve as scratch space
86
92
// TODO(zac) we can re-use the permutation polynomial as the numerator polynomial. Reduces readability
87
- Polynomial numerator{ domain_size, domain_size };
88
- Polynomial denominator{ domain_size, domain_size };
89
-
90
- auto check_is_active = [&](size_t idx) {
91
- if (active_block_ranges.empty ()) {
92
- return true ;
93
- }
94
- return std::any_of (active_block_ranges.begin (), active_block_ranges.end (), [idx](const auto & range) {
95
- return idx >= range.first && idx < range.second ;
96
- });
97
- };
93
+ Polynomial numerator{ active_domain_size };
94
+ Polynomial denominator{ active_domain_size };
98
95
99
96
// Step (1)
100
97
// Populate `numerator` and `denominator` with the algebra described by Relation
101
- FF gamma_fourth = relation_parameters.gamma .pow (4 );
102
- parallel_for (num_threads, [&](size_t thread_idx) {
98
+ parallel_for (active_range_thread_data.num_threads , [&](size_t thread_idx) {
99
+ const size_t start = active_range_thread_data.start [thread_idx];
100
+ const size_t end = active_range_thread_data.end [thread_idx];
103
101
typename Flavor::AllValues row;
104
- const size_t start = idx_bounds[thread_idx].first ;
105
- const size_t end = idx_bounds[thread_idx].second ;
106
102
for (size_t i = start; i < end; ++i) {
107
- if (check_is_active (i)) {
108
- // TODO(https://github.com/AztecProtocol/barretenberg/issues/940):consider avoiding get_row if possible.
109
- row = full_polynomials.get_row (i);
110
- numerator.at (i) =
111
- GrandProdRelation::template compute_grand_product_numerator<Accumulator>(row, relation_parameters);
112
- denominator.at (i) = GrandProdRelation::template compute_grand_product_denominator<Accumulator>(
113
- row, relation_parameters);
103
+ // TODO(https://github.com/AztecProtocol/barretenberg/issues/940):consider avoiding get_row if possible.
104
+ auto row_idx = get_active_range_poly_idx (i);
105
+ if constexpr (IsUltraFlavor<Flavor>) {
106
+ row = full_polynomials.get_row_for_permutation_arg (row_idx);
114
107
} else {
115
- numerator.at (i) = gamma_fourth;
116
- denominator.at (i) = gamma_fourth;
108
+ row = full_polynomials.get_row (row_idx);
117
109
}
110
+ numerator.at (i) =
111
+ GrandProdRelation::template compute_grand_product_numerator<Accumulator>(row, relation_parameters);
112
+ denominator.at (i) =
113
+ GrandProdRelation::template compute_grand_product_denominator<Accumulator>(row, relation_parameters);
118
114
}
119
115
});
120
116
@@ -133,12 +129,12 @@ void compute_grand_product(typename Flavor::ProverPolynomials& full_polynomials,
133
129
// (ii) Take partial products P = { 1, a0a1, a2a3, a4a5 }
134
130
// (iii) Each thread j computes N[i][j]*P[j]=
135
131
// {{a0,a0a1},{a0a1a2,a0a1a2a3},{a0a1a2a3a4,a0a1a2a3a4a5},{a0a1a2a3a4a5a6,a0a1a2a3a4a5a6a7}}
136
- std::vector<FF> partial_numerators (num_threads);
137
- std::vector<FF> partial_denominators (num_threads);
132
+ std::vector<FF> partial_numerators (active_range_thread_data. num_threads );
133
+ std::vector<FF> partial_denominators (active_range_thread_data. num_threads );
138
134
139
- parallel_for (num_threads, [&](size_t thread_idx) {
140
- const size_t start = idx_bounds [thread_idx]. first ;
141
- const size_t end = idx_bounds [thread_idx]. second ;
135
+ parallel_for (active_range_thread_data. num_threads , [&](size_t thread_idx) {
136
+ const size_t start = active_range_thread_data. start [thread_idx];
137
+ const size_t end = active_range_thread_data. end [thread_idx];
142
138
for (size_t i = start; i < end - 1 ; ++i) {
143
139
numerator.at (i + 1 ) *= numerator[i];
144
140
denominator.at (i + 1 ) *= denominator[i];
@@ -150,9 +146,9 @@ void compute_grand_product(typename Flavor::ProverPolynomials& full_polynomials,
150
146
DEBUG_LOG_ALL (partial_numerators);
151
147
DEBUG_LOG_ALL (partial_denominators);
152
148
153
- parallel_for (num_threads, [&](size_t thread_idx) {
154
- const size_t start = idx_bounds [thread_idx]. first ;
155
- const size_t end = idx_bounds [thread_idx]. second ;
149
+ parallel_for (active_range_thread_data. num_threads , [&](size_t thread_idx) {
150
+ const size_t start = active_range_thread_data. start [thread_idx];
151
+ const size_t end = active_range_thread_data. end [thread_idx];
156
152
if (thread_idx > 0 ) {
157
153
FF numerator_scaling = 1 ;
158
154
FF denominator_scaling = 1 ;
@@ -179,14 +175,44 @@ void compute_grand_product(typename Flavor::ProverPolynomials& full_polynomials,
179
175
// We have a 'virtual' 0 at the start (as this is a to-be-shifted polynomial)
180
176
ASSERT (grand_product_polynomial.start_index () == 1 );
181
177
182
- parallel_for (num_threads, [&](size_t thread_idx) {
183
- const size_t start = idx_bounds[thread_idx].first ;
184
- const size_t end = idx_bounds[thread_idx].second ;
178
+ // For Ultra/Mega, the first row is an inactive zero row thus the grand prod takes value 1 at both i = 0 and i = 1
179
+ if constexpr (IsUltraFlavor<Flavor>) {
180
+ grand_product_polynomial.at (1 ) = 1 ;
181
+ }
182
+
183
+ // Compute grand product values corresponding only to the active regions of the trace
184
+ parallel_for (active_range_thread_data.num_threads , [&](size_t thread_idx) {
185
+ const size_t start = active_range_thread_data.start [thread_idx];
186
+ const size_t end = active_range_thread_data.end [thread_idx];
185
187
for (size_t i = start; i < end; ++i) {
186
- grand_product_polynomial.at (i + 1 ) = numerator[i] * denominator[i];
188
+ const auto poly_idx = get_active_range_poly_idx (i + 1 );
189
+ grand_product_polynomial.at (poly_idx) = numerator[i] * denominator[i];
187
190
}
188
191
});
189
192
193
+ // Final step: If active/inactive regions have been specified, the value of the grand product in the inactive
194
+ // regions have not yet been set. The polynomial takes an already computed constant value across each inactive
195
+ // region (since no copy constraints are present there) equal to the value of the grand product at the first index
196
+ // of the subsequent active region.
197
+ if (has_active_ranges) {
198
+ MultithreadData full_domain_thread_data = calculate_thread_data (domain_size);
199
+ parallel_for (full_domain_thread_data.num_threads , [&](size_t thread_idx) {
200
+ const size_t start = full_domain_thread_data.start [thread_idx];
201
+ const size_t end = full_domain_thread_data.end [thread_idx];
202
+ for (size_t i = start; i < end; ++i) {
203
+ for (size_t j = 0 ; j < active_region_data.num_ranges () - 1 ; ++j) {
204
+ const size_t previous_range_end = active_region_data.get_range (j).second ;
205
+ const size_t next_range_start = active_region_data.get_range (j + 1 ).first ;
206
+ // Set the value of the polynomial if the index falls in an inactive region
207
+ if (i >= previous_range_end && i < next_range_start) {
208
+ grand_product_polynomial.at (i) = grand_product_polynomial[next_range_start];
209
+ break ;
210
+ }
211
+ }
212
+ }
213
+ });
214
+ }
215
+
190
216
DEBUG_LOG_ALL (grand_product_polynomial.coeffs ());
191
217
}
192
218
0 commit comments