Encoding faithfulness: the completeness direction #
Reflect/Encode.lean provides soundness: every clause satisfied $\\Longrightarrow$ the
semantic condition holds. Is that enough to turn "the solver reports unsatisfiable" into
"there is no light logical operator"? No. What is missing is the converse direction:
the encoding does not over-constrain, so that whenever the semantic condition holds
there exists an assignment of the auxiliary variables under which all clauses are true.
Only the two together give "satisfiable if and only if a light logical operator exists", and
"a UNSAT verdict implies a distance lower bound" is exactly the forward direction of that
equivalence.
This module supplies the converse direction. Each of the four components comes with a constructive lemma:
| component | construction |
|---|---|
xorChain | the auxiliary variables take the prefix parity: at counter c + i the value is |
`acc ⊕ σ x₀ ⊕ ⋯ ⊕ σ xᵢ` |
| prodClauses | the product variables take σ x ∧ σ w |
| atMostK | slot (i,j) takes "at least j of the first i variables are true" |
| buildPair | the four segments are concatenated, see buildPair_complete |
Why the construction is written segment by segment rather than as a single function #
The encoder allocates the auxiliary variable numbers sequentially (c1Of/c2Of/c3Of
are determined by the exit point of the preceding segment), so building the assignment
segment by segment matches that order and avoids counting arithmetic of the form
c1Of = 2n + Σ(|r| - 1). The interface for the concatenation is the pair of properties of
chainAssign: below the counter it agrees with the input, and above the counter it
reads only the current segment, so the assignment of a later segment leaves the ground of
an earlier one untouched.
What "light logical operator" means in this module #
Here a light logical operator is still only a semantic condition (a weight bound,
kernels on the two sides, and a pairing equal to 1); the step that turns it into a statement
about binary matrices and Hamming weight is proved in Reflect/Distance.lean.
1. Completeness of the Tseitin chain #
The fill value of a chain: rest allocates the auxiliary variables starting from the
counter c, and acc is the value of the accumulator on entering this segment.
At t = c the value is acc ⊕ σ x₀, and at t = c + i it is
acc ⊕ σ x₀ ⊕ ⋯ ⊕ σ xⱼ (where j is the i-th element); outside the allocated range it is
false (those variable numbers should never be referenced anyway).
Note that it does not read σ at t < c, which is the source of "a later segment does
not overwrite an earlier one" during concatenation.
Equations
Instances For
The fill value reads only the elements of rest and the initial accumulator; the
compatibility needed for concatenation rests on this.
The complete assignment of a chain: below the counter c it keeps σ, above it it
takes the fill value of the chain.
Equations
- QECCertificates.LRAT.chainAssign σ rest acc c t = if t < c then σ t else QECCertificates.LRAT.chainFill σ rest acc c t
Instances For
One step past the counter (the chainAssign version).
The value of chainAssign at the counter.
Compatibility of chainAssign: if the two assignments agree below the counter according
to hlt and on rest according to h, then they are equal.
Feeding the assignment of one segment of a chain on into its tail yields the same assignment: the concatenation lemma.
Note that the two agree exactly at t = c (the left side takes the new accumulator
acc ⊕ σ x, the right side takes the value of chainFill at the counter), which is
precisely why chainFill does not read below the counter.
Completeness of one step: if the values satisfy the xor relation, all four clauses are true.
Completeness of a chain: if the semantic condition holds and every input variable
read by the chain lies below the counter c, then chainAssign satisfies all the clauses
of the chain.
2. Concatenation: one chain per row #
chainsFrom turns each row into a chain and allocates the variable numbers end to end.
Completeness has to concatenate segment by segment: the assignment of a later segment
must leave the ground of an earlier one untouched, and the interface is the "agrees with the
input below the counter" property of chainAssign.
Two bookkeeping lemmas are needed for this: the exit counter of a chain (the new
variable numbers reach exactly c + rest.length) and the variable bound of the chain
clauses (all of them lie below the exit counter).
The variable bound of the chain clauses: all of them lie below the exit counter.
Completeness of a sequence of chains: if the dot product of every row is false,
then there exists an assignment that satisfies all the chain clauses at once and agrees with
the input below the counter c.
3. Completeness of the product block #
prodClauses c n carries x_j ∧ w_j in the variable c + j (x_j is variable j and
w_j is variable n + j). Completeness consists in assigning c + j directly to the
conjunction.
The assignment of the product block: the range c..c+n-1 takes the pointwise
conjunction and the rest is kept as it is.
Equations
Instances For
4. Completeness of the sequential counter (Sinz) #
The soundness of atMostK is "all clauses true implies at most k variables are true"
(Reflect/Encode.lean). The converse direction assigns slot (i,j) to "at least j of
the first i variables are true" and verifies the four clauses row by row.
Three preliminaries are needed: the recursion for slotBase (the block before row i+1 has
min i (k + 1) slots more than the block before row i, namely those of row i), a
characterization of the value of slotVar, and a few lemmas that bridge the clause
criterion from Bool back to a proposition.
The Bool bridge at the clause level #
Each of the four clauses says that one Bool implies another, so the criterion can be
stated as an implication between values, with no need to unfold decide again and again.
The four branches of slotClauses as explicit equations #
The scrutinee of the match in slotClauses consists of three Option values. A rw on
the slot in the goal does not move it, because the slot is a function argument; the
three values are therefore first written as hypotheses, and the match then reduces.
Completeness of one row #
Row i processes the i-th variable, and the value of slot (i,j) is determined by
"at least j of the first i variables are true". Here cnt' is the number of true values
among the first i-1 variables, cnt that among the first i, and bx the value of the
i-th variable; hprev/hcur state the slot values of the previous row and of this row as
hypotheses, so this lemma only performs the verification at the level of Bool.
The four cases are separated by whether the slots (i-1,j) and (i-1,j-1) of the previous
row are valid, which is the branch condition of the match in slotClauses.
Filling row by row, and the main theorem #
Write the slot block of row i into the assignment. The block boundaries are pinned down
by slotBase: row i occupies [c + slotBase k i, c + slotBase k (i+1)), and
slotBase k (i+1) = slotBase k i + min i (k + 1) (slotBase_succ) says that the two blocks
meet end to end without overlapping. In the induction, "later rows leave the slots already
filled by earlier rows alone" is exactly this, and it chains the segment-by-segment
construction together without having to recover the row index from a variable number.
Write the slot block of row i into the assignment: inside the block it takes "at least
j of the first i variables are true", outside the block it is kept as it is.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Completeness of the sequential counter: if at most k variables are true, then
there exists an assignment satisfying all the counter clauses that agrees with the given
assignment on the input variables.
The induction follows the traversal of atMostKAux (pre is the prefix already processed
and suf the remainder), writing the slot block of that row into the assignment at each
step. The returned assignment has two properties that the outer argument uses: it agrees
with the original assignment below c + slotBase k (pre.length + 1) (the slots filled by
earlier rows are not overwritten by later ones), and it characterizes the slot values of
the last row (the next step uses this as its hprev).
5. Assembly: completeness of buildPair #
The assignments of the four segments are concatenated segment by segment. The assembly has to move "the clauses of a segment are satisfied by that segment's assignment" to the final assignment, and it rests on two facts: the counters only increase (the threshold of a later segment is never lower than that of an earlier one), and the variables of each segment's clauses lie below its own exit counter.
The segments and counters of buildPair (Reflect/Encode.lean): the kernel constraints
start at 2n, the pairing constraints at c1Of, the product block at c2Of, the chain for
x·w at c2Of + n, and the counter at c3Of.
The clause variables of a sequence of chains all lie below the exit counter of that segment.
A set of clauses is preserved between assignments that agree below the threshold.
Completeness of buildPair: when the four semantic conditions hold, the encoding is
satisfiable — with the witness pinned down on the input variables.
Together with buildPair_sat of Reflect/Encode.lean (soundness) this composes into
an equivalence with the Boolean semantic conditions. Reflect/Distance.lean bridges
these conditions to binary matrices and Hamming weight. Composing that bridge with the
soundness theorem of Reflect/LRAT.lean gives a matrix-level distance lower bound.
The agreement clause ∀ t, t < 2 * n → τ t = σ t is what an assembly needs and what a bare
"there exists a model" cannot supply: the base part is followed by other clause blocks, and to
carry a model of the base part past those blocks one has to know that the base model reads the
input variables — that is, the ones a lexicographic comparator compares — exactly as the
semantic data does. It costs nothing here, since the whole proof is already built out of
extensions that agree below their own starting point: the first segment agrees below 2 * n, and
every later segment agrees with the previous one below a number that is at least 2 * n
(hge1).