-
Notifications
You must be signed in to change notification settings - Fork 103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Zw/recursion constraint reduction #377
Changes from all commits
cee6505
4e86e8f
a42e104
a996cdb
1a8e781
a24bc4c
514c974
8c0107e
8185307
0fb9f9a
ab94f8c
332bdd1
9cfb73d
bb44578
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -531,6 +531,7 @@ std::shared_ptr<proving_key> UltraComposer::compute_proving_key() | |
* our circuit is finalised, and we must not to execute these functions again. | ||
*/ | ||
if (!circuit_finalised) { | ||
process_non_native_field_multiplications(); | ||
process_ROM_arrays(public_inputs.size()); | ||
process_RAM_arrays(public_inputs.size()); | ||
process_range_lists(); | ||
|
@@ -1846,18 +1847,22 @@ std::array<uint32_t, 2> UltraComposer::decompose_non_native_field_double_width_l | |
} | ||
|
||
/** | ||
* NON NATIVE FIELD MULTIPLICATION CUSTOM GATE SEQUENCE | ||
* @brief Queue up non-native field multiplication data. | ||
* | ||
* This method will evaluate the equation (a * b = q * p + r) | ||
* Where a, b, q, r are all emulated non-native field elements that are each split across 4 distinct witness variables | ||
* @details The data queued represents a non-native field multiplication identity a * b = q * p + r, | ||
* where a, b, q, r are all emulated non-native field elements that are each split across 4 distinct witness variables. | ||
* | ||
* Without this queue some functions, such as proof_system::plonk::stdlib::element::double_montgomery_ladder, would | ||
* duplicate non-native field operations, which can be quite expensive. We queue up these operations, and remove | ||
* duplicates in the circuit finishing stage of the proving key computation. | ||
* | ||
* The non-native field modulus, p, is a circuit constant | ||
* | ||
* The return value are the witness indices of the two remainder limbs `lo_1, hi_2` | ||
* | ||
* N.B. this method does NOT evaluate the prime field component of non-native field multiplications | ||
* N.B.: This method does NOT evaluate the prime field component of non-native field multiplications. | ||
**/ | ||
std::array<uint32_t, 2> UltraComposer::evaluate_non_native_field_multiplication( | ||
std::array<uint32_t, 2> UltraComposer::queue_non_native_field_multiplication( | ||
const non_native_field_witnesses& input, const bool range_constrain_quotient_and_remainder) | ||
{ | ||
|
||
|
@@ -1889,8 +1894,6 @@ std::array<uint32_t, 2> UltraComposer::evaluate_non_native_field_multiplication( | |
constexpr barretenberg::fr LIMB_SHIFT = uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; | ||
constexpr barretenberg::fr LIMB_SHIFT_2 = uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); | ||
constexpr barretenberg::fr LIMB_SHIFT_3 = uint256_t(1) << (3 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); | ||
constexpr barretenberg::fr LIMB_RSHIFT = | ||
barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); | ||
constexpr barretenberg::fr LIMB_RSHIFT_2 = | ||
barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); | ||
|
||
|
@@ -1939,82 +1942,127 @@ std::array<uint32_t, 2> UltraComposer::evaluate_non_native_field_multiplication( | |
range_constrain_two_limbs(input.q[2], input.q[3]); | ||
} | ||
|
||
// product gate 1 | ||
// (lo_0 + q_0(p_0 + p_1*2^b) + q_1(p_0*2^b) - (r_1)2^b)2^-2b - lo_1 = 0 | ||
create_big_add_gate({ input.q[0], | ||
input.q[1], | ||
input.r[1], | ||
lo_1_idx, | ||
input.neg_modulus[0] + input.neg_modulus[1] * LIMB_SHIFT, | ||
input.neg_modulus[0] * LIMB_SHIFT, | ||
-LIMB_SHIFT, | ||
-LIMB_SHIFT.sqr(), | ||
0 }, | ||
true); | ||
// Add witnesses into the multiplication cache | ||
// (when finalising the circuit, we will remove duplicates; several dups produced by biggroup.hpp methods) | ||
cached_non_native_field_multiplication cache_entry{ | ||
.a = input.a, | ||
.b = input.b, | ||
.q = input.q, | ||
.r = input.r, | ||
.cross_terms = { lo_0_idx, lo_1_idx, hi_0_idx, hi_1_idx, hi_2_idx, hi_3_idx }, | ||
.neg_modulus = input.neg_modulus, | ||
}; | ||
cached_non_native_field_multiplications.emplace_back(cache_entry); | ||
|
||
w_l.emplace_back(input.a[1]); | ||
w_r.emplace_back(input.b[1]); | ||
w_o.emplace_back(input.r[0]); | ||
w_4.emplace_back(lo_0_idx); | ||
apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_1); | ||
++num_gates; | ||
w_l.emplace_back(input.a[0]); | ||
w_r.emplace_back(input.b[0]); | ||
w_o.emplace_back(input.a[3]); | ||
w_4.emplace_back(input.b[3]); | ||
apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_2); | ||
++num_gates; | ||
w_l.emplace_back(input.a[2]); | ||
w_r.emplace_back(input.b[2]); | ||
w_o.emplace_back(input.r[3]); | ||
w_4.emplace_back(hi_0_idx); | ||
apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_3); | ||
++num_gates; | ||
w_l.emplace_back(input.a[1]); | ||
w_r.emplace_back(input.b[1]); | ||
w_o.emplace_back(input.r[2]); | ||
w_4.emplace_back(hi_1_idx); | ||
apply_aux_selectors(AUX_SELECTORS::NONE); | ||
++num_gates; | ||
return std::array<uint32_t, 2>{ lo_1_idx, hi_3_idx }; | ||
} | ||
|
||
/** | ||
* product gate 6 | ||
* | ||
* hi_2 - hi_1 - lo_1 - q[2](p[1].2^b + p[0]) - q[3](p[0].2^b) = 0 | ||
* | ||
**/ | ||
create_big_add_gate( | ||
{ | ||
input.q[2], | ||
input.q[3], | ||
lo_1_idx, | ||
hi_1_idx, | ||
-input.neg_modulus[1] * LIMB_SHIFT - input.neg_modulus[0], | ||
-input.neg_modulus[0] * LIMB_SHIFT, | ||
-1, | ||
-1, | ||
0, | ||
}, | ||
true); | ||
/** | ||
* @brief Called in `compute_proving_key` when finalizing circuit. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO: name and description of |
||
* Iterates over the cached_non_native_field_multiplication objects, | ||
* removes duplicates, and instantiates the remainder as constraints` | ||
*/ | ||
void UltraComposer::process_non_native_field_multiplications() | ||
{ | ||
std::sort(cached_non_native_field_multiplications.begin(), cached_non_native_field_multiplications.end()); | ||
|
||
/** | ||
* product gate 7 | ||
* | ||
* hi_3 - (hi_2 - q[0](p[3].2^b + p[2]) - q[1](p[2].2^b + p[1])).2^-2b | ||
**/ | ||
create_big_add_gate({ | ||
hi_3_idx, | ||
input.q[0], | ||
input.q[1], | ||
hi_2_idx, | ||
-1, | ||
input.neg_modulus[3] * LIMB_RSHIFT + input.neg_modulus[2] * LIMB_RSHIFT_2, | ||
input.neg_modulus[2] * LIMB_RSHIFT + input.neg_modulus[1] * LIMB_RSHIFT_2, | ||
LIMB_RSHIFT_2, | ||
0, | ||
}); | ||
auto last = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eliminate duplicates by shifting unique entries back to toward the beginning of the vector, then returns a pointer to the first element of the vector past the last unique element ✔️. |
||
std::unique(cached_non_native_field_multiplications.begin(), cached_non_native_field_multiplications.end()); | ||
|
||
return std::array<uint32_t, 2>{ lo_1_idx, hi_3_idx }; | ||
auto it = cached_non_native_field_multiplications.begin(); | ||
|
||
constexpr barretenberg::fr LIMB_SHIFT = uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; | ||
constexpr barretenberg::fr LIMB_RSHIFT = | ||
barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); | ||
constexpr barretenberg::fr LIMB_RSHIFT_2 = | ||
barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); | ||
|
||
// iterate over the cached items and create constraints | ||
while (it != last) { | ||
const auto input = *it; | ||
const uint32_t lo_0_idx = input.cross_terms.lo_0_idx; | ||
const uint32_t lo_1_idx = input.cross_terms.lo_1_idx; | ||
const uint32_t hi_0_idx = input.cross_terms.hi_0_idx; | ||
const uint32_t hi_1_idx = input.cross_terms.hi_1_idx; | ||
const uint32_t hi_2_idx = input.cross_terms.hi_2_idx; | ||
const uint32_t hi_3_idx = input.cross_terms.hi_3_idx; | ||
|
||
// product gate 1 | ||
// (lo_0 + q_0(p_0 + p_1*2^b) + q_1(p_0*2^b) - (r_1)2^b)2^-2b - lo_1 = 0 | ||
create_big_add_gate({ input.q[0], | ||
input.q[1], | ||
input.r[1], | ||
lo_1_idx, | ||
input.neg_modulus[0] + input.neg_modulus[1] * LIMB_SHIFT, | ||
input.neg_modulus[0] * LIMB_SHIFT, | ||
-LIMB_SHIFT, | ||
-LIMB_SHIFT.sqr(), | ||
0 }, | ||
true); | ||
|
||
w_l.emplace_back(input.a[1]); | ||
w_r.emplace_back(input.b[1]); | ||
w_o.emplace_back(input.r[0]); | ||
w_4.emplace_back(lo_0_idx); | ||
apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_1); | ||
++num_gates; | ||
w_l.emplace_back(input.a[0]); | ||
w_r.emplace_back(input.b[0]); | ||
w_o.emplace_back(input.a[3]); | ||
w_4.emplace_back(input.b[3]); | ||
apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_2); | ||
++num_gates; | ||
w_l.emplace_back(input.a[2]); | ||
w_r.emplace_back(input.b[2]); | ||
w_o.emplace_back(input.r[3]); | ||
w_4.emplace_back(hi_0_idx); | ||
apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_3); | ||
++num_gates; | ||
w_l.emplace_back(input.a[1]); | ||
w_r.emplace_back(input.b[1]); | ||
w_o.emplace_back(input.r[2]); | ||
w_4.emplace_back(hi_1_idx); | ||
apply_aux_selectors(AUX_SELECTORS::NONE); | ||
++num_gates; | ||
|
||
/** | ||
* product gate 6 | ||
* | ||
* hi_2 - hi_1 - lo_1 - q[2](p[1].2^b + p[0]) - q[3](p[0].2^b) = 0 | ||
* | ||
**/ | ||
create_big_add_gate( | ||
{ | ||
input.q[2], | ||
input.q[3], | ||
lo_1_idx, | ||
hi_1_idx, | ||
-input.neg_modulus[1] * LIMB_SHIFT - input.neg_modulus[0], | ||
-input.neg_modulus[0] * LIMB_SHIFT, | ||
-1, | ||
-1, | ||
0, | ||
}, | ||
true); | ||
|
||
/** | ||
* product gate 7 | ||
* | ||
* hi_3 - (hi_2 - q[0](p[3].2^b + p[2]) - q[1](p[2].2^b + p[1])).2^-2b | ||
**/ | ||
create_big_add_gate({ | ||
hi_3_idx, | ||
input.q[0], | ||
input.q[1], | ||
hi_2_idx, | ||
-1, | ||
input.neg_modulus[3] * LIMB_RSHIFT + input.neg_modulus[2] * LIMB_RSHIFT_2, | ||
input.neg_modulus[2] * LIMB_RSHIFT + input.neg_modulus[1] * LIMB_RSHIFT_2, | ||
LIMB_RSHIFT_2, | ||
0, | ||
}); | ||
++it; | ||
} | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: clearer to make this a struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sanity check: these are witness indices, so the multiplications being removed really are duplicates. ✔️.