Skip to content

Commit 22d9296

Browse files
committed
Refactor columns
1 parent 3fb98db commit 22d9296

File tree

2 files changed

+99
-27
lines changed

2 files changed

+99
-27
lines changed

poseidon2-circuit-air/src/air.rs

Lines changed: 79 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,19 @@ use p3_poseidon2_air::{Poseidon2Air, RoundConstants};
99

1010
use crate::{Poseidon2CircuitCols, num_cols};
1111

12+
/// Extends the Poseidon2 AIR with recursion circuit-specific columns and constraints.
1213
/// Assumes the field size is at least 16 bits.
14+
///
15+
/// SPECIFIC ASSUMPTIONS:
16+
/// - Memory elements from the witness table are extension elements of degree D.
17+
/// - RATE and CAPACITY are the number of extension elements in the rate/capacity.
18+
/// - WIDTH is the number of field elements in the state, i.e., (RATE + CAPACITY) * D.
19+
/// - `reset` can only be set during an absorb.
1320
#[derive(Debug)]
1421
pub struct Poseidon2CircuitAir<
1522
F: PrimeCharacteristicRing,
1623
LinearLayers,
24+
const D: usize,
1725
const RATE: usize,
1826
const CAPACITY: usize,
1927
const WIDTH: usize,
@@ -36,6 +44,7 @@ pub struct Poseidon2CircuitAir<
3644
impl<
3745
F: PrimeCharacteristicRing,
3846
LinearLayers,
47+
const D: usize,
3948
const RATE: usize,
4049
const CAPACITY: usize,
4150
const WIDTH: usize,
@@ -47,6 +56,7 @@ impl<
4756
Poseidon2CircuitAir<
4857
F,
4958
LinearLayers,
59+
D,
5060
RATE,
5161
CAPACITY,
5262
WIDTH,
@@ -59,6 +69,11 @@ impl<
5969
pub const fn new(
6070
constants: RoundConstants<F, WIDTH, HALF_FULL_ROUNDS, PARTIAL_ROUNDS>,
6171
) -> Self {
72+
assert!((CAPACITY + RATE) * D == WIDTH);
73+
assert!(RATE.is_multiple_of(D));
74+
assert!(CAPACITY.is_multiple_of(D));
75+
assert!(WIDTH.is_multiple_of(D));
76+
6277
Self {
6378
p3_poseidon2: Poseidon2Air::new(constants),
6479
}
@@ -68,6 +83,7 @@ impl<
6883
impl<
6984
F: PrimeCharacteristicRing + Sync,
7085
LinearLayers: Sync,
86+
const D: usize,
7187
const RATE: usize,
7288
const CAPACITY: usize,
7389
const WIDTH: usize,
@@ -79,6 +95,7 @@ impl<
7995
for Poseidon2CircuitAir<
8096
F,
8197
LinearLayers,
98+
D,
8299
RATE,
83100
CAPACITY,
84101
WIDTH,
@@ -89,13 +106,14 @@ impl<
89106
>
90107
{
91108
fn width(&self) -> usize {
92-
num_cols::<WIDTH, SBOX_DEGREE, SBOX_REGISTERS, HALF_FULL_ROUNDS, PARTIAL_ROUNDS>()
109+
num_cols::<WIDTH, RATE, SBOX_DEGREE, SBOX_REGISTERS, HALF_FULL_ROUNDS, PARTIAL_ROUNDS>()
93110
}
94111
}
95112

96113
pub(crate) fn eval<
97114
AB: AirBuilder,
98115
LinearLayers: GenericPoseidon2LinearLayers<WIDTH>,
116+
const D: usize,
99117
const RATE: usize,
100118
const CAPACITY: usize,
101119
const WIDTH: usize,
@@ -107,6 +125,7 @@ pub(crate) fn eval<
107125
air: &Poseidon2CircuitAir<
108126
AB::F,
109127
LinearLayers,
128+
D,
110129
RATE,
111130
CAPACITY,
112131
WIDTH,
@@ -119,6 +138,7 @@ pub(crate) fn eval<
119138
local: &Poseidon2CircuitCols<
120139
AB::Var,
121140
WIDTH,
141+
RATE,
122142
SBOX_DEGREE,
123143
SBOX_REGISTERS,
124144
HALF_FULL_ROUNDS,
@@ -127,6 +147,7 @@ pub(crate) fn eval<
127147
next: &Poseidon2CircuitCols<
128148
AB::Var,
129149
WIDTH,
150+
RATE,
130151
SBOX_DEGREE,
131152
SBOX_REGISTERS,
132153
HALF_FULL_ROUNDS,
@@ -135,39 +156,71 @@ pub(crate) fn eval<
135156
) {
136157
air.p3_poseidon2.eval(builder);
137158

138-
// When resetting the state, we just have to clear the capacity. The rate will be overwritten by the input.
139-
builder
140-
.when(local.reset.clone())
141-
.assert_zeros::<CAPACITY, _>(array::from_fn(|i| local.poseidon2.inputs[i + RATE].clone()));
142-
143-
// If the next row doesn't reset, propagate the capacity.
144159
let next_no_reset = AB::Expr::ONE - next.reset.clone();
145-
builder
146-
.when(next_no_reset)
147-
.assert_zeros::<CAPACITY, _>(array::from_fn(|i| {
148-
next.poseidon2.inputs[i + RATE].clone()
149-
- local.poseidon2.ending_full_rounds[HALF_FULL_ROUNDS - 1].post[i + RATE].clone()
150-
}));
160+
for i in 0..(CAPACITY * D) {
161+
// The first row has capacity zeroed.
162+
builder
163+
.when_first_row()
164+
.assert_zero(local.poseidon2.inputs[RATE * D + i].clone());
165+
166+
// When resetting the state, we just have to clear the capacity. The rate will be overwritten by the input.
167+
builder
168+
.when(local.reset.clone())
169+
.assert_zero(local.poseidon2.inputs[RATE * D + i].clone());
170+
171+
// If the next row doesn't reset, propagate the capacity.
172+
builder.when(next_no_reset.clone()).assert_zero(
173+
next.poseidon2.inputs[RATE * D + i].clone()
174+
- local.poseidon2.ending_full_rounds[HALF_FULL_ROUNDS - 1].post[RATE * D + i]
175+
.clone(),
176+
);
177+
}
151178

152-
// If the next row is squeezing, then we propagate the entire output state forward.
153-
let next_squeeze = AB::Expr::ONE - next.absorb.clone();
154-
builder
155-
.when(next_squeeze)
156-
.assert_zeros::<WIDTH, _>(array::from_fn(|i| {
157-
next.poseidon2.inputs[i].clone()
158-
- local.poseidon2.ending_full_rounds[HALF_FULL_ROUNDS - 1].post[i].clone()
159-
}));
179+
let mut next_absorb = [AB::Expr::ZERO; RATE];
180+
for i in 0..RATE {
181+
for col in next_absorb.iter_mut().take(RATE).skip(i) {
182+
*col += next.absorb_flags[i].clone();
183+
}
184+
}
185+
let next_no_absorb = array::from_fn::<_, RATE, _>(|i| AB::Expr::ONE - next_absorb[i].clone());
186+
// In the next row, each rate element not being absorbed comes from the current row.
187+
for index in 0..(RATE * D) {
188+
let i = index / D;
189+
let j = index % D;
190+
builder.when(next_no_absorb[i].clone()).assert_zero(
191+
next.poseidon2.inputs[i * D + j].clone()
192+
- local.poseidon2.ending_full_rounds[HALF_FULL_ROUNDS - 1].post[i * D + j].clone(),
193+
);
194+
}
195+
196+
let mut current_absorb = [AB::Expr::ZERO; RATE];
197+
for i in 0..RATE {
198+
for col in current_absorb.iter_mut().take(RATE).skip(i) {
199+
*col += local.absorb_flags[i].clone();
200+
}
201+
}
202+
let current_no_absorb =
203+
array::from_fn::<_, RATE, _>(|i| AB::Expr::ONE - current_absorb[i].clone());
204+
// During a reset, the rate elements not being absorbed are zeroed.
205+
for (i, col) in current_no_absorb.iter().enumerate().take(RATE) {
206+
let arr = array::from_fn::<_, D, _>(|j| local.poseidon2.inputs[i * D + j].clone().into());
207+
builder
208+
.when(local.reset.clone() * col.clone())
209+
.assert_zeros(arr);
210+
}
160211

212+
let _is_squeeze = AB::Expr::ONE - current_absorb[0].clone();
161213
// TODO: Add all lookups:
162-
// - If local.absorb = 1:
163-
// * local.rate comes from input lookups.
164-
// - If local.absorb = 0:
214+
// - If current_absorb[i] = 1:
215+
// * local.rate[i] comes from input lookups.
216+
// - If is_squeeze = 1:
165217
// * local.rate is sent to output lookups.
166218
}
167219

