Documentation

Complexitylib.Circuits.Internal.Simulation

Internal: Completeness of fan-in-2 AND/OR #

This module proves CompleteBasis Basis.andOr2 using the generic simulation lemma CompleteBasis.of_simulation. The proof compiles any circuit over Basis.unboundedAndOr into one over Basis.andOr2 by decomposing each unbounded fan-in gate into a chain of fan-in-2 gates.

Strategy #

Given a circuit c : Circuit Basis.unboundedAndOr N M G, we construct c' : Circuit Basis.andOr2 N M G' with c'.eval = c.eval.

Each original gate with fan-in k is replaced by a chain of chainLen k fan-in-2 gates:

The new circuit's internal gates consist of chains for all original internal gates followed by chains for all original output gates. The new output gates are trivial passthroughs reading the last wire of each output chain.

AndOrOp extensions #

Dual operation: swaps AND ↔ OR. Used for constant-gate construction.

Equations
Instances For

    Identity element for fold: true for AND, false for OR.

    Equations
    Instances For

      The binary operation corresponding to an AndOrOp.

      Equations
      Instances For
        theorem Complexity.AndOrOp.eval_eq_foldl (op : AndOrOp) (n : ) (v : BitString n) :
        op.eval n v = Fin.foldl n (fun (acc : Bool) (i : Fin n) => op.binOp acc (v i)) op.identity

        op.eval is the left fold of op.binOp over the inputs, starting from op.identity.

        op.identity is a left identity for op.binOp.

        theorem Complexity.AndOrOp.binOp_self (op : AndOrOp) (b : Bool) :
        op.binOp b b = b

        op.binOp is idempotent: op.binOp b b = b.

        theorem Complexity.AndOrOp.binOp_assoc (op : AndOrOp) (a b c : Bool) :
        op.binOp (op.binOp a b) c = op.binOp a (op.binOp b c)

        op.binOp is associative.

        theorem Complexity.AndOrOp.dual_const (op : AndOrOp) (b : Bool) :
        (op.dual.eval 2 fun (i : Fin 2) => (if i = 0 then false else true) ^^ b) = op.identity

        The dual-op trick for constants: OR(b, ¬b) = true, AND(b, ¬b) = false.

        theorem Complexity.AndOrOp.passthrough_eq (op : AndOrOp) (v : Bool) :
        (op.eval 2 fun (x : Fin 2) => v) = v

        A passthrough evaluates to the input value.

        eval on 0 inputs gives the identity.

        theorem Complexity.AndOrOp.eval_one (op : AndOrOp) (v : Fin 1Bool) :
        op.eval 1 v = op.binOp op.identity (v 0)

        eval on 1 input gives identity op input.

        Chain length and prefix sums #

        Number of fan-in-2 gates needed to simulate one gate with k inputs.

        Equations
        Instances For
          @[simp]

          A fan-in-0 gate is simulated by a single (constant) gate.

          @[simp]

          A fan-in-1 gate is simulated by a single (passthrough) gate.

          Every chain contains at least one gate.

          For fan-in k ≥ 2, the chain has exactly k - 1 gates.

          Prefix sum: prefixSum f n = f 0 + f 1 + ⋯ + f (n-1).

          Equations
          Instances For
            @[simp]

            The empty prefix sum is 0.

            theorem Complexity.CompileAndOr.prefixSum_succ (f : ) (n : ) :
            prefixSum f (n + 1) = prefixSum f n + f n

            Unfolding lemma: prefixSum f (n + 1) = prefixSum f n + f n.

            theorem Complexity.CompileAndOr.prefixSum_mono (f : ) {i j : } (h : i j) :

            prefixSum f is monotone in the length argument.

            Segment lookup #

            def Complexity.CompileAndOr.segLookup (n : ) (f : ) (idx : ) (h : idx < prefixSum f n) :

            Given a flat index into a segmented array, find the segment and position.

            Equations
            Instances For
              theorem Complexity.CompileAndOr.segLookup_fst_lt (n : ) (f : ) (idx : ) (h : idx < prefixSum f n) :
              (segLookup n f idx h).1 < n

              The segment index returned by segLookup is a valid segment number (< n).

              theorem Complexity.CompileAndOr.segLookup_snd_lt (n : ) (f : ) (idx : ) (h : idx < prefixSum f n) :
              (segLookup n f idx h).2 < f (segLookup n f idx h).1

              The position returned by segLookup lies within its segment's size.

              theorem Complexity.CompileAndOr.segLookup_sum (n : ) (f : ) (idx : ) (h : idx < prefixSum f n) :
              prefixSum f (segLookup n f idx h).1 + (segLookup n f idx h).2 = idx

              segLookup decomposes the flat index: segment offset plus position recovers idx.

              Wire layout definitions #

              Chain size function for internal gates (0-padded beyond G).

              Equations
              Instances For

                Chain size function for output gates (0-padded beyond M).

                Equations
                Instances For

                  Total number of fan-in-2 gates in all chains simulating internal gates.

                  Equations
                  Instances For

                    Total number of fan-in-2 gates in all chains simulating output gates.

                    Equations
                    Instances For

                      Total internal gates in the compiled circuit.

                      Equations
                      Instances For

                        Offset of the chain for internal gate i.

                        Equations
                        Instances For
                          theorem Complexity.CompileAndOr.iChainF_eq {N M G : } [NeZero N] [NeZero M] (c : Circuit Basis.unboundedAndOr N M G) {i : } (hi : i < G) :

                          Within bounds, iChainF is the chain length of the corresponding internal gate.

                          theorem Complexity.CompileAndOr.oChainF_eq {N M G : } [NeZero N] [NeZero M] (c : Circuit Basis.unboundedAndOr N M G) {j : } (hj : j < M) :

                          Within bounds, oChainF is the chain length of the corresponding output gate.

                          theorem Complexity.CompileAndOr.iOffset_succ {N M G : } [NeZero N] [NeZero M] (c : Circuit Basis.unboundedAndOr N M G) {i : } (hi : i < G) :
                          iOffset c (i + 1) = iOffset c i + chainLen (c.gates i, hi).fanIn

                          The chain for internal gate i + 1 starts right after the chain for gate i.

                          The chain for internal gate i fits inside the internal-chain region.

                          theorem Complexity.CompileAndOr.oOffset_succ {N M G : } [NeZero N] [NeZero M] (c : Circuit Basis.unboundedAndOr N M G) {j : } (hj : j < M) :
                          oOffset c (j + 1) = oOffset c j + chainLen (c.outputs j, hj).fanIn

                          The chain for output gate j + 1 starts right after the chain for output gate j.

                          theorem Complexity.CompileAndOr.oOffset_chain_le_G' {N M G : } [NeZero N] [NeZero M] (c : Circuit Basis.unboundedAndOr N M G) {j : } (hj : j < M) :

                          The chain for output gate j fits inside the compiled circuit's gate count.

                          Wire remapping #

                          def Complexity.CompileAndOr.remapWire {N M G : } [NeZero N] [NeZero M] (c : Circuit Basis.unboundedAndOr N M G) (w : Fin (N + G)) :
                          Fin (N + G' c)

                          Map an old wire index to its new position. Input wires are unchanged; internal gate i maps to the last gate of its chain.

                          Equations
                          • One or more equations did not get rendered due to their size.
                          Instances For
                            theorem Complexity.CompileAndOr.remapWire_input {N M G : } [NeZero N] [NeZero M] (c : Circuit Basis.unboundedAndOr N M G) (w : Fin (N + G)) (hw : w < N) :
                            (remapWire c w) = w

                            remapWire fixes input wires (indices below N).

                            theorem Complexity.CompileAndOr.remapWire_gate {N M G : } [NeZero N] [NeZero M] (c : Circuit Basis.unboundedAndOr N M G) {i : } (hi : i < G) :
                            (remapWire c N + i, ) = N + iOffset c i + chainLen (c.gates i, hi).fanIn - 1

                            remapWire sends internal gate i to the last gate of its chain.

                            theorem Complexity.CompileAndOr.remapWire_lt_of_lt {N M G : } [NeZero N] [NeZero M] (c : Circuit Basis.unboundedAndOr N M G) (w : Fin (N + G)) {i : } (hi : i G) (hw : w < N + i) :
                            (remapWire c w) < N + iOffset c i

                            If w < N + i in the old circuit, remapWire w < N + iOffset i in the new.

                            theorem Complexity.CompileAndOr.remapWire_lt_oOffset {N M G : } [NeZero N] [NeZero M] (c : Circuit Basis.unboundedAndOr N M G) (w : Fin (N + G)) (j : ) :
                            (remapWire c w) < N + oOffset c j

                            remapWire maps to a wire that comes before any output chain.

                            Chain gate construction #

                            def Complexity.CompileAndOr.fin2 {α : Sort u_1} (a b : α) :
                            Fin 2α

                            Helper: construct a function Fin 2 → α from two values.

                            Equations
                            Instances For
                              theorem Complexity.CompileAndOr.fin2_zero {α : Sort u_1} (a b : α) :
                              fin2 a b 0, = a

                              fin2 a b at index 0 is a.

                              theorem Complexity.CompileAndOr.fin2_one {α : Sort u_1} (a b : α) :
                              fin2 a b 1, = b

                              fin2 a b at index 1 is b.

                              def Complexity.CompileAndOr.mkChainGate {W : } (hW : 0 < W) (op : AndOrOp) (k : ) (ri : Fin kFin W) (rn : Fin kBool) (base j : ) (hj : j < chainLen k) (hbase : base + chainLen k W) :

                              Build the j-th fan-in-2 gate in a chain for an original gate. Components are split out so projections reduce without unfolding dite.

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

                                Compiled circuit #

                                Gate function for the compiled circuit. Components are separated so projections reduce without going through dite.

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

                                  Output gates: passthroughs reading the last wire of each output chain.

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

                                    The compiled circuit over Basis.andOr2.

                                    Equations
                                    Instances For

                                      Eval equivalence #

                                      theorem Complexity.CompileAndOr.segLookup_of_prefixSum (n : ) (f : ) (i j : ) (hi : i < n) (hj : j < f i) (h : prefixSum f i + j < prefixSum f n) :
                                      segLookup n f (prefixSum f i + j) h = (i, j)

                                      segLookup inverts prefixSum: if idx = prefixSum f i + j and j < f i, then segLookup returns (i, j).

                                      theorem Complexity.CompileAndOr.wireValue_remapWire {N M G : } [NeZero N] [NeZero M] (c : Circuit Basis.unboundedAndOr N M G) (input : BitString N) (w : Fin (N + G)) :
                                      (compileFn c).wireValue input (remapWire c w) = c.wireValue input w

                                      Key lemma: remapWire values in the compiled circuit match the original.

                                      The compiled fan-in-2 circuit computes the same function as the original circuit.

                                      Fan-in-2 AND/OR is functionally complete.