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 {
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[AllEntities::EntityId::w_l]); // s_0^(0): row's lane-0 at round 4i
99 const auto w_r = CoeffAcc(in[AllEntities::EntityId::w_r]); // s_0^(1): row's lane-0 at round 4i+1
100 const auto w_o = CoeffAcc(in[AllEntities::EntityId::w_o]); // s_0^(2): row's lane-0 at round 4i+2
101 const auto w_4 = CoeffAcc(in[AllEntities::EntityId::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 =
105 CoeffAcc(in[AllEntities::EntityId::w_l_shift]); // next row's s_0^(0) (= state[0] at round 4(i+1))
106 const auto w_r_shift = CoeffAcc(in[AllEntities::EntityId::w_r_shift]); // next row's s_0^(1)
107 const auto w_o_shift = CoeffAcc(in[AllEntities::EntityId::w_o_shift]); // next row's s_0^(2)
108 const auto w_4_shift = CoeffAcc(in[AllEntities::EntityId::w_4_shift]); // next row's s_0^(3)
109
110 // Round constants (current row), used to compute u_0..u_3.
111 const auto q_l = CoeffAcc(in[AllEntities::EntityId::q_l]); // c_{4i}
112 const auto q_r = CoeffAcc(in[AllEntities::EntityId::q_r]); // c_{4i+1}
113 const auto q_o = CoeffAcc(in[AllEntities::EntityId::q_o]); // c_{4i+2}
114 const auto q_4 = CoeffAcc(in[AllEntities::EntityId::q_4]); // c_{4i+3}
115 // Next quad's first three round constants. Needed to compute u_0', u_1', u_2', which
116 // appear in the b'-formulas (RHS of the cross-row encoding equation). Carried on the
117 // current row in q_m, q_c, q_5 because Mega has no shifted selectors.
118 const auto q_m = CoeffAcc(in[AllEntities::EntityId::q_m]); // c_{4(i+1)}
119 const auto q_c = CoeffAcc(in[AllEntities::EntityId::q_c]); // c_{4(i+1)+1}
120 const auto q_5 = CoeffAcc(in[AllEntities::EntityId::q_5]); // c_{4(i+1)+2}
121
122 const auto q_sel = CoeffAcc(in[AllEntities::EntityId::q_poseidon2_quad_internal]);
123
124 // Square the degree-1 pow5 input in the coefficient basis before promoting -- see
125 // UnivariateCoefficientBasis::sqr.
126 auto pow5 = [](const auto& x_m) -> Accumulator {
127 const Accumulator x(x_m);
128 const Accumulator sq(x_m.sqr());
129 return sq.sqr() * x;
130 };
131
132 // ── Current row: u_k = (s_0^{(k)} + c_k)^5. The four S-box outputs feed the closed-form
133 // prediction of the row-end state. ──
134 auto u_0 = pow5(w_l + q_l); // u_0 = (s_0^(0) + c_{4i})^5
135 auto u_1 = pow5(w_r + q_r); // u_1 = (s_0^(1) + c_{4i+1})^5
136 auto u_2 = pow5(w_o + q_o); // u_2 = (s_0^(2) + c_{4i+2})^5
137 auto u_3 = pow5(w_4 + q_4); // u_3 = (s_0^(3) + c_{4i+3})^5
138
139 // ── Next row's S-box outputs. Together with the next row's lane-0 wires they let us
140 // forward-compute V · (next row's hidden lanes) via the b'-formulas, without ever
141 // materializing the hidden lanes themselves. Only three (not four) because the
142 // b'-formulas only depend on u_0', u_1', u_2'. ──
143 auto u_0_next = pow5(w_l_shift + q_m); // u_0' = (next_s_0^(0) + c_{4(i+1)})^5
144 auto u_1_next = pow5(w_r_shift + q_c); // u_1' = (next_s_0^(1) + c_{4(i+1)+1})^5
145 auto u_2_next = pow5(w_o_shift + q_5); // u_2' = (next_s_0^(2) + c_{4(i+1)+2})^5
146 auto u_0_next_D1 = u_0_next * D1; // CSE: D_1 · u_0' is reused in A_1 and A_2
147
148 // Precomputed coefficient vectors. Each is indexed [W_R, W_O, W_4, U_0, U_1, U_2, U_3].
149 const auto& cf0 = QuadParams::tables.closed_form[0]; // out_0 (row-end lane 0)
150 const auto& l0 = QuadParams::tables.forward_vandermonde_lhs[0]; // out_1 + out_2 + out_3
151 const auto& l1 = QuadParams::tables.forward_vandermonde_lhs[1]; // D_2 out_1 + D_3 out_2 + D_4 out_3
152 const auto& l2 = QuadParams::tables.forward_vandermonde_lhs[2]; // D_2² out_1 + D_3² out_2 + D_4² out_3
153
154 // Wire-only parts of the four subrelations. Each subrelation A_K has the form
155 // A_K = (LHS_K computed from current row) - (RHS_K computed from next row).
156 // wpK_full collects all the wire-only terms (no u_*) from BOTH sides at once; the u_*
157 // terms are folded in below in aK_body.
158 // A_0: LHS = out_0, RHS = w_l_shift.
159 // A_1: LHS = out_1 + out_2 + out_3, RHS = b_1' = w_r' - D_1 u_0'.
160 // 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'.
161 // 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' + ...
162 // The LHS wire contributions come from cf0 / l0 / l1 / l2 (forward V applied to predicted
163 // output); the RHS wire contributions are the shifted lane-0 wires multiplied by the
164 // coefficients with which they appear in the b'-formulas above.
165 auto wp0_full = w_r * cf0[0] + w_o * cf0[1] + w_4 * cf0[2] - w_l_shift;
166 auto wp1_full = w_r * l0[0] + w_o * l0[1] + w_4 * l0[2] - w_r_shift;
167 auto wp2_full = w_r * l1[0] + w_o * l1[1] + w_4 * l1[2] - w_o_shift + w_r_shift + w_r_shift;
168 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;
169
170 const auto q_times_scaling_m = q_sel * scaling_factor;
171 const auto q_times_scaling = Accumulator(q_times_scaling_m);
172
173 // A_0: out_0 - w_l_shift = 0.
174 auto a0_body = u_0 * cf0[3] + u_1 * cf0[4] + u_2 * cf0[5] + u_3 * cf0[6] + Accumulator(wp0_full);
175 std::get<0>(evals) += q_times_scaling * a0_body;
176
177 // A_1: (out_1 + out_2 + out_3) - b_1_next = 0.
178 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);
179 std::get<1>(evals) += q_times_scaling * a1_body;
180
181 // A_2: (D_2 out_1 + D_3 out_2 + D_4 out_3) - b_2_next = 0.
182 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) +
183 (u_0_next + u_0_next + u_0_next) + u_1_next * D1 + Accumulator(wp2_full);
184 std::get<2>(evals) += q_times_scaling * a2_body;
185
186 // A_3: (D_2^2 out_1 + D_3^2 out_2 + D_4^2 out_3) - b_3_next = 0.
187 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 -
188 u_1_next * D1_MINUS_3 + u_2_next * D1 + Accumulator(wp3_full);
189 std::get<3>(evals) += q_times_scaling * a3_body;
190 }
191};
192
194
195} // 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