Skip to content
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

chore: Minor Gemini updates in prep for more general shifts #11292

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,22 @@ template <typename Curve> class GeminiProver_ {
using Claim = ProverOpeningClaim<Curve>;

public:
static std::vector<Polynomial> compute_fold_polynomials(const size_t log_N,
static std::vector<Polynomial> compute_fold_polynomials(const size_t log_n,
std::span<const Fr> multilinear_challenge,
Polynomial&& batched_unshifted,
Polynomial&& batched_to_be_shifted,
Polynomial&& batched_concatenated = {});
const Polynomial& A_0);

static std::vector<Claim> compute_fold_polynomial_evaluations(
const size_t log_N,
std::vector<Polynomial>&& fold_polynomials,
static std::pair<Polynomial, Polynomial> compute_partially_evaluated_batch_polynomials(
const size_t log_n,
Polynomial&& batched_F,
Polynomial&& batched_G,
const Fr& r_challenge,
std::vector<Polynomial>&& batched_groups_to_be_concatenated = {});
std::vector<Polynomial> batched_groups_to_be_concatenated = {});

static std::vector<Claim> construct_univariate_opening_claims(const size_t log_n,
Polynomial&& A_0_pos,
Polynomial&& A_0_neg,
std::vector<Polynomial>&& fold_polynomials,
const Fr& r_challenge);

template <typename Transcript>
static std::vector<Claim> prove(const Fr circuit_size,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ std::vector<typename GeminiProver_<Curve>::Claim> GeminiProver_<Curve>::prove(
RefSpan<Polynomial> concatenated_polynomials,
const std::vector<RefVector<Polynomial>>& groups_to_be_concatenated,
bool has_zk)

{
const size_t log_n = numeric::get_msb(static_cast<uint32_t>(circuit_size));
const size_t n = 1 << log_n;

const bool has_concatenations = concatenated_polynomials.size() > 0;

// Compute batched polynomials
Polynomial batched_unshifted(n);
Polynomial batched_to_be_shifted = Polynomial::shiftable(n);
Expand All @@ -78,6 +79,7 @@ std::vector<typename GeminiProver_<Curve>::Claim> GeminiProver_<Curve>::prove(
// ρ⁰ is used to batch the hiding polynomial
rho_challenge *= rho;
}

for (size_t i = 0; i < f_polynomials.size(); i++) {
batched_unshifted.add_scaled(f_polynomials[i], rho_challenge);
rho_challenge *= rho;
Expand All @@ -90,34 +92,41 @@ std::vector<typename GeminiProver_<Curve>::Claim> GeminiProver_<Curve>::prove(
size_t num_groups = groups_to_be_concatenated.size();
size_t num_chunks_per_group = groups_to_be_concatenated.empty() ? 0 : groups_to_be_concatenated[0].size();

// Allocate space for the groups to be concatenated and for the concatenated polynomials
Polynomial batched_concatenated(n);
// If needed, allocate space for the groups to be concatenated and for the concatenated polynomials
Polynomial batched_concatenated;
std::vector<Polynomial> batched_group;
for (size_t i = 0; i < num_chunks_per_group; ++i) {
batched_group.push_back(Polynomial(n));
}
if (has_concatenations) {
batched_concatenated = Polynomial(n);
for (size_t i = 0; i < num_chunks_per_group; ++i) {
batched_group.push_back(Polynomial(n));
}

for (size_t i = 0; i < num_groups; ++i) {
batched_concatenated.add_scaled(concatenated_polynomials[i], rho_challenge);
for (size_t j = 0; j < num_chunks_per_group; ++j) {
batched_group[j].add_scaled(groups_to_be_concatenated[i][j], rho_challenge);
for (size_t i = 0; i < num_groups; ++i) {
batched_concatenated.add_scaled(concatenated_polynomials[i], rho_challenge);
for (size_t j = 0; j < num_chunks_per_group; ++j) {
batched_group[j].add_scaled(groups_to_be_concatenated[i][j], rho_challenge);
}
rho_challenge *= rho;
}
rho_challenge *= rho;
}

auto fold_polynomials = compute_fold_polynomials(log_n,
multilinear_challenge,
std::move(batched_unshifted),
std::move(batched_to_be_shifted),
std::move(batched_concatenated));
// Construct the batched polynomial A₀(X) = F(X) + G↺(X) = F(X) + G(X)/X
Polynomial A_0 = batched_unshifted;
A_0 += batched_to_be_shifted.shifted();
if (has_concatenations) { // If proving for translator, add contribution of the batched concatenation polynomials
A_0 += batched_concatenated;
}

// Construct the d-1 Gemini foldings of A₀(X)
std::vector<Polynomial> fold_polynomials = compute_fold_polynomials(log_n, multilinear_challenge, A_0);

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1159): Decouple constants from primitives.
for (size_t l = 0; l < CONST_PROOF_SIZE_LOG_N - 1; l++) {
std::string label = "Gemini:FOLD_" + std::to_string(l + 1);
if (l < log_n - 1) {
transcript->send_to_verifier("Gemini:FOLD_" + std::to_string(l + 1),
commitment_key->commit(fold_polynomials[l + 2]));
transcript->send_to_verifier(label, commitment_key->commit(fold_polynomials[l]));
} else {
transcript->send_to_verifier("Gemini:FOLD_" + std::to_string(l + 1), Commitment::one());
transcript->send_to_verifier(label, Commitment::one());
}
}
const Fr r_challenge = transcript->template get_challenge<Fr>("Gemini:r");
Expand All @@ -131,14 +140,20 @@ std::vector<typename GeminiProver_<Curve>::Claim> GeminiProver_<Curve>::prove(
throw_or_abort("Gemini evaluation challenge is in the SmallSubgroup.");
}

std::vector<Claim> claims =
compute_fold_polynomial_evaluations(log_n, std::move(fold_polynomials), r_challenge, std::move(batched_group));
// Compute polynomials A₀₊(X) = F(X) + G(X)/r and A₀₋(X) = F(X) - G(X)/r
auto [A_0_pos, A_0_neg] = compute_partially_evaluated_batch_polynomials(
log_n, std::move(batched_unshifted), std::move(batched_to_be_shifted), r_challenge, batched_group);

// Construct claims for the d + 1 univariate evaluations A₀₊(r), A₀₋(-r), and Foldₗ(−r^{2ˡ}), l = 1, ..., d-1
std::vector<Claim> claims = construct_univariate_opening_claims(
log_n, std::move(A_0_pos), std::move(A_0_neg), std::move(fold_polynomials), r_challenge);

for (size_t l = 1; l <= CONST_PROOF_SIZE_LOG_N; l++) {
std::string label = "Gemini:a_" + std::to_string(l);
if (l <= log_n) {
transcript->send_to_verifier("Gemini:a_" + std::to_string(l), claims[l].opening_pair.evaluation);
transcript->send_to_verifier(label, claims[l].opening_pair.evaluation);
} else {
transcript->send_to_verifier("Gemini:a_" + std::to_string(l), Fr::zero());
transcript->send_to_verifier(label, Fr::zero());
}
}

Expand All @@ -148,48 +163,24 @@ std::vector<typename GeminiProver_<Curve>::Claim> GeminiProver_<Curve>::prove(
/**
* @brief Computes d-1 fold polynomials Fold_i, i = 1, ..., d-1
*
* @param mle_opening_point multilinear opening point 'u'
* @param batched_unshifted F(X) = ∑ⱼ ρʲ fⱼ(X) .
* @param batched_to_be_shifted G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X)
* @param batched_concatenated The sum of batched concatenated polynomial,
* @param multilinear_challenge multilinear opening point 'u'
* @param A_0 = F(X) + G↺(X) = F(X) + G(X)/X
* @return std::vector<Polynomial>
*/
template <typename Curve>
std::vector<typename GeminiProver_<Curve>::Polynomial> GeminiProver_<Curve>::compute_fold_polynomials(
const size_t num_variables,
std::span<const Fr> mle_opening_point,
Polynomial&& batched_unshifted,
Polynomial&& batched_to_be_shifted,
Polynomial&& batched_concatenated)
const size_t log_n, std::span<const Fr> multilinear_challenge, const Polynomial& A_0)
{
const size_t num_threads = get_num_cpus_pow2();
constexpr size_t efficient_operations_per_thread = 64; // A guess of the number of operation for which there
// would be a point in sending them to a separate thread

// Allocate space for m+1 Fold polynomials
//
// The first two are populated here with the batched unshifted and to-be-shifted polynomial respectively.
// They will eventually contain the full batched polynomial A₀ partially evaluated at the challenges r,-r.
// This function populates the other m-1 polynomials with the foldings of A₀.
// Reserve and allocate space for m-1 Fold polynomials, the foldings of the full batched polynomial A₀
std::vector<Polynomial> fold_polynomials;
fold_polynomials.reserve(num_variables + 1);

// F(X) = ∑ⱼ ρʲ fⱼ(X) and G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X)
Polynomial& batched_F = fold_polynomials.emplace_back(std::move(batched_unshifted));
Polynomial& batched_G = fold_polynomials.emplace_back(std::move(batched_to_be_shifted));
constexpr size_t offset_to_folded = 2; // Offset because of F an G
// A₀(X) = F(X) + G↺(X) = F(X) + G(X)/X.
Polynomial A_0 = batched_F;

// If proving the opening for translator, add a non-zero contribution of the batched concatenation polynomials
A_0 += batched_concatenated;

A_0 += batched_G.shifted();

// Allocate everything before parallel computation
for (size_t l = 0; l < num_variables - 1; ++l) {
fold_polynomials.reserve(log_n - 1);
for (size_t l = 0; l < log_n - 1; ++l) {
// size of the previous polynomial/2
const size_t n_l = 1 << (num_variables - l - 1);
const size_t n_l = 1 << (log_n - l - 1);

// A_l_fold = Aₗ₊₁(X) = (1-uₗ)⋅even(Aₗ)(X) + uₗ⋅odd(Aₗ)(X)
fold_polynomials.emplace_back(Polynomial(n_l));
Expand All @@ -199,9 +190,9 @@ std::vector<typename GeminiProver_<Curve>::Polynomial> GeminiProver_<Curve>::com
// in the first iteration, we take the batched polynomial
// in the next iteration, it is the previously folded one
auto A_l = A_0.data();
for (size_t l = 0; l < num_variables - 1; ++l) {
for (size_t l = 0; l < log_n - 1; ++l) {
// size of the previous polynomial/2
const size_t n_l = 1 << (num_variables - l - 1);
const size_t n_l = 1 << (log_n - l - 1);

// Use as many threads as it is useful so that 1 thread doesn't process 1 element, but make sure that there is
// at least 1
Expand All @@ -210,11 +201,11 @@ std::vector<typename GeminiProver_<Curve>::Polynomial> GeminiProver_<Curve>::com
size_t chunk_size = n_l / num_used_threads;
size_t last_chunk_size = (n_l % chunk_size) ? (n_l % num_used_threads) : chunk_size;

// Openning point is the same for all
const Fr u_l = mle_opening_point[l];
// Opening point is the same for all
const Fr u_l = multilinear_challenge[l];

// A_l_fold = Aₗ₊₁(X) = (1-uₗ)⋅even(Aₗ)(X) + uₗ⋅odd(Aₗ)(X)
auto A_l_fold = fold_polynomials[l + offset_to_folded].data();
auto A_l_fold = fold_polynomials[l].data();

parallel_for(num_used_threads, [&](size_t i) {
size_t current_chunk_size = (i == (num_used_threads - 1)) ? last_chunk_size : chunk_size;
Expand All @@ -235,51 +226,31 @@ std::vector<typename GeminiProver_<Curve>::Polynomial> GeminiProver_<Curve>::com
};

/**
* @brief Computes/aggragates d+1 Fold polynomials and their opening pairs (challenge, evaluation)
*
* @details This function assumes that, upon input, last d-1 entries in fold_polynomials are Fold_i.
* The first two entries are assumed to be, respectively, the batched unshifted and batched to-be-shifted
* polynomials F(X) = ∑ⱼ ρʲfⱼ(X) and G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X). This function completes the computation
* of the first two Fold polynomials as F + G/r and F - G/r. It then evaluates each of the d+1
* fold polynomials at, respectively, the points r, rₗ = r^{2ˡ} for l = 0, 1, ..., d-1.
* @brief Computes partially evaluated batched polynomials A₀₊(X) = F(X) + G(X)/r and A₀₋(X) = F(X) - G(X)/r
*
* @param mle_opening_point u = (u₀,...,uₘ₋₁) is the MLE opening point
* @param fold_polynomials vector of polynomials whose first two elements are F(X) = ∑ⱼ ρʲfⱼ(X)
* and G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X), and the next d-1 elements are Fold_i, i = 1, ..., d-1.
* @param r_challenge univariate opening challenge
* @param batched_F F(X) = ∑ⱼ ρʲfⱼ(X)
* @param batched_G G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X)
* @param r_challenge
* @param batched_groups_to_be_concatenated
* @return {A₀₊(X), A₀₋(X)}
*/
template <typename Curve>
std::vector<typename GeminiProver_<Curve>::Claim> GeminiProver_<Curve>::compute_fold_polynomial_evaluations(
const size_t num_variables,
std::vector<Polynomial>&& fold_polynomials,
const Fr& r_challenge,
std::vector<Polynomial>&& batched_groups_to_be_concatenated)
std::pair<typename GeminiProver_<Curve>::Polynomial, typename GeminiProver_<Curve>::Polynomial> GeminiProver_<
Curve>::compute_partially_evaluated_batch_polynomials(const size_t log_n,
Polynomial&& batched_F,
Polynomial&& batched_G,
const Fr& r_challenge,
std::vector<Polynomial> batched_groups_to_be_concatenated)
{

Polynomial& batched_F = fold_polynomials[0]; // F(X) = ∑ⱼ ρʲ fⱼ(X)

Polynomial& batched_G = fold_polynomials[1]; // G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X)

// Compute univariate opening queries rₗ = r^{2ˡ} for l = 0, 1, ..., m-1
std::vector<Fr> r_squares = gemini::powers_of_evaluation_challenge(r_challenge, num_variables);
Polynomial& A_0_pos = batched_F; // A₀₊ = F
Polynomial A_0_neg = batched_F; // A₀₋ = F

// Compute G/r
Fr r_inv = r_challenge.invert();
batched_G *= r_inv;

// Construct A₀₊ = F + G/r and A₀₋ = F - G/r in place in fold_polynomials
Polynomial tmp = batched_F;
Polynomial& A_0_pos = fold_polynomials[0];

// A₀₊(X) = F(X) + G(X)/r, s.t. A₀₊(r) = A₀(r)
A_0_pos += batched_G;

// Perform a swap so that tmp = G(X)/r and A_0_neg = F(X)
std::swap(tmp, batched_G);
Polynomial& A_0_neg = fold_polynomials[1];

// A₀₋(X) = F(X) - G(X)/r, s.t. A₀₋(-r) = A₀(-r)
A_0_neg -= tmp;
A_0_pos += batched_G; // A₀₊ = F + G/r
A_0_neg -= batched_G; // A₀₋ = F - G/r

// Reconstruct the batched concatenated polynomial from the batched groups, partially evaluated at r and -r and add
// the result to A₀₊(X) and A₀₋(X). Explanation (for simplification assume a single concatenated polynomial):
Expand All @@ -292,7 +263,7 @@ std::vector<typename GeminiProver_<Curve>::Claim> GeminiProver_<Curve>::compute_
// P
if (!batched_groups_to_be_concatenated.empty()) {
// The "real" size of polynomials in concatenation groups (i.e. the number of non-zero values)
const size_t mini_circuit_size = (1 << num_variables) / batched_groups_to_be_concatenated.size();
const size_t mini_circuit_size = (1 << log_n) / batched_groups_to_be_concatenated.size();
Fr current_r_shift_pos = Fr(1);
Fr current_r_shift_neg = Fr(1);

Expand All @@ -308,18 +279,57 @@ std::vector<typename GeminiProver_<Curve>::Claim> GeminiProver_<Curve>::compute_
}
}

std::vector<Claim> opening_claims;
opening_claims.reserve(num_variables + 1);
return { std::move(A_0_pos), std::move(A_0_neg) };
};

// Compute first opening pair {r, A₀(r)}
Fr evaluation = fold_polynomials[0].evaluate(r_challenge);
opening_claims.emplace_back(Claim{ fold_polynomials[0], { r_challenge, evaluation } });
// Compute the remaining m opening pairs {−r^{2ˡ}, Aₗ(−r^{2ˡ})}, l = 0, ..., m-1.
for (size_t l = 0; l < num_variables; ++l) {
evaluation = fold_polynomials[l + 1].evaluate(-r_squares[l]);
opening_claims.emplace_back(Claim{ fold_polynomials[l + 1], { -r_squares[l], evaluation } });
/**

*
* @param mle_opening_point u = (u₀,...,uₘ₋₁) is the MLE opening point
* @param fold_polynomials vector of polynomials whose first two elements are F(X) = ∑ⱼ ρʲfⱼ(X)
* and G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X), and the next d-1 elements are Fold_i, i = 1, ..., d-1.
* @param r_challenge univariate opening challenge
*/

/**
* @brief Computes/aggragates d+1 univariate polynomial opening claims of the form {polynomial, (challenge, evaluation)}
*
* @details The d+1 evaluations are A₀₊(r), A₀₋(-r), and Aₗ(−r^{2ˡ}) for l = 1, ..., d-1, where the Aₗ are the fold
* polynomials.
*
* @param A_0_pos A₀₊
* @param A_0_neg A₀₋
* @param fold_polynomials Aₗ, l = 1, ..., d-1
* @param r_challenge
* @return std::vector<typename GeminiProver_<Curve>::Claim> d+1 univariate opening claims
*/
template <typename Curve>
std::vector<typename GeminiProver_<Curve>::Claim> GeminiProver_<Curve>::construct_univariate_opening_claims(
const size_t log_n,
Polynomial&& A_0_pos,
Polynomial&& A_0_neg,
std::vector<Polynomial>&& fold_polynomials,
const Fr& r_challenge)
{
std::vector<Claim> claims;

// Compute evaluation of partially evaluated batch polynomial (positive) A₀₊(r)
Fr a_0_pos = A_0_pos.evaluate(r_challenge);
claims.emplace_back(Claim{ A_0_pos, { r_challenge, a_0_pos } });
// Compute evaluation of partially evaluated batch polynomial (negative) A₀₋(-r)
Fr a_0_neg = A_0_neg.evaluate(-r_challenge);
claims.emplace_back(Claim{ A_0_neg, { -r_challenge, a_0_neg } });

// Compute univariate opening queries rₗ = r^{2ˡ} for l = 0, 1, ..., m-1
std::vector<Fr> r_squares = gemini::powers_of_evaluation_challenge(r_challenge, log_n);

// Compute the remaining m opening pairs {−r^{2ˡ}, Aₗ(−r^{2ˡ})}, l = 1, ..., m-1.
for (size_t l = 0; l < log_n - 1; ++l) {
Fr evaluation = fold_polynomials[l].evaluate(-r_squares[l + 1]);
claims.emplace_back(Claim{ fold_polynomials[l], { -r_squares[l + 1], evaluation } });
}

return opening_claims;
return claims;
};

} // namespace bb
Loading
Loading