Documentation

Complexitylib.Circuits.Barrington

Toward Barrington's theorem: the group-theoretic core #

Barrington's theorem builds a width-5 permutation branching program for any NC¹ formula by representing a Boolean function through a fixed non-identity permutation. This module formalizes the reusable, group-theoretic heart of that construction (roadmap track M3), independent of the eventual S₅ choice of permutations.

The central notion is BP.Computes σ p f: the program p represents the Boolean function f through the permutation σ, meaning it evaluates to σ exactly when f holds and to 1 (the identity) otherwise. The closure lemmas proved here are the moves in Barrington's inductive construction:

Main definitions and results #

Invert a branching-program instruction: keep the variable read, invert both branch permutations.

Equations
Instances For
    @[simp]
    theorem Complexity.BPInstr.eval_inverse {w : } (α : Bool) (ins : BPInstr w) :
    eval α ins.inverse = (eval α ins)⁻¹

    Inverting an instruction inverts the permutation it selects.

    A constant instruction applies the permutation c regardless of the input.

    Equations
    Instances For
      @[simp]
      theorem Complexity.BPInstr.eval_const {w : } (α : Bool) (c : Equiv.Perm (Fin w)) :
      eval α (const c) = c

      A constant instruction always evaluates to its permutation.

      def Complexity.BPInstr.conjugate {w : } (τ : Equiv.Perm (Fin w)) (ins : BPInstr w) :

      Conjugate both branches of an instruction by the same permutation.

      Equations
      Instances For
        @[simp]
        theorem Complexity.BPInstr.eval_conjugate {w : } (α : Bool) (τ : Equiv.Perm (Fin w)) (ins : BPInstr w) :
        eval α (conjugate τ ins) = τ * eval α ins * τ⁻¹

        Pointwise instruction conjugation realizes group conjugation.

        def Complexity.BPInstr.postMul {w : } (ins : BPInstr w) (c : Equiv.Perm (Fin w)) :

        Right-multiply both branches of an instruction by a fixed permutation.

        Equations
        Instances For
          @[simp]
          theorem Complexity.BPInstr.eval_postMul {w : } (α : Bool) (ins : BPInstr w) (c : Equiv.Perm (Fin w)) :
          eval α (ins.postMul c) = eval α ins * c

          Right-multiplication commutes with selecting an instruction branch.

          def Complexity.BP.conjugate {w : } (τ : Equiv.Perm (Fin w)) (p : BP w) :
          BP w

          Conjugate every instruction of a branching program. Unlike wrapping with constant instructions, this operation preserves length exactly.

          Equations
          Instances For
            theorem Complexity.BP.eval_conjugate {w : } (α : Bool) (τ : Equiv.Perm (Fin w)) (p : BP w) :
            eval α (conjugate τ p) = τ * eval α p * τ⁻¹

            Pointwise conjugation conjugates the value of the whole program.

            @[simp]

            Pointwise conjugation preserves program length exactly.

            def Complexity.BP.postMul {w : } (p : BP w) (c : Equiv.Perm (Fin w)) :
            BP w

            Fold a final constant multiplication into the last instruction. The empty program has no last instruction, so it becomes a singleton constant program.

            Equations
            Instances For
              theorem Complexity.BP.eval_postMul {w : } (α : Bool) (p : BP w) (c : Equiv.Perm (Fin w)) :
              eval α (p.postMul c) = eval α p * c

              Folding a final constant into the last instruction right-multiplies the program value.

              theorem Complexity.BP.length_postMul {w : } (p : BP w) (c : Equiv.Perm (Fin w)) :

              Folding a final constant uses the original length, except that an empty program needs one instruction.

              def Complexity.BP.inverse {w : } (p : BP w) :
              BP w

              Invert a branching program: reverse the instruction list and invert each instruction.

              Equations
              Instances For
                theorem Complexity.BP.eval_inverse {w : } (α : Bool) (p : BP w) :
                eval α p.inverse = (eval α p)⁻¹

                Inversion is correct: the inverted program evaluates to the inverse of the original program's permutation.

                def Complexity.BP.Computes {w : } (σ : Equiv.Perm (Fin w)) (p : BP w) (f : (Bool)Bool) :

                Computes σ p f : the branching program p represents the Boolean function f through the permutation σ — it evaluates to σ when f holds and to the identity otherwise.

                Equations
                Instances For
                  theorem Complexity.BP.Computes_conj {w : } {σ : Equiv.Perm (Fin w)} {p : BP w} {f : (Bool)Bool} (h : Computes σ p f) (τ : Equiv.Perm (Fin w)) :

                  Conjugation of the representing permutation. Wrapping a program between a constant τ and a constant τ⁻¹ conjugates the permutation it represents.

                  theorem Complexity.BP.Computes_conjugate {w : } {σ : Equiv.Perm (Fin w)} {p : BP w} {f : (Bool)Bool} (h : Computes σ p f) (τ : Equiv.Perm (Fin w)) :
                  Computes (τ * σ * τ⁻¹) (conjugate τ p) f

                  Length-preserving conjugation. Conjugating every instruction changes the representing permutation without adding constant instructions.

                  theorem Complexity.BP.Computes_not {w : } {σ : Equiv.Perm (Fin w)} {p : BP w} {f : (Bool)Bool} (h : Computes σ p f) :
                  Computes σ⁻¹ (p ++ [BPInstr.const σ⁻¹]) fun (α : Bool) => !f α

                  Negation. Appending a constant σ⁻¹ to a program that represents f through σ yields a program representing ¬f through σ⁻¹.

                  theorem Complexity.BP.Computes_not_compact {w : } {σ : Equiv.Perm (Fin w)} {p : BP w} {f : (Bool)Bool} (h : Computes σ p f) :
                  Computes σ⁻¹ (p.postMul σ⁻¹) fun (α : Bool) => !f α

                  Compact negation. Multiplying the final selected permutation by σ⁻¹ represents ¬f through σ⁻¹. The multiplication is folded into the last instruction, so the length becomes only max 1 p.length.

                  theorem Complexity.BP.Computes_and {w : } {σ τ : Equiv.Perm (Fin w)} {p q : BP w} {f g : (Bool)Bool} (hp : Computes σ p f) (hq : Computes τ q g) :
                  Computes σ, τ (p ++ q ++ p.inverse ++ q.inverse) fun (α : Bool) => f α && g α

                  The commutator trick (Barrington's AND gate). If p represents f through σ and q represents g through τ, then the commutator program p q p⁻¹ q⁻¹ represents f ∧ g through the commutator ⁅σ, τ⁆.

                  When both f and g hold the four factors multiply to σ τ σ⁻¹ τ⁻¹ = ⁅σ, τ⁆; if either fails, the corresponding factor collapses to 1 and the product telescopes to the identity.

                  theorem Complexity.BP.Computes_false {w : } (σ : Equiv.Perm (Fin w)) :
                  Computes σ [] fun (x : Bool) => false

                  Base case — constant false. The empty program represents the constant function false through any permutation (it always evaluates to the identity).

                  theorem Complexity.BP.Computes_true {w : } (σ : Equiv.Perm (Fin w)) :
                  Computes σ [BPInstr.const σ] fun (x : Bool) => true

                  Base case — constant true. A single constant-σ instruction represents the constant function true through σ.

                  theorem Complexity.BP.Computes_var {w : } (σ : Equiv.Perm (Fin w)) (i : ) :
                  Computes σ [{ var := i, perm0 := 1, perm1 := σ }] fun (α : Bool) => α i

                  Base case — a literal. The single instruction reading variable i, which applies σ when the bit is true and the identity when it is false, represents the projection fun α => α i through σ.

                  theorem Complexity.BP.Computes_or {w : } {σ τ : Equiv.Perm (Fin w)} {p q : BP w} {f g : (Bool)Bool} (hp : Computes σ p f) (hq : Computes τ q g) :
                  (μ : Equiv.Perm (Fin w)), (r : BP w), Computes μ r fun (α : Bool) => f α || g α

                  Disjunction is representable. By De Morgan (f ∨ g = ¬(¬f ∧ ¬g)), the negation and commutator constructions combine to represent f ∨ g. With the base cases (Computes_false, Computes_true, Computes_var) and negation, this gives a functionally complete set of moves for width-w permutation branching programs — the abstract engine of Barrington's induction.