Documentation

Complexitylib.Algebraic.LowerBound.KarchmerWigderson.Sharing.Circuit

From De Morgan circuits to programs with shared gates #

The fan-out of a gate of a circuit is the number of argument slots of gates that read it plus the number of designated outputs equal to it (gateFanOut), and sharedGateCount counts the gates of fan-out at least two. Every single-output circuit over the De Morgan basis computing f yields a SharedProgram computing f with at most sharedGateCount c shared gates and at most as many AND and OR gates as the circuit (exists_sharedProgram_of_circuit); NOT, identity, and constant gates are free, as in DeMorgan.binaryCost.

The program is built from the last gate down. Its variables are all wires of the circuit, so the initial program outputs the literal of the output wire. A gate of fan-out at least two becomes a shared gate computing its formula, and its wire is then exactly the new shared variable (SharedProgram.share). Any other gate is substituted into the program (SharedProgram.substVar); since its variable occurs at most once, this adds the gate's own cost and keeps every earlier gate occurring at most as often as its fan-out.

Consequently Khrapchenko's bound with shared gates holds for the library's De Morgan circuits: a circuit computing the parity of n ≥ 1 bits in which at most k gates have fan-out at least two has n² ≤ (k + 1) · (cost + k + 1), where cost counts the AND and OR gates (parity_sq_le_cost). It has at least C · n AND and OR gates when (k + 1) · (C + 1) ≤ n (mul_le_cost_of_parity), and a family of such circuits with k(n) = o(n) gates of fan-out at least two has superlinear cost (isLittleO_cost_of_parity). Analogous quadratic and superlinear bounds hold for threshold and majority circuits (threshold_mul_le_cost, majority_mul_le_cost, majority_sq_le_cost_of_odd, mul_le_cost_of_majority, isLittleO_cost_of_majority).

Fan-out of circuit gates #

def Algebraic.KW.argUses {σ : Signature} {n t : ℕ} (line : Line σ n t) (g : Fin t) :

The number of arguments of a gate that are equal to gate g.

