Skip to content

Commit 3827e14

Browse files
iakovenkosledwards2225notnotraju
authored
feat!: optimized Poseidon2 (#22652)
More efficient Poseidon2 thanks to quad compression trick --------- Co-authored-by: ledwards2225 <l.edwards.d@gmail.com> Co-authored-by: notnotraju <raju@aztec.foundation>
1 parent 8ee7069 commit 3827e14

56 files changed

Lines changed: 6119 additions & 3175 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ script_path="$root/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_cha
2121
# - Generate a hash for versioning: sha256sum bb-chonk-inputs.tar.gz
2222
# - Upload the compressed results: aws s3 cp bb-chonk-inputs.tar.gz s3://aztec-ci-artifacts/protocol/bb-chonk-inputs-[hash(0:8)].tar.gz
2323
# Note: In case of the "Test suite failed to run ... Unexpected token 'with' " error, need to run: docker pull aztecprotocol/build:3.0
24-
pinned_short_hash="341842bf"
24+
pinned_short_hash="20c388cc"
2525
pinned_chonk_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-chonk-inputs-${pinned_short_hash}.tar.gz"
2626

2727
function update_pinned_hash_in_script {

barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ BENCHMARK(execute_relation_for_univariates<UltraFlavor, UltraPermutationRelation
6161
BENCHMARK(execute_relation_for_univariates<MegaFlavor, EccOpQueueRelation<Fr>>);
6262
BENCHMARK(execute_relation_for_univariates<MegaFlavor, DatabusLookupRelation<Fr>>);
6363
BENCHMARK(execute_relation_for_univariates<MegaFlavor, Poseidon2ExternalRelation<Fr>>);
64-
BENCHMARK(execute_relation_for_univariates<MegaFlavor, Poseidon2InternalRelation<Fr>>);
64+
BENCHMARK(execute_relation_for_univariates<MegaFlavor, Poseidon2QuadInternalRelation<Fr>>);
65+
BENCHMARK(execute_relation_for_univariates<MegaFlavor, Poseidon2QuadInternalTerminalRelation<Fr>>);
66+
BENCHMARK(execute_relation_for_univariates<MegaFlavor, Poseidon2TransitionEntryRelation<Fr>>);
6567

6668
// Ultra relations (verifier work)
6769
BENCHMARK(execute_relation_for_values<UltraFlavor, ArithmeticRelation<Fr>>);
@@ -76,7 +78,9 @@ BENCHMARK(execute_relation_for_values<UltraFlavor, UltraPermutationRelation<Fr>>
7678
BENCHMARK(execute_relation_for_values<MegaFlavor, EccOpQueueRelation<Fr>>);
7779
BENCHMARK(execute_relation_for_values<MegaFlavor, DatabusLookupRelation<Fr>>);
7880
BENCHMARK(execute_relation_for_values<MegaFlavor, Poseidon2ExternalRelation<Fr>>);
79-
BENCHMARK(execute_relation_for_values<MegaFlavor, Poseidon2InternalRelation<Fr>>);
81+
BENCHMARK(execute_relation_for_values<MegaFlavor, Poseidon2QuadInternalRelation<Fr>>);
82+
BENCHMARK(execute_relation_for_values<MegaFlavor, Poseidon2QuadInternalTerminalRelation<Fr>>);
83+
BENCHMARK(execute_relation_for_values<MegaFlavor, Poseidon2TransitionEntryRelation<Fr>>);
8084

8185
// Translator VM
8286
BENCHMARK(execute_relation_for_values<TranslatorFlavor, TranslatorDecompositionRelation<Fr>>);

barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/mega_honk.bench.cpp

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <benchmark/benchmark.h>
22

33
#include "barretenberg/benchmark/ultra_bench/mock_circuits.hpp"
4+
#include "barretenberg/common/bb_bench.hpp"
45
#include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp"
56

67
using namespace benchmark;
@@ -41,7 +42,37 @@ static void get_row_power_of_2(State& state) noexcept
4142
}
4243
}
4344

45+
/**
46+
* @brief Benchmark: Mega Honk proof of a single poseidon2 hash over a vector of state.range(0) elements.
47+
*/
48+
static void construct_proof_megahonk_poseidon2_hash(State& state) noexcept
49+
{
50+
const auto num_inputs = static_cast<size_t>(state.range(0));
51+
52+
MegaCircuitBuilder builder;
53+
bb::generate_poseidon2_hash_test_circuit<MegaCircuitBuilder>(builder, num_inputs);
54+
auto instance = std::make_shared<ProverInstance_<MegaFlavor>>(builder);
55+
info("construct_proof_megahonk_poseidon2_hash: num_inputs=",
56+
num_inputs,
57+
", actual_gates=",
58+
builder.num_gates(),
59+
", dyadic_size=",
60+
instance->dyadic_size());
61+
62+
bb::mock_circuits::construct_proof_with_specified_num_iterations<MegaProver>(
63+
state, &bb::generate_poseidon2_hash_test_circuit<MegaCircuitBuilder>, num_inputs);
64+
}
65+
4466
// Define benchmarks
67+
// Sweep input sizes so dyadic domain ranges 2^15..2^19 (Mega: ~12 gates/input).
68+
BENCHMARK(construct_proof_megahonk_poseidon2_hash)
69+
->Arg(1500)
70+
->Arg(3000)
71+
->Arg(6000)
72+
->Arg(12000)
73+
->Arg(24000)
74+
->Arg(50000)
75+
->Unit(kMillisecond);
4576

4677
// This exists due to an issue where get_row was blowing up in time
4778
BENCHMARK_CAPTURE(construct_proof_megahonk, sha256, &generate_sha256_test_circuit<MegaCircuitBuilder>)
@@ -61,4 +92,18 @@ BENCHMARK(construct_proof_megahonk_power_of_2)
6192
->DenseRange(15, 20)
6293
->Unit(kMillisecond);
6394

64-
BENCHMARK_MAIN();
95+
int main(int argc, char** argv)
96+
{
97+
bb::detail::use_bb_bench = true;
98+
99+
::benchmark::Initialize(&argc, argv);
100+
if (::benchmark::ReportUnrecognizedArguments(argc, argv))
101+
return 1;
102+
::benchmark::RunSpecifiedBenchmarks();
103+
::benchmark::Shutdown();
104+
105+
std::cout << "\n=== Detailed BB_BENCH Profiling Stats ===\n";
106+
bb::detail::GLOBAL_BENCH_STATS.print_aggregate_counts_hierarchical(std::cout);
107+
108+
return 0;
109+
}

barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk.bench.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,38 @@ static void construct_proof_ultrahonk_1M_gates_dyadic_2_21(State& state) noexcep
8888
state, &bb::mock_circuits::generate_basic_arithmetic_circuit_with_target_gates<UltraCircuitBuilder>, num_gates);
8989
}
9090

