Documentation

Complexitylib.Classes.Containments.Internal.WitnessEnum

Enumerating the witnesses of a bounded existential #

⚠️ Unreviewed by Bolton

polyExistsLang quantifies over witnesses w with |w| ≤ p |x|. A machine cannot quantify; it counts. This file replaces the quantifier over strings by a quantifier over a length and a value — the two numbers a loop actually iterates — by exhibiting the round trip between a bitstring and its length-plus-binary-value.

Nothing here is about machines: it is the arithmetic that makes an enumeration faithful.

Main definitions #

Main results #

The big-endian value of a bitstring.

Equations
Instances For

    The bitstring of a given length and value, big-endian.

    Equations
    Instances For
      @[simp]
      theorem Complexity.bitsOfLen_length (v : ) :
      (bitsOfLen v).length =

      A bitstring's value fits in its length.

      The round trip. A bitstring is recovered from its length together with its value, so iterating over lengths and values enumerates every bitstring exactly once.

      theorem Complexity.binVal_bitsOfLen (v : ) :
      v < 2 ^ binVal (bitsOfLen v) = v

      The other half of the round trip. The value of the bitstring of length and value v is v again, provided v fits. Together with bitsOfLen_binVal this makes the correspondence a bijection, which is what lets a loop's counter be the witness: incrementing the counter advances the witness by exactly one place in the enumeration.

      theorem Complexity.exists_bounded_iff (m : ) (P : List BoolProp) :
      (∃ (w : List Bool), w.length m P w) m, v < 2 ^ , P (bitsOfLen v)

      A bounded existential over strings is a bounded existential over two numbers. This is the form a counting loop can implement: iterate the length, then the value.

      The little-endian enumeration #

      Every binary subroutine in the library — TM.binarySuccTM and the rest — uses canonical little-endian bit lists. Since the order in which witnesses are enumerated is immaterial, only that the enumeration is a bijection, the machine should use the convention its counter already speaks. These are the little-endian counterparts of the definitions above.

      The little-endian value of a bitstring.

      Equations
      Instances For

        The little-endian bitstring of a given length and value.

        Equations
        Instances For
          @[simp]
          theorem Complexity.bitsOfLenLE_length (v : ) :
          (bitsOfLenLE v).length =

          The round trip, one way.

          theorem Complexity.binValLE_bitsOfLenLE (v : ) :
          v < 2 ^ binValLE (bitsOfLenLE v) = v

          The round trip, the other way.

          theorem Complexity.exists_bounded_iff_le (m : ) (P : List BoolProp) :
          (∃ (w : List Bool), w.length m P w) m, v < 2 ^ , P (bitsOfLenLE v)

          The little-endian form of the enumeration.

          Choice sequences as counter values #

          The counting machine iterates a binary counter, but NTM.acceptCount ranges over functions Fin T → Bool. These convert between the two, reusing the bitstring enumeration.

          def Complexity.choicesOfNat (T v : ) :
          Fin TBool

          The choice sequence of length T with counter value v.

          Equations
          Instances For
            def Complexity.natOfChoices (T : ) (ch : Fin TBool) :

            The counter value of a choice sequence.

            Equations
            Instances For
              theorem Complexity.natOfChoices_lt (T : ) (ch : Fin TBool) :
              natOfChoices T ch < 2 ^ T
              theorem Complexity.bitsOfLenLE_getElem (v j : ) (h : j < ) :
              (bitsOfLenLE v)[j] = decide (v / 2 ^ j % 2 = 1)

              Each bit of the enumeration is a bit of the counter. The j-th entry of the length- little-endian string for v is bit j of v. This is the form in which the correspondence meets a tape: whatever encoding a counter tape uses, its j-th cell holds this bit — and cells beyond the counter's own digits read as false, which is bit j of v too.

              theorem Complexity.choicesOfNat_apply (T v : ) (j : Fin T) :
              choicesOfNat T v j = decide (v / 2 ^ j % 2 = 1)

              The j-th choice bit of counter value v is bit j of v.

              Canonical bits and their value #

              The library's counter subroutines represent a number by Nat.bits. Reading that representation back as a value is binValLE, and the two are mutually inverse — which is what makes a counter tape determine the number it holds.

              The canonical representation reads back as its value.

              The canonical representation determines the number.

              theorem Complexity.getD_eq_bit (w : List Bool) (j : ) :
              w.getD j false = decide (binValLE w / 2 ^ j % 2 = 1)

              Reading a bitstring past its end agrees with reading the number's bits. Entry j of a little-endian string, taken as false beyond the end, is bit j of the number it denotes — the high bits of that number being zero. This is what lets a counter tape be read as a choice sequence with no padding step.

              The single-counter enumeration #

              A nested loop over a length and then a value is two loops; a machine that enumerates witnesses would rather run one. Appending a marker bit turns the enumeration into a plain count: the numbers in [1, 2 ^ (m + 1)) are in bijection with the bitstrings of length at most m, a number denoting its canonical bits with the leading one removed.

              The witness a counter value denotes: the value's canonical bits, less the leading one.

              Equations
              Instances For

                The counter value at which a witness is enumerated: its value with a marker bit above it.

                Equations
                Instances For

                  The marker bit is the leading one.

                  The round trip.

                  theorem Complexity.topPlus_lt {m : } {w : List Bool} (h : w.length m) :
                  topPlus w < 2 ^ (m + 1)
                  theorem Complexity.length_dropTop_le {m v : } (hv : v < 2 ^ (m + 1)) :

                  A counter value below 2 ^ (m + 1) denotes a witness of length at most m.

                  theorem Complexity.exists_bounded_iff_count (m : ) (P : List BoolProp) :
                  (∃ (w : List Bool), w.length m P w) v < 2 ^ (m + 1), P (dropTop v)

                  The bounded existential is a count. A witness of length at most m exists exactly when some counter value below 2 ^ (m + 1) denotes one — a single loop over a single register, with the same shape the path-counting machine of PP ⊆ PSPACE already runs.

                  The enumeration is not injective at the bottom — 0 and 1 both denote the empty witness — but it does not have to be: only that every witness is denoted, and that every value denotes one.

                  Enumerating the witness alongside the counter #

                  The machine will not compute dropTop from the counter; it will carry the witness on a tape of its own and advance it in step with the counter. bumpLE is that advance — the counter's increment seen through dropTop. It is the ordinary little-endian increment except at the end of the string, where the carry extends the witness by a zero instead of writing a one: the bit it would have written is the counter's leading one, which dropTop discards.

                  One step of the witness enumeration: increment the string, extending it on overflow.

                  Equations
                  Instances For

                    The advance is the counter's increment.

                    A positive number's canonical bits end in one, so they are its witness and that one.

                    theorem Complexity.topPlus_dropTop {v : } (h : 1 v) :

                    The counter is recovered from its witness.

                    theorem Complexity.dropTop_succ {v : } (h : 1 v) :

                    The witness advances with the counter. This is the loop invariant the enumerating machine carries: one tape holds the counter, another holds the witness it denotes, and each iteration advances both.

                    theorem Complexity.exists_bounded_iff_count_pos (m : ) (P : List BoolProp) :
                    (∃ (w : List Bool), w.length m P w) ∃ (v : ), 1 v v < 2 ^ (m + 1) P (dropTop v)

                    Every witness of the admitted lengths is still reached, with the counter starting at one: the value 0, which the count above admits, denotes the same empty witness as 1.