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 #
bitsOfLen_binVal,binVal_bitsOfLen— the round trip, both ways: a bijection between bitstrings and length-plus-value pairsexists_bounded_iff,exists_bounded_iff_le— the bounded existential over strings is one over two numbers, in either bit orderchoicesOfNat,natOfChoices— choice sequences as counter values, for the path-counting machinebitsOfLenLE_getElem,choicesOfNat_apply— each bit of the enumeration is a bit of the counter, in the form a tape encoding producesbinValLE_bits,bits_injective— the canonical counter representation determines its valuegetD_eq_bit— reading a bitstring past its end agrees with reading the number's bitsdropTop,topPlus,exists_bounded_iff_count— the same existential as a single count, over the counter values below2 ^ (m + 1)bumpLE,dropTop_succ— the witness advances in step with the counter, so a machine can carry it on a tape instead of computing it
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.
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
- Complexity.binValLE [] = 0
- Complexity.binValLE (b :: w) = (if b = true then 1 else 0) + 2 * Complexity.binValLE w
Instances For
The little-endian bitstring of a given length and value.
Equations
- Complexity.bitsOfLenLE 0 x✝ = []
- Complexity.bitsOfLenLE ℓ.succ x✝ = decide (x✝ % 2 = 1) :: Complexity.bitsOfLenLE ℓ (x✝ / 2)
Instances For
The round trip, one way.
The round trip, the other way.
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.
The choice sequence of length T with counter value v.
Equations
- Complexity.choicesOfNat T v j = (Complexity.bitsOfLenLE T v)[↑j]
Instances For
The counter value of a choice sequence.
Equations
- Complexity.natOfChoices T ch = Complexity.binValLE (List.ofFn ch)
Instances For
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.
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 determines the number.
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
- Complexity.topPlus w = Complexity.binValLE w + 2 ^ w.length
Instances For
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.
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.