91+
/**
92+
* @brief Benchmark: Ultra Honk proof of a single poseidon2 hash over a vector of state.range(0) elements.
93+
*/
94+
static void construct_proof_ultrahonk_poseidon2_hash(State& state) noexcept
95+
{
96+
const auto num_inputs = static_cast<size_t>(state.range(0));
97+
98+
UltraCircuitBuilder builder;
99+
bb::generate_poseidon2_hash_test_circuit<UltraCircuitBuilder>(builder, num_inputs);
100+
auto instance = std::make_shared<ProverInstance_<UltraFlavor>>(builder);
101+
info("construct_proof_ultrahonk_poseidon2_hash: num_inputs=",
102+
num_inputs,
103+
", actual_gates=",
104+
builder.num_gates(),
105+
", dyadic_size=",
106+
instance->dyadic_size());
107+
108+
bb::mock_circuits::construct_proof_with_specified_num_iterations<UltraProver>(
109+
state, &bb::generate_poseidon2_hash_test_circuit<UltraCircuitBuilder>, num_inputs);
110+
}
111+
91112
// Define benchmarks
113+
// Sweep input sizes so dyadic domain ranges 2^15..2^19 (Ultra: ~25 gates/input).
114+
BENCHMARK(construct_proof_ultrahonk_poseidon2_hash)
115+
->Arg(750)
116+
->Arg(1500)
117+
->Arg(3000)
118+
->Arg(6000)
119+
->Arg(12000)
120+
->Arg(50000)
121+
->Unit(kMillisecond);
122+
92123
BENCHMARK_CAPTURE(construct_proof_ultrahonk, sha256, &generate_sha256_test_circuit<UltraCircuitBuilder>)
93124
->Unit(kMillisecond);
94125
BENCHMARK_CAPTURE(construct_proof_ultrahonk,

barretenberg/cpp/src/barretenberg/boomerang_value_detection/gate_patterns.hpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,81 @@ inline const GatePattern POSEIDON2_EXTERNAL = { .name = "poseidon2_external",
322322
{ Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
323323
} };
324324

325+
// ============================================================================
326+
// Poseidon2 Initial External Pattern (from poseidon2_initial_external_relation.hpp)
327+
//
328+
// All 4 current wires and all 4 shifted wires are constrained
329+
//
330+
// gate_selector = q_poseidon2_external_initial
331+
// ============================================================================
332+
333+
inline const GatePattern POSEIDON2_INITIAL_EXTERNAL = { .name = "poseidon2_initial_external",
334+
.wires = {
335+
{ Wire::W_L, [](const Selectors&) { return true; } },
336+
{ Wire::W_R, [](const Selectors&) { return true; } },
337+
{ Wire::W_O, [](const Selectors&) { return true; } },
338+
{ Wire::W_4, [](const Selectors&) { return true; } },
339+
{ Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
340+
{ Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
341+
{ Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
342+
{ Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
343+
} };
344+
345+
// ============================================================================
346+
// Poseidon2 Quad-Internal Pattern (from poseidon2_quad_internal_relation.hpp)
347+
//
348+
// gate_selector = q_poseidon2_quad_internal
349+
// ============================================================================
350+
351+
inline const GatePattern POSEIDON2_QUAD_INTERNAL = { .name = "poseidon2_quad_internal",
352+
.wires = {
353+
{ Wire::W_L, [](const Selectors&) { return true; } },
354+
{ Wire::W_R, [](const Selectors&) { return true; } },
355+
{ Wire::W_O, [](const Selectors&) { return true; } },
356+
{ Wire::W_4, [](const Selectors&) { return true; } },
357+
{ Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
358+
{ Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
359+
{ Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
360+
{ Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
361+
} };
362+
363+
// ============================================================================
364+
// Poseidon2 Quad-Internal Terminal Pattern
365+
// (from poseidon2_quad_internal_terminal_relation.hpp)
366+
//
367+
// gate_selector = q_poseidon2_quad_internal_terminal
368+
// ============================================================================
369+
370+
inline const GatePattern
371+
POSEIDON2_QUAD_INTERNAL_TERMINAL = { .name = "poseidon2_quad_internal_terminal",
372+
.wires = {
373+
{ Wire::W_L, [](const Selectors&) { return true; } },
374+
{ Wire::W_R, [](const Selectors&) { return true; } },
375+
{ Wire::W_O, [](const Selectors&) { return true; } },
376+
{ Wire::W_4, [](const Selectors&) { return true; } },
377+
{ Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
378+
{ Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
379+
{ Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
380+
{ Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
381+
} };
382+
383+
// ============================================================================
384+
// Poseidon2 Transition Entry Pattern (from poseidon2_transition_entry_relation.hpp)
385+
//
386+
// gate_selector = q_poseidon2_transition_entry
387+
// ============================================================================
388+
389+
inline const GatePattern POSEIDON2_TRANSITION_ENTRY = { .name = "poseidon2_transition_entry",
390+
.wires = {
391+
{ Wire::W_L, [](const Selectors&) { return true; } },
392+
{ Wire::W_R, [](const Selectors&) { return true; } },
393+
{ Wire::W_O, [](const Selectors&) { return true; } },
394+
{ Wire::W_4, [](const Selectors&) { return true; } },
395+
{ Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
396+
{ Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
397+
{ Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
398+
} };
399+
325400
// ============================================================================
326401
// Databus Pattern (from databus_lookup_relation.hpp)
327402
//

barretenberg/cpp/src/barretenberg/boomerang_value_detection/gate_patterns.test.cpp

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111

1212
#include "gate_patterns.hpp"
1313
#include "barretenberg/flavor/mega_flavor.hpp"
14+
#include "barretenberg/flavor/ultra_flavor.hpp"
1415
#include "barretenberg/relations/databus_lookup_relation.hpp"
1516
#include "barretenberg/relations/delta_range_constraint_relation.hpp"
1617
#include "barretenberg/relations/elliptic_relation.hpp"
1718
#include "barretenberg/relations/logderiv_lookup_relation.hpp"
1819
#include "barretenberg/relations/memory_relation.hpp"
1920
#include "barretenberg/relations/non_native_field_relation.hpp"
2021
#include "barretenberg/relations/poseidon2_external_relation.hpp"
22+
#include "barretenberg/relations/poseidon2_initial_external_relation.hpp"
2123
#include "barretenberg/relations/poseidon2_internal_relation.hpp"
2224
#include "barretenberg/relations/relation_parameters.hpp"
2325
#include "barretenberg/relations/ultra_arithmetic_relation.hpp"
@@ -30,16 +32,16 @@ using namespace bb::gate_patterns;
3032
using FF = fr;
3133
using Entities = MegaFlavor::AllValues;
3234

33-
Entities get_random_entities()
35+
template <typename E> E get_random_entities()
3436
{
35-
Entities entities;
37+
E entities;
3638
for (auto& field : entities.get_all()) {
3739
field = FF::random_element();
3840
}
3941
return entities;
4042
}
4143

42-
FF& get_wire(Entities& entities, Wire wire)
44+
template <typename E> FF& get_wire(E& entities, Wire wire)
4345
{
4446
switch (wire) {
4547
case Wire::W_L:
@@ -62,7 +64,7 @@ FF& get_wire(Entities& entities, Wire wire)
6264
__builtin_unreachable();
6365
}
6466

65-
Selectors make_selectors(const Entities& entities, int64_t gate_selector_value)
67+
template <typename E> Selectors make_selectors(const E& entities, int64_t gate_selector_value)
6668
{
6769
return Selectors{
6870
.gate_selector = gate_selector_value,
@@ -94,8 +96,8 @@ std::set<Wire> get_pattern_wires(const GatePattern& pattern, const Selectors& se
9496
*
9597
* This is the ground truth: perturb each wire and see if the output changes.
9698
*/
97-
template <typename Relation>
98-
std::set<Wire> get_actually_constrained_wires(const Entities& entities, const auto& parameters)
99+
template <typename Relation, typename E>
100+
std::set<Wire> get_actually_constrained_wires(const E& entities, const auto& parameters)
99101
{
100102
std::set<Wire> constrained;
101103

@@ -112,7 +114,7 @@ std::set<Wire> get_actually_constrained_wires(const Entities& entities, const au
112114
Wire::W_R_SHIFT,
113115
Wire::W_O_SHIFT,
114116
Wire::W_4_SHIFT }) {
115-
Entities perturbed = entities;
117+
E perturbed = entities;
116118
get_wire(perturbed, wire) += FF::random_element();
117119

118120
typename Relation::SumcheckArrayOfValuesOverSubrelations perturbed_result{};
@@ -131,17 +133,18 @@ std::set<Wire> get_actually_constrained_wires(const Entities& entities, const au
131133
*
132134
* @param configure_selectors Lambda that configures entity selectors and returns the gate selector field value
133135
*/
134-
template <typename Relation> void verify_pattern(const GatePattern& pattern, auto configure_selectors)
136+
template <typename Relation, typename E = Entities>
137+
void verify_pattern(const GatePattern& pattern, auto configure_selectors)
135138
{
136-
Entities entities = get_random_entities();
139+
E entities = get_random_entities<E>();
137140
FF gate_selector = configure_selectors(entities);
138141
int64_t gate_selector_value = static_cast<int64_t>(uint64_t(gate_selector));
139142

140143
Selectors selectors = make_selectors(entities, gate_selector_value);
141144
auto pattern_claims = get_pattern_wires(pattern, selectors);
142145

143146
auto parameters = RelationParameters<FF>::get_random();
144-
auto actually_constrained = get_actually_constrained_wires<Relation>(entities, parameters);
147+
auto actually_constrained = get_actually_constrained_wires<Relation, E>(entities, parameters);
145148

146149
EXPECT_EQ(actually_constrained, pattern_claims);
147150
}
@@ -288,8 +291,11 @@ TEST(PatternTest, MemoryRamConsistency)
288291

289292
TEST(PatternTest, Poseidon2Internal)
290293
{
291-
verify_pattern<Poseidon2InternalRelation<FF>>(POSEIDON2_INTERNAL,
292-
[](Entities& e) { return e.q_poseidon2_internal = FF(1); });
294+
// q_poseidon2_internal lives on UltraFlavor only; MegaFlavor covers all internal rounds via the
295+
// compressed quad-internal block.
296+
using UltraEntities = UltraFlavor::AllValues;
297+
verify_pattern<Poseidon2InternalRelation<FF>, UltraEntities>(
298+
POSEIDON2_INTERNAL, [](UltraEntities& e) { return e.q_poseidon2_internal = FF(1); });
293299
}
294300

295301
TEST(PatternTest, Poseidon2External)
@@ -298,6 +304,12 @@ TEST(PatternTest, Poseidon2External)
298304
[](Entities& e) { return e.q_poseidon2_external = FF(1); });
299305
}
300306

307+
TEST(PatternTest, Poseidon2InitialExternal)
308+
{
309+
verify_pattern<Poseidon2InitialExternalRelation<FF>>(
310+
POSEIDON2_INITIAL_EXTERNAL, [](Entities& e) { return e.q_poseidon2_external_initial = FF(1); });
311+
}
312+
301313
TEST(PatternTest, LookupBasic)
302314
{
303315
verify_pattern<LogDerivLookupRelation<FF>>(LOOKUP, [](Entities& e) {
@@ -363,7 +375,7 @@ TEST(PatternTest, DetectOverConstrained)
363375
} };
364376

365377
// q_arith=3 disables mul term, q_2=0 means w_r has no linear term, so w_r is unconstrained
366-
Entities entities = get_random_entities();
378+
Entities entities = get_random_entities<Entities>();
367379
entities.q_arith = FF(3);
368380
entities.q_m = FF(1);
369381
entities.q_l = FF(1);
@@ -373,7 +385,7 @@ TEST(PatternTest, DetectOverConstrained)
373385
auto pattern_claims = get_pattern_wires(OVERCONSTRAINED_PATTERN, selectors);
374386
auto correct_claims = get_pattern_wires(ARITHMETIC, selectors);
375387
auto parameters = RelationParameters<FF>::get_random();
376-
auto actually_constrained = get_actually_constrained_wires<ArithmeticRelation<FF>>(entities, parameters);
388+
auto actually_constrained = get_actually_constrained_wires<ArithmeticRelation<FF>, Entities>(entities, parameters);
377389

378390
EXPECT_TRUE(pattern_claims.contains(Wire::W_R)) << "Over-constrained pattern claims W_R";
379391
EXPECT_FALSE(actually_constrained.contains(Wire::W_R)) << "Relation does not constrain W_R in this config";
@@ -402,15 +414,15 @@ TEST(PatternTest, DetectUnderConstrained)
402414
} };
403415

404416
// RAM consistency check: q_3 != 0
405-
Entities entities = get_random_entities();
417+
Entities entities = get_random_entities<Entities>();
406418
entities.q_memory = FF(1);
407419
entities.q_o = FF(1); // q_3
408420

409421
Selectors selectors = make_selectors(entities, 1);
410422
auto pattern_claims = get_pattern_wires(UNDERCONSTRAINED_PATTERN, selectors);
411423
auto correct_claims = get_pattern_wires(MEMORY, selectors);
412424
auto parameters = RelationParameters<FF>::get_random();
413-
auto actually_constrained = get_actually_constrained_wires<MemoryRelation<FF>>(entities, parameters);
425+
auto actually_constrained = get_actually_constrained_wires<MemoryRelation<FF>, Entities>(entities, parameters);
414426

415427
EXPECT_FALSE(pattern_claims.contains(Wire::W_L)) << "Under-constrained pattern missing W_L";
416428
EXPECT_FALSE(pattern_claims.contains(Wire::W_R)) << "Under-constrained pattern missing W_R";

0 commit comments

Comments
 (0)