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

feat: k-shifts #11663

Merged
merged 20 commits into from
Feb 5, 2025
Merged
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
2 changes: 2 additions & 0 deletions barretenberg/cpp/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 18)
# We target clang18 and need this, eventually warning should be fixed or this will be unconditional.
add_compile_options(-Wno-vla-cxx-extension)
# This gets in the way of a valid designated initializer pattern (i.e. MyClass my_class{ .my_member = init_value })
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added after a discussion with Adam. IIUC this is part of Charlies incoming PR anyway.

add_compile_options(-Wno-missing-field-initializers)
endif()
endif()

Expand Down
128 changes: 128 additions & 0 deletions barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#pragma once
#include "barretenberg/common/ref_vector.hpp"
#include <optional>

namespace bb {

/**
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class was added in a prior PR, I'm just moving it to its own file. The only changes are those associated with the right shift by k claims.

* @brief Logic to support batching opening claims for unshifted and shifted polynomials in Shplemini
* @details Stores references to the commitments/evaluations of unshifted and shifted polynomials to be batched
* opened via Shplemini. Aggregates the commitments and batching scalars for each batch into the corresponding
* containers for Shplemini. Computes the batched evaluation. Contains logic for computing the per-batch scalars
* used to batch each set of claims (see details below).
* @note This class performs the actual batching of the evaluations but not of the commitments. The latter are
* simply appended to a larger container, along with the scalars used to batch them. This is because Shplemini
* is optimized to perform a single batch mul that includes all commitments from each stage of the PCS. See
* description of ShpleminiVerifier for more details.
*
*/
template <typename Curve> struct ClaimBatcher_ {
using Fr = typename Curve::ScalarField;
using Commitment = typename Curve::AffineElement;

struct Batch {
RefVector<Commitment> commitments;
RefVector<Fr> evaluations;
// scalar used for batching the claims, excluding the power of batching challenge \rho
Fr scalar = 0;
};

std::optional<Batch> unshifted; // commitments and evaluations of unshifted polynomials
std::optional<Batch> shifted; // commitments of to-be-shifted-by-1 polys, evals of their shifts
std::optional<Batch> right_shifted_by_k; // commitments of to-be-right-shifted-by-k polys, evals of their shifts

Batch get_unshifted() { return (unshifted) ? *unshifted : Batch{}; }
Batch get_shifted() { return (shifted) ? *shifted : Batch{}; }
Batch get_right_shifted_by_k() { return (right_shifted_by_k) ? *right_shifted_by_k : Batch{}; }

size_t k_shift_magnitude = 0; // magnitude of right-shift-by-k (assumed even)

Fr get_unshifted_batch_scalar() const { return unshifted ? unshifted->scalar : Fr{ 0 }; }

/**
* @brief Compute scalars used to batch each set of claims, excluding contribution from batching challenge \rho
* @details Computes scalars s_0, s_1, s_2 given by
* \f[
* - s_0 = \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right) \f],
* - s_1 = \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right)
* - s_2 = r^{k} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right)
* \f]
* where the scalars used to batch the claims are given by
* \f[
* \left(
* - s_0,
* \ldots,
* - \rho^{i+k-1} \times s_0,
* - \rho^{i+k} \times s_1,
* \ldots,
* - \rho^{k+m-1} \times s_1
* \right)
* \f]
*
* @param inverse_vanishing_eval_pos 1/(z-r)
* @param inverse_vanishing_eval_neg 1/(z+r)
* @param nu_challenge ν (shplonk batching challenge)
* @param r_challenge r (gemini evaluation challenge)
*/
void compute_scalars_for_each_batch(const Fr& inverse_vanishing_eval_pos,
const Fr& inverse_vanishing_eval_neg,
const Fr& nu_challenge,
const Fr& r_challenge)
{
if (unshifted) {
// (1/(z−r) + ν/(z+r))
unshifted->scalar = inverse_vanishing_eval_pos + nu_challenge * inverse_vanishing_eval_neg;
}
if (shifted) {
// r⁻¹ ⋅ (1/(z−r) − ν/(z+r))
shifted->scalar =
r_challenge.invert() * (inverse_vanishing_eval_pos - nu_challenge * inverse_vanishing_eval_neg);
}
if (right_shifted_by_k) {
// r^k ⋅ (1/(z−r) + ν/(z+r))
right_shifted_by_k->scalar = r_challenge.pow(k_shift_magnitude) *
(inverse_vanishing_eval_pos + nu_challenge * inverse_vanishing_eval_neg);
}
}

/**
* @brief Append the commitments and scalars from each batch of claims to the Shplemini batch mul input vectors;
* update the batched evaluation and the running batching challenge (power of rho) in place.
*
* @param commitments commitment inputs to the single Shplemini batch mul
* @param scalars scalar inputs to the single Shplemini batch mul
* @param batched_evaluation running batched evaluation of the committed multilinear polynomials
* @param rho multivariate batching challenge \rho
* @param rho_power current power of \rho used in the batching scalar
*/
void update_batch_mul_inputs_and_batched_evaluation(std::vector<Commitment>& commitments,
std::vector<Fr>& scalars,
Fr& batched_evaluation,
const Fr& rho,
Fr& rho_power)
{
// Append the commitments/scalars from a given batch to the corresponding containers; update the batched
// evaluation and the running batching challenge in place
auto aggregate_claim_data_and_update_batched_evaluation = [&](const Batch& batch, Fr& rho_power) {
for (auto [commitment, evaluation] : zip_view(batch.commitments, batch.evaluations)) {
commitments.emplace_back(std::move(commitment));
scalars.emplace_back(-batch.scalar * rho_power);
batched_evaluation += evaluation * rho_power;
rho_power *= rho;
}
};

// Incorporate the claim data from each batch of claims that is present
if (unshifted) {
aggregate_claim_data_and_update_batched_evaluation(*unshifted, rho_power);
}
if (shifted) {
aggregate_claim_data_and_update_batched_evaluation(*shifted, rho_power);
}
if (right_shifted_by_k) {
aggregate_claim_data_and_update_batched_evaluation(*right_shifted_by_k, rho_power);
}
}
};

} // namespace bb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "barretenberg/commitment_schemes/claim.hpp"
#include "barretenberg/commitment_schemes/claim_batcher.hpp"
#include "barretenberg/polynomials/polynomial.hpp"
#include "barretenberg/transcript/transcript.hpp"