168220
impl<
169221
AB: AirBuilder,
170222
LinearLayers: GenericPoseidon2LinearLayers<WIDTH>,
223+
const D: usize,
171224
const RATE: usize,
172225
const CAPACITY: usize,
173226
const WIDTH: usize,
@@ -179,6 +232,7 @@ impl<
179232
for Poseidon2CircuitAir<
180233
AB::F,
181234
LinearLayers,
235+
D,
182236
RATE,
183237
CAPACITY,
184238
WIDTH,
@@ -199,6 +253,7 @@ impl<
199253
eval::<
200254
_,
201255
_,
256+
D,
202257
RATE,
203258
CAPACITY,
204259
WIDTH,

poseidon2-circuit-air/src/columns.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,18 @@ use p3_poseidon2_air::Poseidon2Cols;
55
/// Columns for a Poseidon2 AIR which computes one permutation per row.
66
///
77
/// They extend the P3 columns with some circuit-specific columns.
8+
///
9+
/// `reset` (transparent): indicates whether the state is being reset this row.
10+
/// `absorb_flags` (transparent): for each rate element, indicates if it is being absorbed this row.
11+
/// At most one flag is set to 1 per row: if `absorb_flags[i]` is 1, then all elements up to the `i`-th
12+
/// are absorbed; the rest are propagated from the previous row.
13+
/// `input_indices` (transparent): for each rate element, indicates the index in the witness table for the
14+
/// memory lookup. It's either received (for an absorb) or sent (for a squeeze).
815
#[repr(C)]
916
pub struct Poseidon2CircuitCols<
1017
T,
1118
const WIDTH: usize,
19+
const RATE: usize,
1220
const SBOX_DEGREE: u64,
1321
const SBOX_REGISTERS: usize,
1422
const HALF_FULL_ROUNDS: usize,
@@ -18,13 +26,13 @@ pub struct Poseidon2CircuitCols<
1826
Poseidon2Cols<T, WIDTH, SBOX_DEGREE, SBOX_REGISTERS, HALF_FULL_ROUNDS, PARTIAL_ROUNDS>,
1927

