Gadget decomposition
Akita often works with coefficients, the individual numbers in a polynomial, that can be almost as large as the field modulus. The modulus is the prime number around which field arithmetic wraps. Akita later locks these coefficients into short commitments. Its security calculations and matrix sizes need an explicit bound for every committed value.
Gadget decomposition replaces one large coefficient with several small signed integers called digits. The original coefficient can be recovered from those digits. Akita can therefore run its matrix commitments with small, known input bounds without losing the original value.
This chapter first explains the operation for one ordinary integer. It then shows the general equation, the exact range that a fixed number of digits can represent, and the places where Akita uses different decomposition depths.
Ways to read this chapter
The explanation through the exact range of a digit list is the common foundation. After that, different readers may want different details.
| If you want to | Continue with |
|---|---|
| Learn the idea without prior knowledge of proof systems | The chapter in order, starting with the base 4 example |
| Supply values to Akita | Where Akita uses decomposition and source width and source shape |
| Change or review the implementation | How digits are stored, the code path, and the review checklist |
| Audit how the protocol rule reaches the shipped code | From the protocol rule to the code |
| Study performance | From one coefficient to a vector and how digits are stored |
The later sections do not introduce a different version of decomposition. They show how the same mathematical rule becomes an input contract, a public schedule choice, a memory layout, and an optimized routine.
The values being decomposed
Akita performs most arithmetic in a finite field. A field element is stored as an integer from to , where is a prime number called the modulus. Arithmetic wraps around modulo . For example, and represent the same field element.
A ring element in Akita is a polynomial whose coefficients are field elements. Decomposing a ring element means decomposing each of its coefficients. The same coefficient operation is also used on flat vectors of field elements.
A polynomial commitment is a short value that fixes a polynomial. After the prover publishes the commitment, the prover should not be able to claim that it committed to a different polynomial. A witness is the data used to make a proof. In Akita, several commitment and opening witnesses contain decomposed coefficients.
Akita's lattice commitment multiplies witness coefficients by a public matrix. The security calculation needs an upper bound on those coefficients. A larger bound can require a matrix with more output rows, which makes the commitment or proof larger. Decomposition gives the matrix small inputs with an exact bound while preserving the original coefficient modulo .
A small example
Choose base . Ordinary base 4 digits range from 0 to 3. Akita instead uses the balanced signed digits
The integer 19 has the following balanced base 4 decomposition:
Akita records the digits from the least significant position to the most significant position. The digit list for 19 is therefore .
Negative values work in the same way. For example,
so its digit list is .
These equations also hold for field elements because Akita checks the result modulo . A field element has many integer representatives that differ by a multiple of . Before decomposition, Akita chooses a signed representative that fits the available balanced digit range.
The general decomposition rule
Akita uses bases that are powers of two. It writes
where is called the basis exponent in the code. The balanced digit set is
The number of digits is written as . A coefficient is decomposed into digits that satisfy
The symbols have the following meanings.
| Symbol | Meaning |
|---|---|
| The prime field modulus | |
| The decomposition base | |
The base 2 logarithm of , stored as log_basis | |
| The number of digits, also called the decomposition depth | |
| The signed digit at position |
The digit at position zero has weight 1. The next digit has weight . Each later position multiplies the weight by another factor of .
From one coefficient to a vector
The gadget matrix records the powers of used to rebuild every coefficient. For coefficients, it is written as
Here is the square identity matrix with rows and columns. It has ones on its diagonal and zeros elsewhere. The tensor product symbol means that each coefficient gets its own copy of the power vector . Multiplying this matrix by the digits rebuilds the original coefficients modulo .
The matrix formula groups all digits of one coefficient together. The flat decomposition function in the code stores the same data in digit major order. It stores the first digit of every coefficient, then the second digit of every coefficient, and continues in that order.
For the two coefficients from the example above, the digit planes are
The flat output is therefore . This order lets the prover process one digit plane across many coefficients at once.
The exact range of a digit list
A fixed number of digits cannot represent every integer. The largest possible absolute value depends on the base and the number of digits.
Add the weights of all digit positions and call the result :
The most negative digit at every position is . The largest positive digit is only . Therefore, digits represent every integer in
where
The interval extends farther in the negative direction. This is not a rounding error. The digit set contains , but it does not contain .
For base 4 with three digits, . The negative endpoint is . The positive endpoint is . The exact range is therefore .
The code provides two kinds of range helper. The exact helper returns None
for an endpoint when the true mathematical value is larger than any u128.
In that result, None means that no u128 coefficient can exceed the
endpoint. A separate helper returns a finite value when its calculation would
overflow. That finite value is safe for estimates, but code that accepts or
rejects a coefficient uses the exact helper.
How Akita chooses the number of digits
The decomposition depth must be large enough for the value being decomposed. Akita uses two related rules because a full field element and a source with a smaller declared bound have different ranges.
Full field elements
Let be the number of bits needed for field elements. A full field decomposition uses
The ceiling brackets mean that the fraction is rounded up to the next integer. For example, a field with 128 bits and basis exponent uses 43 digits. Forty two digits cover only 126 bit positions, so one more digit is required.
There is one edge case when . A symmetric signed interval can appear to require an extra digit even though every field value already has a valid representative. Akita uses the smaller of the exact positive endpoint and as its centering threshold in this case. Residues above that threshold are represented as negative integers by subtracting . This lets the full field path keep the rounded up formula above.
Sources with a smaller bound
Some committed sources are known to be much smaller than a general field element. Akita records their allowed signed width as . A width of means the interval
Akita first tries digits. It then checks the exact positive endpoint . If that endpoint is too small, Akita adds one digit.
A concrete case shows why this check is needed. Let and , so the base is 8. The declared signed interval is . Two balanced base 8 digits represent only . The negative side is wide enough, but the positive side stops at 27. Akita therefore uses three digits.
A nonnegative value that may be as large as needs signed width
. This extra bit records the sign. A source that may contain any
u64 value therefore declares width 65, not 64.
Some parts of the protocol have an exact bound on the largest absolute coefficient instead of a signed bit width. This bound is often written as an infinity norm. Akita tries increasing depths and selects the first exact digit range that contains both the positive and negative bound.
Where Akita uses decomposition
A proof starts with the values supplied by the application. Akita calls these the root source. An opening proves that a committed polynomial has a claimed value at a chosen point. A fold reduces the current claims to an opening of a new recursive digit witness. The full witness includes auxiliary data and need not be smaller than the input, especially at the root.
These values need different decomposition depths.
| Value | Why it is decomposed | Bound used for its depth |
|---|---|---|
| Root committed source | It is the application data entering the first commitment | log_commit_bound |
| Recursive committed source | It is the recursive digit witness produced by an earlier fold | The source bound stored by the schedule |
| Opening value | It is used to prove a claimed polynomial evaluation | log_open_bound or the full field width |
| Setup prefix | It contains public setup field elements used by a later fold | The full field width |
| Folded response | It is the response produced during one fold | The exact response bound stored by the schedule |
The schedule is a public plan chosen before proving. It records the bases, digit counts, matrix sizes, and other values that the prover and verifier must use at each fold. The verifier reads the same digit counts from the resolved schedule. It does not trust a digit count supplied by the proof.
An application using a standard preset does not call the decomposition routine or choose a digit count directly. It declares its source representation and uses a preset whose planner resolves these choices into the schedule. A custom configuration must keep the source class, source bound, basis, and resulting depth consistent. Akita rejects the source at commitment time if that contract does not hold.
Source width and source shape are separate
Akita records both a numeric bound and a source class. The bound says how large each coefficient may be. The class says what pattern the coefficients must follow.
BalancedSignedDigit is the general bounded signed source class.
UnitOneHot is more specific. Its coefficients are 0 or 1, and each configured
chunk contains at most one coefficient equal to 1. A dense vector of zeros and
ones does not satisfy this class merely because each coefficient is small.
For this reason, log_commit_bound == 1 does not select UnitOneHot. The class
must say UnitOneHot explicitly. For a bounded BalancedSignedDigit source,
the accepted coefficient interval is the intersection of two ranges:
- The interval declared by
log_commit_bound. - The interval that the scheduled number of digits can represent.
The code that creates the commitment checks both ranges. It rejects an out of
range coefficient instead of silently dropping high digits. The UnitOneHot
path separately checks that every value is 0 or 1 and that each chunk has at
most one value equal to 1.
How digits are stored
The largest digit depends on , so Akita chooses a signed integer type
that can hold every digit. In Rust, i8 is a signed integer with 8 bits and
i16 is a signed integer with 16 bits.
| Basis exponent | Balanced digit interval | Rust storage |
|---|---|---|
| 1 to 8 | i8 | |
| 9 to 16 | i16 |
The basis exponent is not the base itself. For example, means base , with digits from to 511. It does not mean base 10.
Production schedule validation keeps opening bases in the i8 range and
inner commitment bases in the i16 range. The current schedule search tries
opening exponents from 3 to 6. It tries inner exponents up to 10 for fields
with 32 bits and up to 11 for fields with 64 or 128 bits.
The ring API can return digits as field elements, i8 values, or i16 values.
For 32 bit fields, the packed i8 function uses SIMD when the processor
supports it. SIMD applies the same operation to several integers at once. The
NEON path on AArch64 processes four field values at a time. The AVX2 path on
x86 processes eight. Both read the standard 32 bit representation directly.
The ordinary fallback processes one value at a time and produces the same
digit major output.
The i8 NTT commitment path next represents each signed digit modulo several
smaller primes. It stores those residues in Montgomery form, which is an
internal representation used for fast multiplication. Converting every digit
inside the matrix multiplication loop would repeat the same work many times.
Akita instead builds a lookup table once for each matrix multiplication. The
table covers the active balanced range and returns the prepared residue for a
digit and a small prime. The fixed i8 table can cover every value from -128
to 127, while each operation initializes only the part needed by its basis.
Path through the code
The main code path is:
DecompositionParamsincrates/akita-params/src/config.rsrecordslog_basis,log_commit_bound, and the optionallog_open_bound.crates/akita-params/src/sis/decomposition_digits.rscomputes the exact digit count for each protocol role.- The generated schedule stores the selected counts in
CommittedGroupParams. crates/akita-algebra/src/ring/cyclotomic/decomposition.rsperforms the coefficient decomposition and rebuilding operation.crates/akita-cpu-backend/src/kernels/linear/decompose.rsuses the packedi8kernel. Wider inner decompositions usecrates/akita-cpu-backend/src/arithmetic/exact_i16.rs.crates/akita-params/src/sis/committed_source.rschecks the accepted source range and source class.
Supporting limits live in these files:
crates/akita-params/src/signed_digit.rsselectsi8ori16storage.crates/akita-algebra/src/ring/crt_ntt_repr/lut.rsprepares signedi8digits for the CRT and NTT commitment path.crates/akita-schedules/src/runtime.rsvalidates schedule basis ranges.crates/akita-config/src/proof_optimized.rsdefines the production search ranges.
From the protocol rule to the code
The equations above define decomposition, but the implementation must make more choices. It decides how many digits each protocol value gets. It chooses the signed integer type and memory order. It also decides which inputs are allowed. The schedule makes these choices public so that the prover and verifier use the same values.
| Property to trace | Explanation in this chapter | Current implementation |
|---|---|---|
| A coefficient is rebuilt from balanced powers of the base | The general decomposition rule and the gadget matrix | decomposition.rs extracts the digits, while digit_math.rs produces the public powers used to rebuild them |
| The asymmetric range and full field centering need no extra digit | The exact range and full field elements | decompose_centering_threshold applies the exact threshold, including the 128 bit overflow boundary |
| Every committed digit is in the balanced set | The balanced digit set | layout/digit_range.rs defines the range polynomial and akita-verifier/src/stages/stage1.rs verifies its sumcheck proof |
| Different protocol values can use different bases and depths | Where Akita uses decomposition | decomposition_digits.rs separates root sources, recursive sources, setup prefixes, openings, and folded responses |
| Prover and verifier use the same choice | The public schedule | The resolved schedule stores each role specific basis and depth in CommittedGroupParams |
| A committed source satisfies the assumptions used to size it | Source width and source shape | committed_source.rs defines the contract and akita-cpu-backend/src/backend/commitment/api.rs enforces its numeric and structural parts |
| Compact extraction and field conversion preserve the same digits | How digits are stored | signed_digit.rs selects i8 or i16, decomposition.rs extracts the digits, and crt_ntt_repr/lut.rs prepares i8 digits for multiplication |
| SIMD extraction agrees with scalar extraction | The fast path and fallback | decomposition/aarch64.rs and decomposition/x86.rs implement the fast paths, while the centering boundary tests compare their public dispatch with the scalar definition |
This table separates the protocol contract from its implementation. The equations and ranges state what must hold. The schedule and source contract state which values Akita accepts. The algebra, prover, and verifier code show how Akita computes and checks those values.
Review checklist
A code review or security audit can follow the mechanism in this order.
- Check that extraction produces digits in and that their weighted sum rebuilds the original coefficient modulo .
- Check the independent verifier boundary. Stage 1 must prove that every committed digit is a member of the same balanced set. Correct prover extraction alone is not enough because a proof may contain malformed data.
- Check that the depth selector covers the correct range for each role. Full field values and smaller signed sources use different centering rules.
- Check that source admission applies both parts of the contract. The numeric interval and the declared source class are independent.
- Check that the resolved schedule, proof layout, prover, and verifier all use the same role specific depth and basis.
- Check that the flat, ring,
i8,i16, scalar, NEON, and AVX2 paths agree on digit order and centering at their boundaries. - Check any performance change against the scalar implementation before relying on benchmark results. A faster layout is only valid if it preserves the same digits and reconstruction.
The focused regression tests are in
crates/akita-params/src/sis/decomposition_digits.rs,
crates/akita-params/src/sis/committed_source.rs, and
crates/akita-algebra/src/ring/cyclotomic/tests.rs. The Stage 1 range topology
tests live in crates/akita-types/src/proof/stage1.rs. Together, these tests
cover the asymmetric field boundary, the exact positive reach, the
intersection of declared and representable source ranges, supported range
proof bases, digit major layout, the wider i16 bases, and agreement between
scalar and SIMD decomposition. These tests are useful evidence, but they do
not replace checking that every protocol caller uses the right public
parameters.
What to remember
Gadget decomposition is an exact change of representation. It does not round a coefficient or change the field element. It replaces one coefficient with small signed digits whose weighted sum gives the original value modulo .
The base controls the size of each digit. The depth controls how many digits are available. Akita chooses both values as part of the public schedule, and the prover must reject any source that does not fit the resulting contract.