Expand Down Expand Up @@ -104,11 +105,12 @@ template <typename Curve> class GeminiProver_ {
* @brief Class responsible for computation of the batched multilinear polynomials required by the Gemini protocol
* @details Opening multivariate polynomials using Gemini requires the computation of three batched polynomials. The
* first, here denoted A₀, is a linear combination of all polynomials to be opened. If we denote the linear
* combinations (based on challenge rho) of the unshifted and the to-be-shited-by-1 polynomials by F and G,
* respectively, then A₀ = F + G/X. This polynomial is "folded" in Gemini to produce d-1 univariate polynomials
* Fold_i, i = 1, ..., d-1. The second and third are the partially evaluated batched polynomials A₀₊ = F + G/r, and
* A₀₋ = F - G/r. These are required in order to prove the opening of shifted polynomials G_i/X from the commitments
* to their unshifted counterparts G_i.
* combinations (based on challenge rho) of the unshifted, to-be-shifted-by-1, and to-be-right-shifted-by-k
* polynomials by F, G, and H respectively, then A₀ = F + G/X + X^k*H. (Note: 'k' is assumed even and thus a factor
* (-1)^k in not needed for the evaluation at -r). This polynomial is "folded" in Gemini to produce d-1 univariate
* polynomials Fold_i, i = 1, ..., d-1. The second and third are the partially evaluated batched polynomials A₀₊ = F
* + G/r + r^K*H, and A₀₋ = F - G/r + r^K*H. These are required in order to prove the opening of shifted polynomials
* G_i/X, X^k*H_i and from the commitments to their unshifted counterparts G_i and H_i.
* @note TODO(https://github.com/AztecProtocol/barretenberg/issues/1223): There are certain operations herein that
* could be made more efficient by e.g. reusing already initialized polynomials, possibly at the expense of clarity.
*/
Expand All @@ -122,9 +124,13 @@ template <typename Curve> class GeminiProver_ {

RefVector<Polynomial> unshifted; // set of unshifted polynomials
RefVector<Polynomial> to_be_shifted_by_one; // set of polynomials to be left shifted by 1
RefVector<Polynomial> to_be_shifted_by_k; // set of polynomials to be right shifted by k

size_t k_shift_magnitude = 0; // magnitude of right-shift-by-k (assumed even)

Polynomial batched_unshifted; // linear combination of unshifted polynomials
Polynomial batched_to_be_shifted_by_one; // linear combination of to-be-shifted polynomials
Polynomial batched_to_be_shifted_by_k; // linear combination of to-be-shifted-by-k polynomials

public:
PolynomialBatcher(const size_t full_batched_size)
Expand All @@ -135,10 +141,17 @@ template <typename Curve> class GeminiProver_ {

bool has_unshifted() const { return unshifted.size() > 0; }
bool has_to_be_shifted_by_one() const { return to_be_shifted_by_one.size() > 0; }
bool has_to_be_shifted_by_k() const { return to_be_shifted_by_k.size() > 0; }

// Set references to the polynomials to be batched
void set_unshifted(RefVector<Polynomial> polynomials) { unshifted = polynomials; }
void set_to_be_shifted_by_one(RefVector<Polynomial> polynomials) { to_be_shifted_by_one = polynomials; }
void set_to_be_shifted_by_k(RefVector<Polynomial> polynomials, const size_t shift_magnitude)
{
ASSERT(k_shift_magnitude % 2 == 0); // k must be even for the formulas herein to be valid
to_be_shifted_by_k = polynomials;
k_shift_magnitude = shift_magnitude;
}

// Initialize the random polynomial used to add randomness to the batched polynomials for ZK
void set_random_polynomial(Polynomial&& random)
Expand Down Expand Up @@ -169,19 +182,26 @@ template <typename Curve> class GeminiProver_ {

// if necessary, add randomness to the full batched polynomial for ZK
if (has_random_polynomial) {
full_batched += random_polynomial;
full_batched += random_polynomial; // A₀ += rand
}

// compute the linear combination F of the unshifted polynomials
if (has_unshifted()) {
batch(batched_unshifted, unshifted);
full_batched += batched_unshifted; // A₀ = F
full_batched += batched_unshifted; // A₀ += F
}

// compute the linear combination G of the to-be-shifted polynomials
if (has_to_be_shifted_by_one()) {
batch(batched_to_be_shifted_by_one, to_be_shifted_by_one);
full_batched += batched_to_be_shifted_by_one.shifted(); // A₀ = F + G/X
full_batched += batched_to_be_shifted_by_one.shifted(); // A₀ += G/X
}

// compute the linear combination H of the to-be-shifted-by-k polynomials
if (has_to_be_shifted_by_k()) {
batched_to_be_shifted_by_k = Polynomial(full_batched_size - k_shift_magnitude, full_batched_size, 0);
batch(batched_to_be_shifted_by_k, to_be_shifted_by_k);
full_batched += batched_to_be_shifted_by_k.right_shifted(k_shift_magnitude); // A₀ += X^k * H
}

return full_batched;
Expand All @@ -206,14 +226,20 @@ template <typename Curve> class GeminiProver_ {
A_0_pos += batched_unshifted; // A₀₊ += F
}

if (has_to_be_shifted_by_k()) {
Fr r_pow_k = r_challenge.pow(k_shift_magnitude); // r^k
batched_to_be_shifted_by_k *= r_pow_k;
A_0_pos += batched_to_be_shifted_by_k; // A₀₊ += r^k * H
}

Polynomial A_0_neg = A_0_pos;

if (has_to_be_shifted_by_one()) {
Fr r_inv = r_challenge.invert(); // r⁻¹
batched_to_be_shifted_by_one *= r_inv; // G = G/r

A_0_pos += batched_to_be_shifted_by_one; // A₀₊ = F + G/r
A_0_neg -= batched_to_be_shifted_by_one; // A₀₋ = F - G/r
A_0_pos += batched_to_be_shifted_by_one; // A₀₊ += G/r
A_0_neg -= batched_to_be_shifted_by_one; // A₀₋ -= G/r
}

return { A_0_pos, A_0_neg };
Expand Down Expand Up @@ -252,6 +278,7 @@ template <typename Curve> class GeminiVerifier_ {
using Fr = typename Curve::ScalarField;
using GroupElement = typename Curve::Element;
using Commitment = typename Curve::AffineElement;
using ClaimBatcher = ClaimBatcher_<Curve>;

public:
/**
Expand All @@ -268,10 +295,7 @@ template <typename Curve> class GeminiVerifier_ {
*/
static std::vector<OpeningClaim<Curve>> reduce_verification(
std::span<Fr> multilinear_challenge,
RefSpan<Fr> unshifted_evaluations,
RefSpan<Fr> shifted_evaluations,
RefSpan<Commitment> unshifted_commitments,
RefSpan<Commitment> to_be_shifted_commitments,
ClaimBatcher& claim_batcher,
auto& transcript,
const std::vector<RefVector<Commitment>>& concatenation_group_commitments = {},
RefSpan<Fr> concatenated_evaluations = {})
Expand All @@ -288,13 +312,15 @@ template <typename Curve> class GeminiVerifier_ {

Fr batched_evaluation = Fr(0);
Fr batching_scalar = Fr(1);
for (auto [eval, comm] : zip_view(unshifted_evaluations, unshifted_commitments)) {
for (auto [eval, comm] :
zip_view(claim_batcher.get_unshifted().evaluations, claim_batcher.get_unshifted().commitments)) {
batched_evaluation += eval * batching_scalar;
batched_commitment_unshifted += comm * batching_scalar;
batching_scalar *= rho;
}

for (auto [eval, comm] : zip_view(shifted_evaluations, to_be_shifted_commitments)) {
for (auto [eval, comm] :
zip_view(claim_batcher.get_shifted().evaluations, claim_batcher.get_shifted().commitments)) {
batched_evaluation += eval * batching_scalar;
batched_commitment_to_be_shifted += comm * batching_scalar;
batching_scalar *= rho;
Expand Down
Loading