2028
pub reset: T,
21-
pub absorb: T,
22-
pub input_indices: [T; WIDTH],
23-
pub output_indices: [T; WIDTH],
29+
pub absorb_flags: [T; RATE],
30+
pub input_indices: [T; RATE],
2431
}
2532

2633
pub const fn num_cols<
2734
const WIDTH: usize,
35+
const RATE: usize,
2836
const SBOX_DEGREE: u64,
2937
const SBOX_REGISTERS: usize,
3038
const HALF_FULL_ROUNDS: usize,
@@ -34,6 +42,7 @@ pub const fn num_cols<
3442
Poseidon2CircuitCols<
3543
u8,
3644
WIDTH,
45+
RATE,
3746
SBOX_DEGREE,
3847
SBOX_REGISTERS,
3948
HALF_FULL_ROUNDS,
@@ -45,6 +54,7 @@ pub const fn num_cols<
4554
impl<
4655
T,
4756
const WIDTH: usize,
57+
const RATE: usize,
4858
const SBOX_DEGREE: u64,
4959
const SBOX_REGISTERS: usize,
5060
const HALF_FULL_ROUNDS: usize,
@@ -54,6 +64,7 @@ impl<
5464
Poseidon2CircuitCols<
5565
T,
5666
WIDTH,
67+
RATE,
5768
SBOX_DEGREE,
5869
SBOX_REGISTERS,
5970
HALF_FULL_ROUNDS,
@@ -66,6 +77,7 @@ impl<
6677
) -> &Poseidon2CircuitCols<
6778
T,
6879
WIDTH,
80+
RATE,
6981
SBOX_DEGREE,
7082
SBOX_REGISTERS,
7183
HALF_FULL_ROUNDS,
@@ -75,6 +87,7 @@ impl<
7587
self.align_to::<Poseidon2CircuitCols<
7688
T,
7789
WIDTH,
90+
RATE,
7891
SBOX_DEGREE,
7992
SBOX_REGISTERS,
8093
HALF_FULL_ROUNDS,
@@ -91,6 +104,7 @@ impl<
91104
impl<
92105
T,
93106
const WIDTH: usize,
107+
const RATE: usize,
94108
const SBOX_DEGREE: u64,
95109
const SBOX_REGISTERS: usize,
96110
const HALF_FULL_ROUNDS: usize,
@@ -100,6 +114,7 @@ impl<
100114
Poseidon2CircuitCols<
101115
T,
102116
WIDTH,
117+
RATE,
103118
SBOX_DEGREE,
104119
SBOX_REGISTERS,
105120
HALF_FULL_ROUNDS,
@@ -112,6 +127,7 @@ impl<
112127
) -> &mut Poseidon2CircuitCols<
113128
T,
114129
WIDTH,
130+
RATE,
115131
SBOX_DEGREE,
116132
SBOX_REGISTERS,
117133
HALF_FULL_ROUNDS,
@@ -121,6 +137,7 @@ impl<
121137
self.align_to_mut::<Poseidon2CircuitCols<
122138
T,
123139
WIDTH,
140+
RATE,
124141
SBOX_DEGREE,
125142
SBOX_REGISTERS,
126143
HALF_FULL_ROUNDS,

0 commit comments

Comments
 (0)