Documentation

UniversalHashing.ToeplitzNTT

Toeplitz hashing via NTT-based binary circular convolution #

Computing the Toeplitz hash toeplitzHash by school-book matrix-vector multiplication takes time quadratic in the block size. The Toeplitz structure allows an O(N log N) algorithm ( see, e.g., [HT16] Appendix B.).

A circulant matrix is determined by its first column c_C; its product with a vector is the circular convolution of c_C with that vector. Writing the Toeplitz matrix's first column as c_T and its first row (excluding the corner) as r_T, the embedding is c_C = c_T ++ reverse r_T.

In the parameter layout of ToeplitzMatrix.from_params (where T i j = param (i + (n-1) - j)) we have c_T i = param (n-1+i) and r_T j = param (n-2-j), so the reversal cancels and

c_C i = if i < m then param (n-1+i) else param (i-m).

Main definitions:

Main results:

Tests/ToeplitzNTT.lean additionally checks toeplitzHashNTT (running the fast implementation via the @[csimp] replacement) against the school-book toeplitzHash for small matrices.

def toeplitzCirculantColumn (m n : ) (param : BitVec (m + n - 1)) :
BitVec (m + n - 1)

First column of the circulant matrix that the Toeplitz matrix with parameter vector param (in the layout of ToeplitzMatrix.from_params) embeds into: the Toeplitz matrix's first column followed by its reversed first row (sans corner).

Equations
Instances For
    def toeplitzHashNTT (m n : ) :
    HashFamily (BitVec (m + n - 1)) (BitVec n) (BitVec m)

    The Toeplitz hash family, computed via the NTT-based binary circular convolution circularConvolutionGf2: embed the seed's Toeplitz matrix into a circulant matrix (toeplitzCirculantColumn), zero-pad the input to length m + n - 1, convolve, and keep the low m bits.

    Bit k of the seed corresponds to param k in toeplitzHash; bits of input/output correspond to vector entries, 1 : ZMod 2true.

    This is the reference definition, written for auditability and proofs. As written it has quadratic BitVec boundary cost, but compiled code never pays it: a @[csimp] replacement (toeplitzHashNTT_eq_fast) makes compiled calls run the equivalent O(N log N) implementation toeplitzHashNTTFast.

    Equations
    Instances For

      Correctness #

      def BitVec.toZMod2Fun {w : } (v : BitVec w) :
      Fin wZMod 2

      Decode a bit vector into a ZMod 2 vector (LSB-first, true ↦ 1).

      Equations
      Instances For

        The ring homomorphism from Bool (with xor as + and and as *) to ZMod 2.

        Equations
        • One or more equations did not get rendered due to their size.
        Instances For
          theorem toeplitzHashNTT_eq_toeplitzHash (m n : ) [NeZero m] [NeZero n] (hL : m + n - 1 < 2 ^ 29) (param : BitVec (m + n - 1)) (x : BitVec n) :

          Correctness of NTT-based Toeplitz hashing. On the domain where the NTT convolution is proven correct (m + n - 1 < 2 ^ 29), toeplitzHashNTT computes exactly the school-book Toeplitz hash toeplitzHash, under the correspondence BitVec.toZMod2Fun between bit vectors and ZMod 2 vectors.

          Fast implementation #

          toeplitzHashNTT is algorithmically O(N log N), but its BitVec boundary work is quadratic: every per-bit getLsb/ofFnLE access shifts the whole underlying big integer. toeplitzHashNTTFast performs the same computation with divide-and-conquer bit extraction and assembly (O(N log N) overall) and is proven equal as a function to toeplitzHashNTT (toeplitzHashNTTFast_eq), so toeplitzHashNTT_eq_toeplitzHash applies to it verbatim (toeplitzHashNTTFast_eq_toeplitzHash).

          The @[csimp] theorem toeplitzHashNTT_eq_fast then closes the loop: compiled code that calls toeplitzHashNTT actually runs toeplitzHashNTTFast, so there is no slow path at runtime and no reason to call the fast implementation by name.

          @[irreducible]

          Divide-and-conquer extraction of all bits of a BitVec (LSB first) in O(w log w): each level splits the vector with one whole-vector shift instead of shifting per bit.

          Equations
          • One or more equations did not get rendered due to their size.
          Instances For
            theorem BitVec.toBoolVector_getElem {w : } (v : BitVec w) (i : ) (hi : i < w) :
            @[irreducible]
            def BitVec.ofBoolFnLE (w : ) :
            (Fin wBool)BitVec w

            Divide-and-conquer assembly of a BitVec from a bit function (LSB first) in O(w log w): each level concatenates two halves with one whole-vector shift instead of inserting bits one at a time (as BitVec.ofFnLE's Nat.ofBits does).

            Equations
            • One or more equations did not get rendered due to their size.
            Instances For
              def toeplitzHashNTTFast (m n : ) :
              HashFamily (BitVec (m + n - 1)) (BitVec n) (BitVec m)

              Fast implementation of toeplitzHashNTT: the same circulant-embedding NTT convolution pipeline, but with divide-and-conquer BitVec bit extraction/assembly instead of per-bit big-integer accesses. Proven equal to toeplitzHashNTT in toeplitzHashNTTFast_eq.

              Equations
              • One or more equations did not get rendered due to their size.
              Instances For
                theorem BitVec.toBoolVector_get_eq_getLsb {w : } (v : BitVec w) (i : Fin w) :

                The divide-and-conquer bit extraction agrees with getLsb.

                The fast implementation is the reference implementation — unconditionally, as an equality of functions. Together with toeplitzHashNTT_eq_toeplitzHash this transfers all correctness statements to toeplitzHashNTTFast.

                @[csimp]

                Compiler replacement (@[csimp]): compiled code that calls toeplitzHashNTT runs toeplitzHashNTTFast instead. This is justified by the kernel-checked equality toeplitzHashNTTFast_eq — proofs continue to see the reference definition, while execution is O(N log N).

                theorem toeplitzHashNTTFast_eq_toeplitzHash (m n : ) [NeZero m] [NeZero n] (hL : m + n - 1 < 2 ^ 29) (param : BitVec (m + n - 1)) (x : BitVec n) :

                Correctness of the fast NTT-based Toeplitz hash, transferred from toeplitzHashNTT_eq_toeplitzHash via toeplitzHashNTTFast_eq.