Documentation

HexBerlekamp.PackedKernel

@[inline]

Modular product of two packed words. The operands are widened to UInt64, where their product is exact for any UInt32 inputs.

Equations
Instances For
    @[inline]

    Modular sum of two packed residues below q. The unreduced sum fits in a UInt32 because q < 2^31, so one conditional subtraction canonicalizes it.

    Equations
    Instances For
      @[inline]

      Modular negation of a packed residue below q.

      Equations
      Instances For
        @[inline]

        The modulus as a packed word.

        Equations
        Instances For
          @[inline]

          Read a packed word as a residue.

          Equations
          Instances For
            @[inline]

            Write a residue as a packed word.

            Equations
            Instances For

              A residue is below 2 ^ 32, so ofZMod loses nothing: ZMod64.Bounds caps the modulus at 2 ^ 31.

              @[simp]

              Packing a residue preserves its numeral.

              @[simp]

              ofZMod is a section of toZMod: packing then reading recovers the residue.

              Reading an arbitrary packed word canonicalizes it modulo p. Not @[simp]: ZMod64.toNat_eq_val rewrites the ZMod64.toNat head of the left-hand side, so this shape is not simp-normal.

              @[simp]

              The packed modulus word carries the modulus faithfully.

              Every packed word is below 2 ^ 32.

              theorem Hex.Berlekamp.Packed.pLt32 {p : Nat} [ZMod64.Bounds p] :
              p < 2 ^ 32

              The modulus is below 2 ^ 32, so it is faithful as a packed word.

              A packed word below the modulus is recovered exactly by toZMod.

              theorem Hex.Berlekamp.Packed.toNat_mulMod {p : Nat} [ZMod64.Bounds p] {q a b : UInt32} (hq : q.toNat = p) :
              (mulMod q a b).toNat = a.toNat * b.toNat % p

              mulMod computes the modular product of its operands as naturals. The UInt64 widening keeps the unreduced product exact for arbitrary UInt32 inputs, so no reducedness hypothesis is needed.

              theorem Hex.Berlekamp.Packed.mulMod_lt {p : Nat} [ZMod64.Bounds p] {q a b : UInt32} (hq : q.toNat = p) :
              (mulMod q a b).toNat < p

              mulMod returns a reduced residue.

              theorem Hex.Berlekamp.Packed.toNat_addMod {p : Nat} [ZMod64.Bounds p] {q a b : UInt32} (hq : q.toNat = p) (ha : a.toNat < p) (hb : b.toNat < p) :
              (addMod q a b).toNat = (a.toNat + b.toNat) % p

              addMod computes the modular sum of two reduced operands. Reducedness is required: the single conditional subtraction only canonicalizes a sum that is below 2 * p.

              theorem Hex.Berlekamp.Packed.addMod_lt {p : Nat} [ZMod64.Bounds p] {q a b : UInt32} (hq : q.toNat = p) (ha : a.toNat < p) (hb : b.toNat < p) :
              (addMod q a b).toNat < p

              addMod returns a reduced residue when both operands are reduced.

              theorem Hex.Berlekamp.Packed.toNat_negMod {p : Nat} [ZMod64.Bounds p] {q a : UInt32} (hq : q.toNat = p) (ha : a.toNat < p) :
              (negMod q a).toNat = (p - a.toNat) % p

              negMod computes the modular negation of a reduced operand. The a == 0 branch is what makes the result reduced rather than p.

              theorem Hex.Berlekamp.Packed.negMod_lt {p : Nat} [ZMod64.Bounds p] {q a : UInt32} (hq : q.toNat = p) (ha : a.toNat < p) :
              (negMod q a).toNat < p

              negMod returns a reduced residue when its operand is reduced.

              theorem Hex.Berlekamp.Packed.toZMod_negMod {p : Nat} [ZMod64.Bounds p] {q a : UInt32} (hq : q.toNat = p) (ha : a.toNat < p) :
              toZMod p (negMod q a) = -toZMod p a

              The packed negation of a packed residue is the packed negated residue.

              theorem Hex.Berlekamp.Packed.toZMod_mulMod {p : Nat} [ZMod64.Bounds p] {q a b : UInt32} (hq : q.toNat = p) (ha : a.toNat < p) (hb : b.toNat < p) :
              toZMod p (mulMod q a b) = toZMod p a * toZMod p b

              The packed product of packed residues is the packed product residue.

              theorem Hex.Berlekamp.Packed.toZMod_addMod {p : Nat} [ZMod64.Bounds p] {q a b : UInt32} (hq : q.toNat = p) (ha : a.toNat < p) (hb : b.toNat < p) :
              toZMod p (addMod q a b) = toZMod p a + toZMod p b

              The packed sum of packed residues is the packed sum residue.

              @[inline]

              Modular inverse of a packed residue, routed through Hex.ZMod64.inv. This runs once per pivot column, so it never appears in the inner loop and does not need a word-level extended-Euclid specialization.

              Equations
              Instances For
                @[simp]

                The packed inverse of a packed word is the packed inverse residue.

                invMod returns a reduced residue for any input word.

                theorem Hex.Berlekamp.Packed.toZMod_eq_zero_iff {p : Nat} [ZMod64.Bounds p] {a : UInt32} (ha : a.toNat < p) :
                toZMod p a = 0 a = 0

                A packed word is zero exactly when the residue it represents is zero.

                def Hex.Berlekamp.Packed.Rep {p : Nat} [ZMod64.Bounds p] {n m : Nat} (A : Matrix UInt32 n m) (E : Matrix (ZMod64 p) n m) :

                The packed matrix A represents the residue matrix E.

                Equations
                Instances For
                  theorem Hex.Berlekamp.Packed.Rep.lt {p : Nat} [ZMod64.Bounds p] {n m : Nat} {A : Matrix UInt32 n m} {E : Matrix (ZMod64 p) n m} (h : Rep A E) (i : Fin n) (j : Fin m) :
                  A[i][j].toNat < p

                  Every entry of a representing packed matrix is a reduced residue word, because the residues it represents are canonical.

                  theorem Hex.Berlekamp.Packed.Rep.toZMod_eq {p : Nat} [ZMod64.Bounds p] {n m : Nat} {A : Matrix UInt32 n m} {E : Matrix (ZMod64 p) n m} (h : Rep A E) (i : Fin n) (j : Fin m) :
                  toZMod p A[i][j] = E[i][j]

                  Reading an entry of a representing packed matrix recovers the residue entry. This is the form Rep is consumed in once the goal is stated over residues rather than over toNat.

                  def Hex.Berlekamp.Packed.rowScale {n m : Nat} (q : UInt32) (A : Matrix UInt32 n m) (i : Fin n) (c : UInt32) :

                  Scale row i of a packed matrix by the packed residue c. Mirrors Hex.Matrix.rowScale, which is the same per-entry in-place Hex.Matrix.modifyEntries with the generic product replaced by the modular one.

                  Equations
                  Instances For
                    @[inline]
                    def Hex.Berlekamp.Packed.rowAddFrom {n m : Nat} (q : UInt32) (A : Matrix UInt32 n m) (rsrc : Vector UInt32 m) (dst : Fin n) (c : UInt32) :

                    Replace row dst of a packed matrix by row dst + c * rsrc, with the source row supplied by the caller instead of read out of the matrix.

                    rsrc must be a freshly materialized row, as Hex.Matrix.getRow produces. A borrow of A's own backing buffer would leave that buffer multiply referenced, and the Hex.Matrix.modifyEntries below would copy the whole matrix per row addition instead of writing in place.

                    Equations
                    Instances For
                      def Hex.Berlekamp.Packed.rowAdd {n m : Nat} (q : UInt32) (A : Matrix UInt32 n m) (src dst : Fin n) (c : UInt32) :

                      Replace row dst of a packed matrix by row dst + c * row src. Mirrors Hex.Matrix.rowAdd: the source row is read once, then the destination row's entries are updated in place.

                      Equations
                      Instances For
                        theorem Hex.Berlekamp.Packed.getElem_rowScale {n m : Nat} (q : UInt32) (A : Matrix UInt32 n m) (i r : Fin n) (c : UInt32) (k : Fin m) :
                        (rowScale q A i c)[r][k] = if r = i then mulMod q c A[i][k] else A[r][k]

                        Entrywise characterisation of rowScale: row i is scaled, every other row is untouched.

                        theorem Hex.Berlekamp.Packed.getElem_rowAddFrom {n m : Nat} (q : UInt32) (A : Matrix UInt32 n m) (rsrc : Vector UInt32 m) (dst r : Fin n) (c : UInt32) (k : Fin m) :
                        (rowAddFrom q A rsrc dst c)[r][k] = if r = dst then addMod q A[dst][k] (mulMod q c rsrc[k]) else A[r][k]

                        Entrywise characterisation of rowAddFrom: row dst gains c * rsrc, every other row is untouched.

                        theorem Hex.Berlekamp.Packed.getElem_rowAdd {n m : Nat} (q : UInt32) (A : Matrix UInt32 n m) (src dst r : Fin n) (c : UInt32) (k : Fin m) :
                        (rowAdd q A src dst c)[r][k] = if r = dst then addMod q A[dst][k] (mulMod q c A[src][k]) else A[r][k]

                        Entrywise characterisation of rowAdd, the rowAddFrom special case that reads the source row out of the matrix.

                        theorem Hex.Berlekamp.Packed.Rep.rowSwap {p : Nat} [ZMod64.Bounds p] {n m : Nat} {A : Matrix UInt32 n m} {E : Matrix (ZMod64 p) n m} (h : Rep A E) (i j : Fin n) :
                        Rep (A.rowSwap i j) (E.rowSwap i j)

                        A packed row swap interprets the reference row swap.

                        theorem Hex.Berlekamp.Packed.Rep.rowScale {p : Nat} [ZMod64.Bounds p] {n m : Nat} {q : UInt32} (hq : q.toNat = p) {A : Matrix UInt32 n m} {E : Matrix (ZMod64 p) n m} (h : Rep A E) (i : Fin n) {c : UInt32} {γ : ZMod64 p} (hc : c.toNat = γ.toNat) :
                        Rep (Packed.rowScale q A i c) (E.rowScale i γ)

                        A packed row scaling by a word representing γ interprets the reference row scaling by γ.

                        theorem Hex.Berlekamp.Packed.Rep.rowAddFrom {p : Nat} [ZMod64.Bounds p] {n m : Nat} {q : UInt32} (hq : q.toNat = p) {A : Matrix UInt32 n m} {E : Matrix (ZMod64 p) n m} (h : Rep A E) {rsrc : Vector UInt32 m} (src dst : Fin n) (hsrc : ∀ (k : Fin m), rsrc[k] = A[src][k]) {c : UInt32} {γ : ZMod64 p} (hc : c.toNat = γ.toNat) :
                        Rep (Packed.rowAddFrom q A rsrc dst c) (E.rowAdd src dst γ)

                        A caller-supplied source row that agrees entrywise with row src of the packed matrix interprets the reference row addition, just as reading the row out of the matrix does.

                        theorem Hex.Berlekamp.Packed.Rep.rowAdd {p : Nat} [ZMod64.Bounds p] {n m : Nat} {q : UInt32} (hq : q.toNat = p) {A : Matrix UInt32 n m} {E : Matrix (ZMod64 p) n m} (h : Rep A E) (src dst : Fin n) {c : UInt32} {γ : ZMod64 p} (hc : c.toNat = γ.toNat) :
                        Rep (Packed.rowAdd q A src dst c) (E.rowAdd src dst γ)

                        A packed row addition by a word representing γ interprets the reference row addition by γ.

                        def Hex.Berlekamp.Packed.findPivotAux {n m : Nat} (A : Matrix UInt32 n m) (col : Fin m) (start fuel : Nat) :

                        Packed pivot search: the first row at or below start whose col entry is nonzero. Mirrors Hex.Matrix.findPivotAux.

                        Equations
                        Instances For
                          def Hex.Berlekamp.Packed.findPivot? {n m : Nat} (A : Matrix UInt32 n m) (col : Fin m) (start : Nat) :

                          Packed pivot search from start. Mirrors Hex.Matrix.findPivot?.

                          Equations
                          Instances For
                            def Hex.Berlekamp.Packed.eliminateColumn {n m : Nat} (q : UInt32) (A : Matrix UInt32 n m) (pivotRow : Fin n) (col : Fin m) :

                            Eliminate every non-pivot entry of a packed pivot column. Mirrors Hex.Matrix.eliminateColumn with the transform component dropped.

                            The pivot row is read once for the whole column rather than once per row addition: the fold skips j = pivotRow, so nothing it writes can change what the next row addition reads. A dense column's n - 1 source-row copies become one.

                            Equations
                            • One or more equations did not get rendered due to their size.
                            Instances For

                              Running state of the packed Gauss-Jordan loop. Mirrors Hex.Matrix.RowReduceState without the transform.

                              • row : Nat

                                Rows already assigned a pivot.

                              • echelon : Matrix UInt32 n m

                                The partially reduced packed matrix.

                              • pivots : List (Fin m)

                                Pivot columns found so far, in increasing order.

                              Instances For
                                def Hex.Berlekamp.Packed.reduceLoop {n m : Nat} (p : Nat) [ZMod64.Bounds p] [ZMod64.PrimeModulus p] (q : UInt32) (col fuel : Nat) (s : State n m) :
                                State n m

                                The packed Gauss-Jordan loop. Mirrors Hex.Matrix.rowReduceLoop.

                                Equations
                                Instances For
                                  theorem Hex.Berlekamp.Packed.rep_eliminateColumn_foldl {p : Nat} [ZMod64.Bounds p] {n m : Nat} {q : UInt32} (hq : q.toNat = p) (pivotRow : Fin n) (col : Fin m) (rsrc : Vector UInt32 m) (xs : List (Fin n)) (A : Matrix UInt32 n m) (st : Matrix (ZMod64 p) n m × Matrix (ZMod64 p) n n) :
                                  Rep A st.fst(∀ (k : Fin m), rsrc[k] = A[pivotRow][k])Rep (List.foldl (fun (A : Matrix UInt32 n m) (j : Fin n) => if j = pivotRow then A else have c := negMod q A[(j, col)]; if (c == 0) = true then A else rowAddFrom q A rsrc j c) A xs) (List.foldl (fun (st : Matrix (ZMod64 p) n m × Matrix (ZMod64 p) n n) (j : Fin n) => if _hj : j = pivotRow then st else have coeff := -st.fst[(j, col)]; if coeff = 0 then st else (st.fst.rowAdd pivotRow j coeff, st.snd.rowAdd pivotRow j coeff)) st xs).fst

                                  The packed column elimination represents the reference column elimination. The source row rsrc is hoisted out of the fold, so the fold carries the invariant that it still agrees with the accumulated buffer's pivot row.

                                  theorem Hex.Berlekamp.Packed.reduceLoop_sim {p : Nat} [ZMod64.Bounds p] {n m : Nat} [ZMod64.PrimeModulus p] {q : UInt32} (hq : q.toNat = p) (fuel col row : Nat) (pivots : List (Fin m)) (A : Matrix UInt32 n m) (E : Matrix (ZMod64 p) n m) (T : Matrix (ZMod64 p) n n) :
                                  Rep A E(reduceLoop p q col fuel { row := row, echelon := A, pivots := pivots }).row = (Matrix.rowReduceLoop col fuel { row := row, echelon := E, transform := T, pivots := pivots }).row (reduceLoop p q col fuel { row := row, echelon := A, pivots := pivots }).pivots = (Matrix.rowReduceLoop col fuel { row := row, echelon := E, transform := T, pivots := pivots }).pivots Rep (reduceLoop p q col fuel { row := row, echelon := A, pivots := pivots }).echelon (Matrix.rowReduceLoop col fuel { row := row, echelon := E, transform := T, pivots := pivots }).echelon

                                  The packed Gauss-Jordan loop simulates Hex.Matrix.rowReduceLoop: it finds the same pivot columns in the same order, advances the same row counter, and its packed buffer represents the reference echelon matrix at every step. The reference transform is carried along on the right and never inspected.

                                  Packed Gauss-Jordan elimination of A. Mirrors Hex.Matrix.rowReduce without the transform.

                                  Equations
                                  Instances For
                                    theorem Hex.Berlekamp.Packed.reduce_pivots {p : Nat} [ZMod64.Bounds p] {n m : Nat} [ZMod64.PrimeModulus p] {q : UInt32} (hq : q.toNat = p) {A : Matrix UInt32 n m} {E : Matrix (ZMod64 p) n m} (h : Rep A E) :

                                    The packed reduction finds the reference pivot columns.

                                    theorem Hex.Berlekamp.Packed.reduce_rank {p : Nat} [ZMod64.Bounds p] {n m : Nat} [ZMod64.PrimeModulus p] {q : UInt32} (hq : q.toNat = p) {A : Matrix UInt32 n m} {E : Matrix (ZMod64 p) n m} (h : Rep A E) :

                                    The packed reduction finds the reference rank.

                                    theorem Hex.Berlekamp.Packed.reduce_rep {p : Nat} [ZMod64.Bounds p] {n m : Nat} [ZMod64.PrimeModulus p] {q : UInt32} (hq : q.toNat = p) {A : Matrix UInt32 n m} {E : Matrix (ZMod64 p) n m} (h : Rep A E) :

                                    The packed reduction's buffer represents the reference echelon form.

                                    The pivot row of column j, searching from row i. Mirrors Hex.Matrix.IsRowReduced.pivotIndexAux walking the pivot-column list.

                                    Equations
                                    Instances For
                                      def Hex.Berlekamp.Packed.freeColsList {m : Nat} (pivots : List (Fin m)) :
                                      List (Fin m)

                                      The non-pivot columns, in increasing order. Mirrors Hex.Matrix.IsEchelonForm.freeColsList with the echelon witness dropped.

                                      Equations
                                      Instances For
                                        def Hex.Berlekamp.Packed.nullspaceArray {p : Nat} [ZMod64.Bounds p] {n m : Nat} (q : UInt32) (A : Matrix UInt32 n m) (pivots : List (Fin m)) :

                                        The nullspace basis read straight off a reduced packed buffer: one vector per free column, with a 1 in its own free column, the negated pivot-row entry in each pivot column, and 0 elsewhere.

                                        The i < n test is never false on a reduction's own output -- there are at most n pivots -- and nullspaceArray_eq discharges it.

                                        Equations
                                        • One or more equations did not get rendered due to their size.
                                        Instances For
                                          theorem Hex.Berlekamp.Packed.nullspaceArray_eq {p : Nat} [ZMod64.Bounds p] {n m : Nat} [ZMod64.PrimeModulus p] {q : UInt32} (hq : q.toNat = p) {E : Matrix (ZMod64 p) n m} {A : Matrix UInt32 n m} (h : Rep A E.rowReduce.echelon) {pivots : List (Fin m)} (hpv : pivots = E.rowReduce.pivotCols.toList) :

                                          The packed nullspace readback reproduces Hex.Matrix.nullspace.

                                          The fixed-space matrix Q_f - I, built directly into a packed buffer from the Berlekamp column polynomials.

                                          Equations
                                          • One or more equations did not get rendered due to their size.
                                          Instances For
                                            theorem Hex.Berlekamp.Packed.fixedSpace_rep {p : Nat} [ZMod64.Bounds p] (f : FpPoly p) (hmonic : DensePoly.Monic f) :
                                            Rep (fixedSpace p f hmonic) (fixedSpaceMatrix f hmonic)

                                            The packed fixed-space buffer represents Hex.Berlekamp.fixedSpaceMatrix.

                                            The Berlekamp fixed-space kernel basis, computed on the packed representation: build the packed Q_f - I, reduce it in place with the specialized Gauss-Jordan loop, and read the basis off the reduced buffer.

                                            Equations
                                            • One or more equations did not get rendered due to their size.
                                            Instances For

                                              The correspondence theorem. The packed kernel computation returns exactly Hex.Matrix.nullspace of the fixed-space matrix.

                                              @[csimp]

                                              The packed fixed-space kernel is the generic one. Registered @[csimp], so compiled code runs the packed reduction while every Berlekamp soundness and completeness proof keeps reasoning about Matrix (ZMod64 p) and Hex.Matrix.nullspace.

                                              The fixed-space kernel basis converted back to polynomial representatives.

                                              Compute the vector basis once and map the conversion over it. Keep the basis outside any per-index body so matrix construction and row reduction remain shared; mapping also carries its dependent length without recomputing rank.

                                              Equations
                                              Instances For

                                                Every polynomial representative returned by fixedSpaceKernel satisfies the executable fixed-space kernel condition.