Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
poseidon2_quad_internal_relation.hpp
Go to the documentation of this file.
1#pragma once
4#include "relation_types.hpp"
5
6namespace bb {
7
61template <typename FF_> class Poseidon2QuadInternalRelationImpl {
62 public:
63 using FF = FF_;
65
66 static constexpr std::array<size_t, 4> SUBRELATION_PARTIAL_LENGTHS{
67 7, // A_0: out_0 - w_l_shift
68 7, // A_1: forward Vandermonde row 1
69 7, // A_2: forward Vandermonde row 2
70 7, // A_3: forward Vandermonde row 3
71 };
72
73 // Constants used by the shifted-row Vandermonde RHS reconstruction.
74 static constexpr fr D1 = QuadParams::D1;
75 static constexpr fr SIGMA_PLUS_2 = QuadParams::SIGMA + fr(2); // Σ + 2
76 static constexpr fr B3_U0_COEF = SIGMA_PLUS_2 * D1 - QuadParams::SIGMA - fr(3); // (Σ+2) D_1 - Σ - 3
77 static constexpr fr D1_MINUS_3 = D1 - fr(3); // D_1 - 3
78
82 template <typename AllEntities> inline static bool skip(const AllEntities& in)
83 {
84 return in.q_poseidon2_quad_internal.is_zero();
85 }
86
87 template <typename ContainerOverSubrelations, typename AllEntities, typename Parameters>
88 void static accumulate(ContainerOverSubrelations& evals,
89 const AllEntities& in,
90 const Parameters&,
91 const FF& scaling_factor)
92 {
94 using CoeffAcc = typename Accumulator::CoefficientAccumulator;
95
96 // Wire values: current row's committed lane-0 chain (state[0] at the four rounds covered
97 // by this quad).
98 const auto w_l = CoeffAcc(in.w_l); // s_0^(0): row's lane-0 at round 4i
99 const auto w_r = CoeffAcc(in.w_r); // s_0^(1): row's lane-0 at round 4i+1
100 const auto w_o = CoeffAcc(in.w_o); // s_0^(2): row's lane-0 at round 4i+2
101 const auto w_4 = CoeffAcc(in.w_4); // s_0^(3): row's lane-0 at round 4i+3
102
103 // Next row's committed lane-0 chain.
104 const auto w_l_shift = CoeffAcc(in.w_l_shift); // next row's s_0^(0) (= state[0] at round 4(i+1))
105 const auto w_r_shift = CoeffAcc(in.w_r_shift); // next row's s_0^(1)
106 const auto w_o_shift = CoeffAcc(in.w_o_shift); // next row's s_0^(2)
107 const auto w_4_shift = CoeffAcc(in.w_4_shift); // next row's s_0^(3)
108
109 // Round constants (current row), used to compute u_0..u_3.
110 const auto q_l = CoeffAcc(in.q_l); // c_{4i}
111 const auto q_r = CoeffAcc(in.q_r); // c_{4i+1}
112 const auto q_o = CoeffAcc(in.q_o); // c_{4i+2}
113 const auto q_4 = CoeffAcc(in.q_4); // c_{4i+3}
114 // Next quad's first three round constants. Needed to compute u_0', u_1', u_2', which
115 // appear in the b'-formulas (RHS of the cross-row encoding equation). Carried on the
116 // current row in q_m, q_c, q_5 because Mega has no shifted selectors.
117 const auto q_m = CoeffAcc(in.q_m); // c_{4(i+1)}
118 const auto q_c = CoeffAcc(in.q_c); // c_{4(i+1)+1}
119 const auto q_5 = CoeffAcc(in.q_5); // c_{4(i+1)+2}
120
121 const auto q_sel = CoeffAcc(in.q_poseidon2_quad_internal);
122
123 // Helper: compute fifth power as Accumulator (degree 5 in the input wire).
124 auto pow5 = [](const Accumulator& x) -> Accumulator {
125 auto sq = x.sqr();
126 auto quart = sq.sqr();
127 return quart * x;
128 };
129
130 // ── Current row: u_k = (s_0^{(k)} + c_k)^5. The four S-box outputs feed the closed-form
131 // prediction of the row-end state. ──
132 auto u_0 = pow5(Accumulator(w_l + q_l)); // u_0 = (s_0^(0) + c_{4i})^5
133 auto u_1 = pow5(Accumulator(w_r + q_r)); // u_1 = (s_0^(1) + c_{4i+1})^5
134 auto u_2 = pow5(Accumulator(w_o + q_o)); // u_2 = (s_0^(2) + c_{4i+2})^5
135 auto u_3 = pow5(Accumulator(w_4 + q_4)); // u_3 = (s_0^(3) + c_{4i+3})^5
136
137 // ── Next row's S-box outputs. Together with the next row's lane-0 wires they let us
138 // forward-compute V · (next row's hidden lanes) via the b'-formulas, without ever
139 // materializing the hidden lanes themselves. Only three (not four) because the
140 // b'-formulas only depend on u_0', u_1', u_2'. ──
141 auto u_0_next = pow5(Accumulator(w_l_shift + q_m)); // u_0' = (next_s_0^(0) + c_{4(i+1)})^5
142 auto u_1_next = pow5(Accumulator(w_r_shift + q_c)); // u_1' = (next_s_0^(1) + c_{4(i+1)+1})^5
143 auto u_2_next = pow5(Accumulator(w_o_shift + q_5)); // u_2' = (next_s_0^(2) + c_{4(i+1)+2})^5
144 auto u_0_next_D1 = u_0_next * D1; // CSE: D_1 · u_0' is reused in A_1 and A_2
145
146 // Precomputed coefficient vectors. Each is indexed [W_R, W_O, W_4, U_0, U_1, U_2, U_3].
147 const auto& cf0 = QuadParams::tables.closed_form[0]; // out_0 (row-end lane 0)
148 const auto& l0 = QuadParams::tables.forward_vandermonde_lhs[0]; // out_1 + out_2 + out_3
149 const auto& l1 = QuadParams::tables.forward_vandermonde_lhs[1]; // D_2 out_1 + D_3 out_2 + D_4 out_3
150 const auto& l2 = QuadParams::tables.forward_vandermonde_lhs[2]; // D_2² out_1 + D_3² out_2 + D_4² out_3
151
152 // Wire-only parts of the four subrelations. Each subrelation A_K has the form
153 // A_K = (LHS_K computed from current row) - (RHS_K computed from next row).
154 // wpK_full collects all the wire-only terms (no u_*) from BOTH sides at once; the u_*
155 // terms are folded in below in aK_body.
156 // A_0: LHS = out_0, RHS = w_l_shift.
157 // A_1: LHS = out_1 + out_2 + out_3, RHS = b_1' = w_r' - D_1 u_0'.
158 // A_2: LHS = D_2 out_1 + D_3 out_2 + D_4 out_3, RHS = b_2' = w_o' - 2 w_r' + (2 D_1 - 3) u_0' - D_1 u_1'.
159 // A_3: LHS = D_2² out_1 + D_3² out_2 + D_4² out_3, RHS = b_3' = w_4' - w_o' - (Σ+2) w_r' + ...
160 // The LHS wire contributions come from cf0 / l0 / l1 / l2 (forward V applied to predicted
161 // output); the RHS wire contributions are the shifted lane-0 wires multiplied by the
162 // coefficients with which they appear in the b'-formulas above.
163 auto wp0_full = w_r * cf0[0] + w_o * cf0[1] + w_4 * cf0[2] - w_l_shift;
164 auto wp1_full = w_r * l0[0] + w_o * l0[1] + w_4 * l0[2] - w_r_shift;
165 auto wp2_full = w_r * l1[0] + w_o * l1[1] + w_4 * l1[2] - w_o_shift + w_r_shift + w_r_shift;
166 auto wp3_full = w_r * l2[0] + w_o * l2[1] + w_4 * l2[2] - w_4_shift + w_o_shift + w_r_shift * SIGMA_PLUS_2;
167
168 const auto q_times_scaling_m = q_sel * scaling_factor;
169 const auto q_times_scaling = Accumulator(q_times_scaling_m);
170
171 // A_0: out_0 - w_l_shift = 0.
172 auto a0_body = u_0 * cf0[3] + u_1 * cf0[4] + u_2 * cf0[5] + u_3 * cf0[6] + Accumulator(wp0_full);
173 std::get<0>(evals) += q_times_scaling * a0_body;
174
175 // A_1: (out_1 + out_2 + out_3) - b_1_next = 0.
176 auto a1_body = u_0 * l0[3] + u_1 * l0[4] + u_2 * l0[5] + u_3 * l0[6] + u_0_next_D1 + Accumulator(wp1_full);
177 std::get<1>(evals) += q_times_scaling * a1_body;
178
179 // A_2: (D_2 out_1 + D_3 out_2 + D_4 out_3) - b_2_next = 0.
180 auto a2_body = u_0 * l1[3] + u_1 * l1[4] + u_2 * l1[5] + u_3 * l1[6] - (u_0_next_D1 + u_0_next_D1) +
181 (u_0_next + u_0_next + u_0_next) + u_1_next * D1 + Accumulator(wp2_full);
182 std::get<2>(evals) += q_times_scaling * a2_body;
183
184 // A_3: (D_2^2 out_1 + D_3^2 out_2 + D_4^2 out_3) - b_3_next = 0.
185 auto a3_body = u_0 * l2[3] + u_1 * l2[4] + u_2 * l2[5] + u_3 * l2[6] - u_0_next * B3_U0_COEF -
186 u_1_next * D1_MINUS_3 + u_2_next * D1 + Accumulator(wp3_full);
187 std::get<3>(evals) += q_times_scaling * a3_body;
188 }
189};
190
192
193} // namespace bb
K=4 compressed internal-round relation for Poseidon2.
static void accumulate(ContainerOverSubrelations &evals, const AllEntities &in, const Parameters &, const FF &scaling_factor)
static bool skip(const AllEntities &in)
Skip when the selector is identically zero on this row.
static constexpr std::array< size_t, 4 > SUBRELATION_PARTIAL_LENGTHS
A wrapper for Relations to expose methods used by the Sumcheck prover or verifier to add the contribu...
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:155
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13