Equations
Instances For
    def Algebraic.KW.slotUses {σ : Signature} {n t : ℕ} :
    Program σ n t → Fin t → ℕ

    The number of argument slots of the program's gates that read gate g. The last gate is read by no gate of the program; an earlier gate is read by the gates before the last one and by the arguments of the last gate that are equal to it.

    Equations
    Instances For
      def Algebraic.KW.gateFanOut {σ : Signature} {n m : ℕ} (c : Circuit σ n m) (g : Fin c.size) :

      The fan-out of gate g of a circuit: the number of argument slots of gates that read g plus the number of designated outputs equal to g.

      Equations
      Instances For
        def Algebraic.KW.sharedGateCount {σ : Signature} {n m : ℕ} (c : Circuit σ n m) :

        The number of gates of a circuit whose fan-out is at least two.

        Equations
        Instances For
          @[simp]
          theorem Algebraic.KW.slotUses_gate_last {σ : Signature} {n t : ℕ} (p : Program σ n t) (line : Line σ n t) :
          slotUses (p.gate line) (Fin.last t) = 0
          @[simp]
          theorem Algebraic.KW.slotUses_gate_castSucc {σ : Signature} {n t : ℕ} (p : Program σ n t) (line : Line σ n t) (g : Fin t) :
          slotUses (p.gate line) g.castSucc = slotUses p g + argUses line g
          def Algebraic.KW.sharedCount {σ : Signature} {n t : ℕ} (p : Program σ n t) (u : Fin t → ℕ) :

          The number of gates of p whose fan-out is at least two when, besides the gates of p, gate g is read u g more times.

          Equations
          Instances For
            theorem Algebraic.KW.sharedGateCount_eq {σ : Signature} {n m : ℕ} (c : Circuit σ n m) :
            sharedGateCount c = sharedCount c.program fun (g : Fin c.size) => {o : Fin m | c.outputs o = Wire.gate g}.card

            Occurrences and substitution of a variable #

            def Algebraic.KW.insertVal {N d : ℕ} (y : Fin (N + d) → Bool) (b : Bool) :
            Fin (N + 1 + d) → Bool

            Insert the value b at position N of y, moving the later entries up by one.

            Equations
            Instances For
              theorem Algebraic.KW.insertVal_zero {N : ℕ} (y : Fin N → Bool) (b : Bool) :
              theorem Algebraic.KW.insertVal_snoc {N d : ℕ} (y : Fin (N + d) → Bool) (g b : Bool) :
              @[simp]
              theorem Algebraic.KW.Formula.occ_neg {N : ℕ} (v : ℕ) (F : Formula N) :
              occ v F.neg = occ v F
              @[simp]
              theorem Algebraic.KW.Formula.occ_mapIndex_castLE {N M : ℕ} (v : ℕ) (h : N ≤ M) (F : Formula N) :
              occ v (mapIndex (Fin.castLE h) F) = occ v F
              def Algebraic.KW.Formula.substVarMap {N : ℕ} (φ : Formula N) (d : ℕ) (i : Fin (N + 1 + d)) :
              Formula (N + d)

              The substitution behind SharedProgram.substVar: the variable N becomes φ, the variables below N are kept, and those above N move down by one.

              Equations
              Instances For
                theorem Algebraic.KW.Formula.gates_subst_substVarMap {N : ℕ} (φ : Formula N) (d : ℕ) (F : Formula (N + 1 + d)) :
                (subst (φ.substVarMap d) F).gates = F.gates + occ N F * φ.gates
                theorem Algebraic.KW.Formula.occ_subst_substVarMap {N : ℕ} (φ : Formula N) (d : ℕ) {w : ℕ} (hw : w < N) (F : Formula (N + 1 + d)) :
                occ w (subst (φ.substVarMap d) F) = occ w F + occ N F * occ w φ
                theorem Algebraic.KW.Formula.eval_subst_substVarMap {N : ℕ} (φ : Formula N) (d : ℕ) (F : Formula (N + 1 + d)) (y : Fin (N + d) → Bool) :
                (subst (φ.substVarMap d) F).eval y = F.eval (insertVal y (φ.eval fun (j : Fin N) => y (Fin.castLE ⋯ j)))
                @[simp]
                @[simp]
                theorem Algebraic.KW.SharedProgram.occ_share {N k : ℕ} (v : ℕ) (G : Formula N) (P : SharedProgram (N + 1) k) :
                occ v (share G P) = Formula.occ v G + occ v P
                @[simp]
                theorem Algebraic.KW.SharedProgram.gates_share {N k : ℕ} (G : Formula N) (P : SharedProgram (N + 1) k) :
                (share G P).gates = G.gates + P.gates
                def Algebraic.KW.SharedProgram.substVar {N : ℕ} (φ : Formula N) {d k : ℕ} :
                SharedProgram (N + 1 + d) k → SharedProgram (N + d) k

                Substitute the formula φ for the variable N of a program reading N + 1 + d variables, the last d of them shared values: the variables above N move down by one, and the program then reads φ wherever it read variable N.

                Equations
                Instances For
                  theorem Algebraic.KW.SharedProgram.eval_substVar {N : ℕ} (φ : Formula N) {d k : ℕ} (P : SharedProgram (N + 1 + d) k) (y : Fin (N + d) → Bool) :
                  (substVar φ P).eval y = P.eval (insertVal y (φ.eval fun (j : Fin N) => y (Fin.castLE ⋯ j)))
                  theorem Algebraic.KW.SharedProgram.gates_substVar {N : ℕ} (φ : Formula N) {d k : ℕ} (P : SharedProgram (N + 1 + d) k) :
                  (substVar φ P).gates = P.gates + occ N P * φ.gates
                  theorem Algebraic.KW.SharedProgram.occ_substVar {N : ℕ} (φ : Formula N) {w : ℕ} (hw : w < N) {d k : ℕ} (P : SharedProgram (N + 1 + d) k) :
                  occ w (substVar φ P) = occ w P + occ N P * Formula.occ w φ

                  De Morgan gates as formulas #

                  def Algebraic.KW.wireValues {n t : ℕ} (p : Program DeMorgan.signature n t) (x : Fin n → Bool) :
                  Fin (n + t) → Bool

                  The values of the inputs and gates of a De Morgan program, numbered by Wire.index.

                  Equations
                  Instances For
                    theorem Algebraic.KW.append_index {n t : ℕ} (x : Fin n → Bool) (vals : Fin t → Bool) (w : Wire n t) :
                    Fin.append x vals w.index = Wire.elim x vals w
                    theorem Algebraic.KW.eval_lineFormula {n t : ℕ} (line : Line DeMorgan.signature n t) (x : Fin n → Bool) (vals : Fin t → Bool) :
                    theorem Algebraic.KW.index_val_eq_iff {n t : ℕ} (w : Wire n t) (g : Fin t) :
                    ↑w.index = n + ↑g ↔ w = Wire.gate g
                    theorem Algebraic.KW.occ_lineFormula_le {n t : ℕ} (line : Line DeMorgan.signature n t) (g : Fin t) :
                    Formula.occ (n + ↑g) (lineFormula line) ≤ argUses line g

                    From circuits to programs #

                    theorem Algebraic.KW.sharedCount_gate {σ : Signature} {n t : ℕ} (p : Program σ n t) (line : Line σ n t) (u : Fin (t + 1) → ℕ) :
                    sharedCount (p.gate line) u = (if 2 ≤ u (Fin.last t) then 1 else 0) + sharedCount p fun (g : Fin t) => u g.castSucc + argUses line g
                    theorem Algebraic.KW.exists_sharedProgram_of_program {n : ℕ} (f : Cslib.BooleanFunction n) {t : ℕ} (p : Program DeMorgan.signature n t) (u : Fin t → ℕ) {j : ℕ} (Q : SharedProgram (n + t) j) :
                    (∀ (x : Fin n → Bool), Q.eval (wireValues p x) = f x) → (∀ (g : Fin t), SharedProgram.occ (n + ↑g) Q ≤ u g) → ∃ (k : ℕ) (P : SharedProgram n k), k ≤ j + sharedCount p u ∧ P.Computes f ∧ P.gates ≤ Q.gates + Program.cost DeMorgan.binaryCost p

                    Build a program from the last gate of a De Morgan program down. Q reads all wires of p, gate g occurring at most u g times; a gate of total fan-out slotUses p g + u g at least two becomes a shared gate and any other gate is substituted.

                    De Morgan circuits as programs with shared gates. A single-output circuit over the De Morgan basis computing f yields a program computing f with at most as many shared gates as the circuit has gates of fan-out at least two, and at most as many AND and OR gates as the circuit.

                    Parity #

                    Parity in De Morgan circuits with few shared gates. If a De Morgan circuit computes the parity of n ≥ 1 bits and at most k of its gates have fan-out at least two, then n² ≤ (k + 1) · (cost + k + 1), where cost counts its AND and OR gates; so it has at least n² / (k + 1) - (k + 1) AND and OR gates.

                    Superlinear cost for parity with few shared gates. If (k + 1) · (C + 1) ≤ n, every De Morgan circuit computing the parity of n bits in which at most k gates have fan-out at least two has at least C · n AND and OR gates.

                    theorem Algebraic.KW.isLittleO_cost_of_parity {k : ℕ → ℕ} (hk : (fun (n : ℕ) => ↑(k n)) =o[Filter.atTop] fun (n : ℕ) => ↑n) (c : (n : ℕ) → Circuit DeMorgan.signature n 1) (computes : ∀ (n : ℕ), (c n).ComputesWith DeMorgan.interpretation (GateElimination.Xor.parityTarget n)) (hshared : ∀ (n : ℕ), sharedGateCount (c n) ≤ k n) :
                    (fun (n : ℕ) => ↑n) =o[Filter.atTop] fun (n : ℕ) => ↑((c n).cost DeMorgan.binaryCost)

                    Parity needs superlinear De Morgan circuits when o(n) gates have fan-out at least two. If k n = o(n) and, for every n, c n is a De Morgan circuit computing the parity of n bits in which at most k n gates have fan-out at least two, then the number of AND and OR gates of c n grows faster than n.

                    Threshold and majority #

                    @[reducible, inline]

                    Single-output circuit target for the t-threshold function on n bits.

                    Equations
                    Instances For
                      @[reducible, inline]

                      Single-output circuit target for the strict majority function on n bits.

                      Equations
                      Instances For
                        theorem Algebraic.KW.threshold_mul_le_cost {n t k : ℕ} (ht : 1 ≤ t) (htn : t ≤ n) (c : Circuit DeMorgan.signature n 1) (computes : c.ComputesWith DeMorgan.interpretation (thresholdTarget n t)) (hk : sharedGateCount c ≤ k) :
                        (n + 1 - t) * t ≤ (k + 1) * (c.cost DeMorgan.binaryCost + k + 1)

                        Threshold functions in De Morgan circuits with few shared gates. For 1 ≤ t ≤ n, every De Morgan circuit computing threshold t in which at most k gates have fan-out at least two satisfies (n + 1 - t) · t ≤ (k + 1) · (cost + k + 1), where cost counts its AND and OR gates.

                        theorem Algebraic.KW.majority_mul_le_cost {n k : ℕ} (hn : 1 ≤ n) (c : Circuit DeMorgan.signature n 1) (computes : c.ComputesWith DeMorgan.interpretation (majorityTarget n)) (hk : sharedGateCount c ≤ k) :
                        (n - n / 2) * (n / 2 + 1) ≤ (k + 1) * (c.cost DeMorgan.binaryCost + k + 1)

                        Majority in De Morgan circuits with few shared gates. Every De Morgan circuit computing strict majority of n ≥ 1 bits in which at most k gates have fan-out at least two satisfies (n - ⌊n/2⌋) · (⌊n/2⌋ + 1) ≤ (k + 1) · (cost + k + 1), where cost counts its AND and OR gates.

                        Odd-input majority in De Morgan circuits with few shared gates. For odd n, every De Morgan circuit computing strict majority of n bits in which at most k gates have fan-out at least two satisfies (⌊n/2⌋ + 1)² ≤ (k + 1) · (cost + k + 1).

                        theorem Algebraic.KW.mul_le_cost_of_majority {m k C : ℕ} (hkm : (k + 1) * (C + 1) ≤ m) (c : Circuit DeMorgan.signature (2 * m + 1) 1) (computes : c.ComputesWith DeMorgan.interpretation (majorityTarget (2 * m + 1))) (hk : sharedGateCount c ≤ k) :

                        Superlinear cost for majority with few shared gates. If (k + 1) · (C + 1) ≤ m, every De Morgan circuit computing the majority of 2 * m + 1 bits in which at most k gates have fan-out at least two has at least C · m AND and OR gates.

                        theorem Algebraic.KW.isLittleO_cost_of_majority {k : ℕ → ℕ} (hk : (fun (m : ℕ) => ↑(k m)) =o[Filter.atTop] fun (m : ℕ) => ↑m) (c : (m : ℕ) → Circuit DeMorgan.signature (2 * m + 1) 1) (computes : ∀ (m : ℕ), (c m).ComputesWith DeMorgan.interpretation (majorityTarget (2 * m + 1))) (hshared : ∀ (m : ℕ), sharedGateCount (c m) ≤ k m) :
                        (fun (m : ℕ) => ↑m) =o[Filter.atTop] fun (m : ℕ) => ↑((c m).cost DeMorgan.binaryCost)

                        Majority needs superlinear De Morgan circuits when o(m) gates have fan-out at least two. If k m = o(m) and, for every m, c m is a De Morgan circuit computing the majority of 2 * m + 1 bits in which at most k m gates have fan-out at least two, then the number of AND and OR gates of c m grows faster than m.