CycleBits
overviewCycleBits<T, N>
is a cyclic bit array of size N
, where exactly one bit is active (1
) at a time. It is used to track cyclic state transitions.
The bit array is initialized as follows:
i
is provided, bits[i] = 1
, and all other bits are set to 0
, marking i
as the active position.0
, meaning no active position.Example initialization (
i (Active index) |
bits Representation |
---|---|
None |
[0, 0, 0, 0] |
0 |
[1, 0, 0, 0] |
2 |
[0, 0, 1, 0] |
3 |
[0, 0, 0, 1] |
Each row has well-defined constraints that govern state transitions.
The active index is computed as:
bits representation |
active_idx |
---|---|
[1, 0, 0, 0] |
0 |
[0, 1, 0, 0] |
1 |
[0, 0, 1, 0] |
2 |
[0, 0, 0, 1] |
3 |
is_active
: Returns 1
if any bit is active, otherwise 0
:
is_transition
: Returns 1
if the transition is ongoing, i.e., the sum of the first N-1
bits is 1
:
is_last_row_to_active
: Returns 1
if the last bit (bits[N-1]
) transitions to the first bit (bits[0]
) in the next cycle.
Example transitions (
Current bits |
is_active |
is_transition |
is_last_row_to_active (Next: [1, 0, 0, 0] ) |
---|---|---|---|
[1, 0, 0, 0] |
1 |
1 |
0 |
[0, 1, 0, 0] |
1 |
1 |
0 |
[0, 0, 1, 0] |
1 |
1 |
0 |
[0, 0, 0, 1] |
1 |
0 |
1 |
0
or 1
.The transition rules ensure the correct cyclic movement of the active bit across rows.
1
in the next row.bits[N-1]
) is 1
, then all bits in the next row (except possibly bits[0]
) must be 0
. This allows two valid transitions:
bits[0] = 1
), ensuring cyclic continuity.0
, indicating the cycle ends.Current bits |
Next bits |
Transition properties |
---|---|---|
[1, 0, 0, 0] |
[0, 1, 0, 0] |
Active index increases: 0 → 1 |
[0, 1, 0, 0] |
[0, 0, 1, 0] |
Active index increases: 1 → 2 |
[0, 0, 1, 0] |
[0, 0, 0, 1] |
Active index increases: 2 → 3 |
[0, 0, 0, 1] |
[1, 0, 0, 0] |
Last bits[3] was active,Only bits[0] is 1 in the next row. |
[0, 0, 0, 1] |
[0, 0, 0, 0] |
Last bits[3] was active,The next row has all bits 0. |
[0, 0, 0, 0] |
[0, 0, 0, 0] |
State remains inactive. |
IsZeroCols
overviewIsZeroCols<T>
is a helper structure that verifies whether an input value is zero and enforces constraints accordingly. It consists of two columns:
inv
: Stores the multiplicative inverse of the input (if nonzero).output
: Stores a boolean flag indicating whether the input is zero (1
if zero, 0
otherwise).This gadget is useful to ensure efficient zero-checking without division operations.
The columns of IsZeroCols
are populated based on the given input:
If input = 0
, then:
output = 1
inv = 0
(zero value, since division by zero is undefined)If input ≠ 0
, then:
output = 0
inv = input⁻¹
(multiplicative inverse of the input)Example initialization:
Input | output (1 if input is 0 ) |
inv (input⁻¹ if input ≠ 0 ) |
---|---|---|
0 |
1 |
0 (undefined division) |
2 |
0 |
1/2 |
-3 |
0 |
-1/3 |
5 |
0 |
1/5 |
The gadget enforces the following algebraic constraints:
Zero-product constraint: If input
is nonzero, output
must be 0
, ensuring input * output = 0
:
Identity constraint: Ensures that when input ≠ 0
, inv
correctly represents the multiplicative inverse:
These constraints ensure that output
correctly signals whether input
is zero and that inv
holds a valid inverse when necessary.
The following table illustrates how the constraints hold under different input values:
Input | Expected output |
Expected inv |
Check: input * output = 0 |
Check: output + input * inv = 1 |
---|---|---|---|---|
0 |
1 |
0 |
0 * 1 = 0 ✅ |
1 + 0 * 0 = 1 ✅ |
2 |
0 |
1/2 |
2 * 0 = 0 ✅ |
0 + 2 * (1/2) = 1 ✅ |
-3 |
0 |
-1/3 |
-3 * 0 = 0 ✅ |
0 + (-3) * (-1/3) = 1 ✅ |
5 |
0 |
1/5 |
5 * 0 = 0 ✅ |
0 + 5 * (1/5) = 1 ✅ |
IsEqualCols
overviewIsEqualCols<T>
is a gadget that determines whether two values are equal by reducing the problem to a zero check. It wraps around the IsZeroCols<T>
gadget, leveraging its properties to enforce equality constraints.
To determine whether two values
Then, we apply the IsZeroCols
logic to diff
, which means:
output
field of IsZeroCols
is set to 1
.output
is 0
, while inv
stores a |
b |
a - b (diff) |
output ( 1 if a = b ) |
inv ( (a - b)⁻¹ if a ≠ b ) |
---|---|---|---|---|
3 |
3 |
0 |
1 |
0 |
4 |
2 |
2 |
0 |
1/2 |
7 |
-3 |
10 |
0 |
1/10 |
5 |
9 |
-4 |
0 |
-1/4 |
The gadget enforces the following constraints using IsZeroCols
:
a = b
, then their difference must be 0
, leading to:a ≠ b
, the inverse value must be correctly set:These constraints ensure that output
correctly represents the equality check.
This table illustrates how the constraints hold under different values:
a |
b |
a - b (diff) |
Expected output |
Expected inv |
Check: (a - b) * output = 0 |
Check: output + (a - b) * inv = 1 |
---|---|---|---|---|---|---|
3 |
3 |
0 |
1 |
0 |
0 * 1 = 0 ✅ |
1 + 0 * 0 = 1 ✅ |
4 |
2 |
2 |
0 |
1/2 |
2 * 0 = 0 ✅ |
0 + 2 * (1/2) = 1 ✅ |
7 |
-3 |
10 |
0 |
1/10 |
10 * 0 = 0 ✅ |
0 + 10 * (1/10) = 1 ✅ |
5 |
9 |
-4 |
0 |
-1/4 |
-4 * 0 = 0 ✅ |
0 + (-4) * (-1/4) = 1 ✅ |
CycleInt
overviewCycleInt<T, N>
is a gadget that represents a cyclic counter ranging from 0
to N-1
. It ensures correct step progression and handles wraparound conditions when the last step (N-1
) is reached.
CycleInt<T, N>
consists of:
step
: Tracks the current step in the cycle.is_last_step
: An IsEqualCols<T>
gadget that checks if step
equals N-1
.The gadget initializes as follows:
step
is set to the given step
value.is_last_step
is populated to determine if step == N-1
.Step (step ) |
is_last_step.output ( 1 if step = N-1 , else 0 ) |
---|---|
0 |
0 |
1 |
0 |
2 |
0 |
3 |
1 |
First row constraint: The first row must always start at step = 0
:
Step progression: Every row validates whether the current step is the last step:
Transition constraint: The step increases by 1
unless the last step is reached, in which case it resets to 0
:
Current Step ( step ) |
Is last step ( is_last_step.output ) |
Calculation for next step (step_next ) |
---|---|---|
0 |
0 |
step_next = 0 + 1 - 0 × 4 = 1 |
1 |
0 |
step_next = 1 + 1 - 0 × 4 = 2 |
2 |
0 |
step_next = 2 + 1 - 0 × 4 = 3 |
3 |
1 |
step_next = 3 + 1 - 1 × 4 = 0 (wraparound) |
LowerRowsFilterCols
overviewLowerRowsFilterCols<T>
is a simple filtering gadget that controls whether a row is active in an AIR trace. It ensures that once a row is marked as inactive, all subsequent rows must also remain inactive.
The gadget consists of a single column:
is_active
: A boolean flag indicating whether the current row is active (1
) or inactive (0
).The column is populated as follows:
is_active = 1
.is_active = 0
.Ensures that is_active
is always a valid boolean (0
or 1
):
Enforces that once a row is inactive, all subsequent rows must also be inactive:
This prevents transitions from inactive → active in later rows.
Current row ( is_active ) |
Next row ( is_active ) |
Constraint applied |
---|---|---|
1 |
1 |
Row remains active ✅ |
1 |
0 |
Row becomes inactive ✅ |
0 |
0 |
Stays inactive ✅ |
0 |
1 |
❌ Invalid (violates constraint) |
StrictlyIncreasingCols
overviewStrictlyIncreasingCols<T, MAX_DIFF_BITS>
is a gadget that enforces a strictly increasing sequence of values across rows in an AIR trace. It ensures that each value is strictly greater than the previous one and encodes the difference between consecutive values using binary decomposition.
StrictlyIncreasingCols<T, MAX_DIFF_BITS>
consists of:
value
: The value at the current row.diff_bits
: A binary representation of the difference between the current and next values.diff_inv
: The multiplicative inverse of the difference, used to enforce constraints.For each row:
diff
: The difference between the next and current values (diff = value_next - value
).diff_bits
: The binary decomposition of diff
using MAX_DIFF_BITS
bits.diff_inv
:
diff = 0
, set diff_inv = 0
(not allowed, but needed for handling padding).diff = 1
, set diff_inv = 1
.diff = 2
, set diff_inv = 1/2
.diff_inv = 1/diff
(multiplicative inverse).MAX_DIFF_BITS = 4
)Current value ( value ) |
Next value ( value_next ) |
Difference ( diff ) |
Binary Representation (diff_bits ) |
Inverse (diff_inv ) |
---|---|---|---|---|
3 |
5 |
2 |
[0, 0, 1, 0] |
1/2 |
5 |
8 |
3 |
[0, 0, 1, 1] |
1/3 |
8 |
9 |
1 |
[0, 0, 0, 1] |
1 |
9 |
9 (Padding) |
0 |
[0, 0, 0, 0] |
0 (invalid) |
The difference between consecutive values is computed as:
This ensures that the difference is encoded correctly in binary form.
Boolean constraint for diff_bits
Each bit in diff_bits
must be a valid boolean (0
or 1
):
Multiplicative inverse check
Ensures that diff_inv
correctly represents the inverse of diff
:
This constraint enforces that diff_inv
is correctly set when diff ≠ 0
.
Strictly increasing condition
Ensures that the next value is obtained by adding diff
to the current value:
This enforces a strictly increasing sequence.
MAX_DIFF_BITS = 4
)Current value |
Next value |
Binary diff_bits |
Computed diff |
Constraint applied |
---|---|---|---|---|
3 |
5 |
[0, 0, 1, 0] |
2 |
next.value = 3 + 2 = 5 ✅ |
5 |
8 |
[0, 0, 1, 1] |
3 |
next.value = 5 + 3 = 8 ✅ |
8 |
9 |
[0, 0, 0, 1] |
1 |
next.value = 8 + 1 = 9 ✅ |
9 |
9 (Padding) |
[0, 0, 0, 0] |
0 |
Padding: No change required ✅ |