Documentation

Complexitylib.Algebraic.Basis.AC0.Normalization

Dual-rail normalization for AC0 circuits #

This module implements the local compiler underlying input-negation normal form. Every Boolean value is represented by two wires carrying the value and its complement. A source NOT swaps the two rails without adding a gate. A source AND or OR produces both its ordinary output and its De Morgan dual, using exactly two target gates.

The construction is semantic rather than syntactic: the same block translation simulates both Boolean evaluation and the logical-depth interpretation. Thus compilation preserves logical depth exactly and doubles the charged AND/OR cost, including at fan-in zero.

Encode a Boolean together with its complement.

Equations
Instances For

    Duplicate a logical depth across both rails.

    Equations
    Instances For

      A NOT gate swaps the two rails without adding a gate.

      Equations
      Instances For
        @[simp]

        The dual-rail NOT only swaps the rails: it has no gates.

        def Algebraic.AC0.DualRail.andPositiveLine (inputCount : ℕ) :
        Line signature (inputCount * 2) 0

        The positive AND output of a dual-rail AND gadget.

        Equations
        • One or more equations did not get rendered due to their size.
        Instances For
          def Algebraic.AC0.DualRail.andNegativeLine (inputCount : ℕ) :
          Line signature (inputCount * 2) 1

          The complemented output of a dual-rail AND gadget, computed by OR.

          Equations
          • One or more equations did not get rendered due to their size.
          Instances For
            @[simp]
            theorem Algebraic.AC0.DualRail.andPositiveLine_eval {U : Type u_1} (inputCount : ℕ) (target : Interpretation signature U) (input : Fin (inputCount * 2) → U) (gates : Fin 0 → U) :
            (andPositiveLine inputCount).eval target input gates = target (Op.and inputCount) fun (argument : Fin (signature.Arity (Op.and inputCount))) => input (finProdFinEquiv (argument, 0))
            @[simp]
            theorem Algebraic.AC0.DualRail.andNegativeLine_eval {U : Type u_1} (inputCount : ℕ) (target : Interpretation signature U) (input : Fin (inputCount * 2) → U) (gates : Fin 1 → U) :
            (andNegativeLine inputCount).eval target input gates = target (Op.or inputCount) fun (argument : Fin (signature.Arity (Op.or inputCount))) => input (finProdFinEquiv (argument, 1))
            def Algebraic.AC0.DualRail.andCircuit (inputCount : ℕ) :
            Circuit signature (inputCount * 2) 2

            The two-output gadget implementing AND and its complement.

            Equations
            • One or more equations did not get rendered due to their size.
            Instances For
              @[simp]
              theorem Algebraic.AC0.DualRail.andCircuit_size (inputCount : ℕ) :
              (andCircuit inputCount).size = 2

              The dual-rail AND gadget has exactly two gates: an AND for the positive rail and an OR for its complement.

              def Algebraic.AC0.DualRail.orPositiveLine (inputCount : ℕ) :
              Line signature (inputCount * 2) 0

              The positive OR output of a dual-rail OR gadget.

              Equations
              • One or more equations did not get rendered due to their size.
              Instances For
                def Algebraic.AC0.DualRail.orNegativeLine (inputCount : ℕ) :
                Line signature (inputCount * 2) 1

                The complemented output of a dual-rail OR gadget, computed by AND.

                Equations
                • One or more equations did not get rendered due to their size.
                Instances For
                  @[simp]
                  theorem Algebraic.AC0.DualRail.orPositiveLine_eval {U : Type u_1} (inputCount : ℕ) (target : Interpretation signature U) (input : Fin (inputCount * 2) → U) (gates : Fin 0 → U) :
                  (orPositiveLine inputCount).eval target input gates = target (Op.or inputCount) fun (argument : Fin (signature.Arity (Op.or inputCount))) => input (finProdFinEquiv (argument, 0))
                  @[simp]
                  theorem Algebraic.AC0.DualRail.orNegativeLine_eval {U : Type u_1} (inputCount : ℕ) (target : Interpretation signature U) (input : Fin (inputCount * 2) → U) (gates : Fin 1 → U) :
                  (orNegativeLine inputCount).eval target input gates = target (Op.and inputCount) fun (argument : Fin (signature.Arity (Op.and inputCount))) => input (finProdFinEquiv (argument, 1))
                  def Algebraic.AC0.DualRail.orCircuit (inputCount : ℕ) :
                  Circuit signature (inputCount * 2) 2

                  The two-output gadget implementing OR and its complement.

                  Equations
                  • One or more equations did not get rendered due to their size.
                  Instances For
                    @[simp]
                    theorem Algebraic.AC0.DualRail.orCircuit_size (inputCount : ℕ) :
                    (orCircuit inputCount).size = 2

                    The dual-rail OR gadget has exactly two gates: an OR for the positive rail and an AND for its complement.

                    Dual-rail compilation of arbitrary AC0 gates.

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

                      Each dual-rail gadget has gateCount gates.

                      @[simp]
                      theorem Algebraic.AC0.DualRail.notCircuit_eval_zero {U : Type u_1} (target : Interpretation signature U) (input : Fin (1 * 2) → U) :
                      notCircuit.eval target input 0 = input 1
                      @[simp]
                      theorem Algebraic.AC0.DualRail.notCircuit_eval_one {U : Type u_1} (target : Interpretation signature U) (input : Fin (1 * 2) → U) :
                      notCircuit.eval target input 1 = input 0
                      @[simp]
                      theorem Algebraic.AC0.DualRail.andCircuit_eval_zero {U : Type u_1} (inputCount : ℕ) (target : Interpretation signature U) (input : Fin (inputCount * 2) → U) :
                      (andCircuit inputCount).eval target input 0 = target (Op.and inputCount) fun (argument : Fin (signature.Arity (Op.and inputCount))) => input (finProdFinEquiv (argument, 0))
                      @[simp]
                      theorem Algebraic.AC0.DualRail.andCircuit_eval_one {U : Type u_1} (inputCount : ℕ) (target : Interpretation signature U) (input : Fin (inputCount * 2) → U) :
                      (andCircuit inputCount).eval target input 1 = target (Op.or inputCount) fun (argument : Fin (signature.Arity (Op.or inputCount))) => input (finProdFinEquiv (argument, 1))
                      @[simp]
                      theorem Algebraic.AC0.DualRail.orCircuit_eval_zero {U : Type u_1} (inputCount : ℕ) (target : Interpretation signature U) (input : Fin (inputCount * 2) → U) :
                      (orCircuit inputCount).eval target input 0 = target (Op.or inputCount) fun (argument : Fin (signature.Arity (Op.or inputCount))) => input (finProdFinEquiv (argument, 0))
                      @[simp]
                      theorem Algebraic.AC0.DualRail.orCircuit_eval_one {U : Type u_1} (inputCount : ℕ) (target : Interpretation signature U) (input : Fin (inputCount * 2) → U) :
                      (orCircuit inputCount).eval target input 1 = target (Op.and inputCount) fun (argument : Fin (signature.Arity (Op.and inputCount))) => input (finProdFinEquiv (argument, 1))
                      @[simp]
                      theorem Algebraic.AC0.DualRail.encode_zero (value : Bool) :
                      encode value 0 = value
                      @[simp]
                      theorem Algebraic.AC0.DualRail.encode_one (value : Bool) :
                      encode value 1 = !value
                      @[simp]
                      theorem Algebraic.AC0.DualRail.duplicateDepth_apply (depth : ℕ) (rail : Fin 2) :
                      duplicateDepth depth rail = depth

                      Whole-circuit normalization #

                      The first g input-negation gates over an n-input namespace.

                      Equations
                      Instances For

                        Generate the positive and negative literal rails for every input. The encoder has one NOT gate per input, and each such gate reads that input directly.

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

                          The input encoder computes each input together with its complement.

                          @[simp]

                          Input encoding adds no logical depth: both rails inherit their input's arrival time.

                          @[simp]

                          Input encoding has zero charged AND/OR cost.

                          Every NOT in the input encoder reads an original input.

                          Project the positive rail of every compiled output.

                          Equations
                          Instances For
                            @[simp]
                            theorem Algebraic.AC0.DualRail.positiveOutputs_size {n m : ℕ} (circuit : Circuit signature n (m * 2)) :
                            (positiveOutputs circuit).size = circuit.size

                            Keeping only the positive rails adds no gates.

                            Eliminate every internal negation by dual-rail compilation. The resulting circuit contains n input-literal NOT gates followed by a negation-free compiled program.

                            Equations
                            Instances For

                              The normalized circuit satisfies the checked input-negation invariant.

                              @[simp]
                              theorem Algebraic.AC0.DualRail.normalize_eval {n m : ℕ} (circuit : Circuit signature n m) (input : Fin n → Bool) :
                              (normalize circuit).eval interpretation input = circuit.eval interpretation input

                              Normalization preserves the full output vector on every Boolean input.

                              @[simp]

                              Normalization preserves the logical depth of every designated output.

                              @[simp]

                              Normalization preserves maximum logical depth exactly.

                              @[simp]

                              The compiled dual-rail DAG has exactly twice the source AND/OR cost.

                              @[simp]

                              Whole-circuit normalization has exactly twice the source AND/OR cost.

                              @[simp]
                              theorem Algebraic.AC0.DualRail.normalize_size {n m : ℕ} (circuit : Circuit signature n m) :
                              (normalize circuit).size = n + 2 * circuit.cost andOrCost

                              Normalization uses one input-literal gate per input and two gates per charged source gate.

                              Family-level normalization #

                              Normalize every member of a circuit family.

                              Equations
                              Instances For
                                @[simp]

                                The member at width n is the normalization of the source member.

                                @[simp]

                                Family normalization doubles charged cost pointwise.

                                @[simp]

                                Family normalization has one input-literal gate per input and two gates per charged source gate.

                                @[simp]

                                Family normalization preserves logical depth pointwise.

                                Family normalization preserves the computed target.

                                Every normalized family member has only input-level negations.

                                Polynomial AND/OR cost is preserved by family normalization.

                                Normalization turns polynomial charged cost into polynomial total gate count, with the explicit bound (2 * coefficient + 1) * (n + 1) ^ (degree + 1).

                                Constant logical depth is preserved by family normalization.

                                Every raw small-depth family has an equivalent checked family.

                                Raw and input-negation-normalized presentations define the same nonuniform AC0 class.