Nivel 7 · Completitud en Lean (experto)

El lema de Kalmár constructivo

Se demuestra que toda tautología es derivable a partir de \emptyset. El argumento clásico de Kalmár (Kalmár, 1935) , esbozado en el Teorema 9.12, procede en dos etapas.

  1. Derivación por valuación (lema de Kalmár). Se fija una valuación vv y sea Γv\Gamma_v el contexto de literales de vv: para cada átomo pp relevante para φ\varphi, se incluye el literal pp si v(p)=Vv(p) = \mathsf{V} y ¬p\neg p si v(p)=Fv(p) = \mathsf{F}. Se demuestra, por inducción sobre φ\varphi, que Γv\Gamma_v demuestra φ\varphi cuando φv\llbracket \varphi \rrbracket_v es verdadera, y demuestra ¬φ\neg\varphi cuando es falsa. Intuitivamente, la derivación «sigue la fila vv de la tabla de verdad».
  2. Descarga de los átomos. Si φ\varphi es una tautología, todo contexto de literales demuestra φ\varphi; los átomos se eliminan entonces uno a uno con el análisis por casos derivado. Esta etapa es el contenido de la página siguiente.

No se necesita maquinaria de conjuntos maximales consistentes / Lindenbaum: el argumento es finitario, guiado por la lista finita de átomos que aparecen en φ\varphi. El diagrama siguiente recorre las dos etapas, con el nombre de la función de Lean que realiza cada una.

El algoritmo de Kalmár, ejecutable

De la tabla de verdad a la derivación: el contenido constructivo del teorema de completitud.

Etapa 1 · La tabla de verdad (eval v φ)

ppppp \rightarrow pderivación de la fila
TT
FT

Etapa 2 · El lema de Kalmár en la fila 1 (kalmar_lemma)

El contexto de literales de la fila deriva la fórmula (bilateral: derivaría su negación si la fila la falsara). Cada paso lleva su regla.

p,ppp,\, p\, \vdash p→ipppp\, \vdash p \rightarrow p

Etapa 3 · Descarga de los átomos (discharge / byCases)

El análisis por casos clásico (byCases, vía RAA) funde las 2 ramas en una derivación sin hipótesis. (Se muestran los últimos niveles; las hojas son las derivaciones de la Etapa 2.)

→ipppp\, \vdash p \rightarrow p→i¬ppp\neg p\, \vdash p \rightarrow pbyCasespp\vdash p \rightarrow p

✓ Verificador independiente: las 3 derivaciones construidas son instancias correctas de las reglas.

Átomos, literales y contextos de literales

Definición 11.15 (Átomos de una fórmula)

atoms(φ)\mathrm{atoms}(\varphi) es la lista de átomos que aparecen en φ\varphi (con repetición):

atoms(p)=[p],atoms(¬φ)=atoms(φ),atoms(φψ)=atoms(φ)+ ⁣ ⁣+atoms(ψ).\mathrm{atoms}(p) = [p], \quad \mathrm{atoms}(\neg\varphi) = \mathrm{atoms}(\varphi), \quad \mathrm{atoms}(\varphi \to \psi) = \mathrm{atoms}(\varphi) \mathbin{+\!\!+} \mathrm{atoms}(\psi).
Definición 11.16 (Literal y contexto de literales)

Para una valuación vv y un átomo pp,

litv(p)={psi v(p)=V,¬psi v(p)=F.\mathrm{lit}_v(p) = \begin{cases} p & \text{si } v(p) = \mathsf{V},\\ \neg p & \text{si } v(p) = \mathsf{F}. \end{cases}

Para una lista de átomos ats\mathit{ats}, el contexto de literales es

Γv(ats)={litv(p):pats},\Gamma_v(\mathit{ats}) = \{\, \mathrm{lit}_v(p) : p \in \mathit{ats} \,\},

definido por recursión: Γv([])=\Gamma_v([\,]) = \emptyset y Γv(p::rest)={litv(p)}Γv(rest)\Gamma_v(p :: \mathit{rest}) = \{\mathrm{lit}_v(p)\} \cup \Gamma_v(\mathit{rest}).

Lema 11.17 (Pertenencia del literal)

Si patsp \in \mathit{ats} entonces litv(p)Γv(ats)\mathrm{lit}_v(p) \in \Gamma_v(\mathit{ats}).

Demostración.

A probar: litv(p)Γv(ats)\mathrm{lit}_v(p) \in \Gamma_v(\mathit{ats}), bajo la hipótesis patsp \in \mathit{ats}. Por inducción sobre la lista ats\mathit{ats}.

  1. ats=[]\mathit{ats} = [\,]. La hipótesis p[]p \in [\,] es imposible, así que el caso es vacuo.
  2. ats=a::rest\mathit{ats} = a :: \mathit{rest}. Por definición, Γv(a::rest)={litv(a)}Γv(rest)\Gamma_v(a :: \mathit{rest}) = \{\mathrm{lit}_v(a)\} \cup \Gamma_v(\mathit{rest}). De pa::restp \in a :: \mathit{rest} hay dos posibilidades: si p=ap = a, entonces litv(p)=litv(a)\mathrm{lit}_v(p) = \mathrm{lit}_v(a) es el elemento insertado; si prestp \in \mathit{rest}, la hipótesis de inducción da litv(p)Γv(rest)\mathrm{lit}_v(p) \in \Gamma_v(\mathit{rest}), y como Γv(rest)Γv(a::rest)\Gamma_v(\mathit{rest}) \subseteq \Gamma_v(a :: \mathit{rest}), se concluye.

Extracto de Completeness.lean.

def atoms : Formula → List String
| .atom s => [s]
| .neg p => atoms p
| .impl p q => atoms p ++ atoms q
noncomputable def lit (v : Valuation) (p : String) : Formula :=
if v p then .atom p else ~(.atom p)
noncomputable def litCtx (v : Valuation) : List String → Set Formula
| [] => ∅
| p :: rest => insert (lit v p) (litCtx v rest)
theorem lit_mem {v : Valuation} {p : String} {ats : List String}
(h : p ∈ ats) : lit v p ∈ litCtx v ats := by
induction ats with
| nil => simp at h
| cons a rest ih =>
rcases List.mem_cons.1 h with rfl | h
· exact Set.mem_insert _ _
· exact Set.mem_insert_of_mem _ (ih h)

(lit y litCtx son noncomputable porque el test «vpv\,p» es una proposición; bajo la instancia local Classical.propDecidable el if está bien definido.)

El lema de Kalmár

Lema 11.18 (Kalmár)

Fíjense una valuación vv y una lista ats\mathit{ats} con atoms(φ)ats\mathrm{atoms}(\varphi) \subseteq \mathit{ats}. Entonces

(φvΓv(ats)NDφ)y(¬φvΓv(ats)ND¬φ).\big(\llbracket \varphi \rrbracket_v \Rightarrow \Gamma_v(\mathit{ats}) \Provnd \varphi\big) \quad\text{y}\quad \big(\neg\llbracket \varphi \rrbracket_v \Rightarrow \Gamma_v(\mathit{ats}) \Provnd \neg\varphi\big).
Demostración.

A probar: para Γ:=Γv(ats)\Gamma := \Gamma_v(\mathit{ats}), las dos implicaciones

(P)φvΓNDφ,(N)¬φvΓND¬φ.\text{(P)}\quad \llbracket \varphi \rrbracket_v \Rightarrow \Gamma \Provnd \varphi, \qquad \text{(N)}\quad \neg\llbracket \varphi \rrbracket_v \Rightarrow \Gamma \Provnd \neg\varphi.

Por inducción sobre la estructura de φ\varphi.

Caso átomo φ=s\varphi = s. Como atoms(s)=[s]ats\mathrm{atoms}(s) = [s] \subseteq \mathit{ats}, se tiene satss \in \mathit{ats}, y por el Lema 11.17, litv(s)Γ\mathrm{lit}_v(s) \in \Gamma.

  • (P) Si sv\llbracket s \rrbracket_v (esto es, v(s)=Vv(s) = \mathsf{V}): entonces litv(s)=s\mathrm{lit}_v(s) = s, luego sΓs \in \Gamma y sΓΓNDs (hip.).\frac{s \in \Gamma}{\Gamma \Provnd s}\ \text{(hip.).}
  • (N) Si ¬sv\neg\llbracket s \rrbracket_v: entonces litv(s)=¬s\mathrm{lit}_v(s) = \neg s, luego ¬sΓ\neg s \in \Gamma y ¬sΓΓND¬s (hip.).\frac{\neg s \in \Gamma}{\Gamma \Provnd \neg s}\ \text{(hip.).}

Caso negación φ=¬p\varphi = \neg p. Como atoms(¬p)=atoms(p)ats\mathrm{atoms}(\neg p) = \mathrm{atoms}(p) \subseteq \mathit{ats}, valen las dos hipótesis de inducción para pp: (Pp_p) y (Np_p).

  • (P) Si ¬pv\llbracket \neg p \rrbracket_v, es decir ¬pv\neg\llbracket p \rrbracket_v: por (Np_p) se obtiene directamente la meta ΓND¬p\Gamma \Provnd \neg p.
  • (N) Si ¬¬pv\neg\llbracket \neg p \rrbracket_v, es decir ¬¬pv\neg\neg\llbracket p \rrbracket_v: clásicamente pv\llbracket p \rrbracket_v, así que (Pp_p) da ΓNDp\Gamma \Provnd p, y la introducción de doble negación (Lema 11.9) da la meta: ΓNDpΓND¬¬p (dni).\frac{\Gamma \Provnd p}{\Gamma \Provnd \neg\neg p}\ (\text{dni}).

Caso implicación φ=pq\varphi = p \to q. Como atoms(pq)=atoms(p)+ ⁣ ⁣+atoms(q)ats\mathrm{atoms}(p \to q) = \mathrm{atoms}(p) \mathbin{+\!\!+} \mathrm{atoms}(q) \subseteq \mathit{ats}, valen las hipótesis de inducción para pp y para qq.

(P) pqvΓNDpq\llbracket p \to q \rrbracket_v \Rightarrow \Gamma \Provnd p \to q. Se distingue según pv\llbracket p \rrbracket_v.

Si pv\llbracket p \rrbracket_v: de pqv=(pvqv)\llbracket p \to q \rrbracket_v = (\llbracket p \rrbracket_v \to \llbracket q \rrbracket_v) se sigue qv\llbracket q \rrbracket_v; por (Pq_q), ΓNDq\Gamma \Provnd q. Entonces:

Si ¬pv\neg\llbracket p \rrbracket_v: por (Np_p), ΓND¬p\Gamma \Provnd \neg p. Se deriva Γ,pNDq\Gamma, p \Provnd q y se cierra con \toI:

(N) ¬pqvΓND¬(pq)\neg\llbracket p \to q \rrbracket_v \Rightarrow \Gamma \Provnd \neg(p \to q). De ¬(pvqv)\neg(\llbracket p \rrbracket_v \to \llbracket q \rrbracket_v) se obtiene clásicamente pv\llbracket p \rrbracket_v (si no, la implicación sería vacuamente verdadera) y ¬qv\neg\llbracket q \rrbracket_v (si no, también lo sería). Por (Pp_p), ΓNDp\Gamma \Provnd p; por (Nq_q), ΓND¬q\Gamma \Provnd \neg q. Se cierra con ¬\negI, descargando el supuesto pqp \to q:

Código de Lean (tal como se implementa)

Extracto de Completeness.lean.

theorem kalmar (v : Valuation) (ats : List String) :
∀ φ : Formula, atoms φ ⊆ ats →
(eval v φ → ND (litCtx v ats) φ) ∧ (¬ eval v φ → ND (litCtx v ats) (~φ)) := by
intro φ
induction φ with
| atom s =>
intro hsub
have hs : s ∈ ats := hsub (by simp [atoms])
refine ⟨?_, ?_⟩
· intro hev
have hvs : v s := hev
have hmem := lit_mem (v := v) hs
rw [lit, if_pos hvs] at hmem
exact ND.hyp hmem
· intro hev
have hvs : ¬ v s := hev
have hmem := lit_mem (v := v) hs
rw [lit, if_neg hvs] at hmem
exact ND.hyp hmem
| neg p ih =>
intro hsub
have hsub' : atoms p ⊆ ats := hsub
obtain ⟨ihpos, ihneg⟩ := ih hsub'
refine ⟨?_, ?_⟩
· intro hev; exact ihneg hev
· intro hev; exact dni (ihpos (not_not.1 hev))
| impl p q ihp ihq =>
intro hsub
have hsp : atoms p ⊆ ats := by
intro a ha; exact hsub (by simp only [atoms, List.mem_append]; exact Or.inl ha)
have hsq : atoms q ⊆ ats := by
intro a ha; exact hsub (by simp only [atoms, List.mem_append]; exact Or.inr ha)
obtain ⟨ihp_pos, ihp_neg⟩ := ihp hsp
obtain ⟨ihq_pos, ihq_neg⟩ := ihq hsq
refine ⟨?_, ?_⟩
· intro hev
by_cases hp : eval v p
· exact ND.impI (weakening (ihq_pos (hev hp)) (Set.subset_insert _ _))
· apply ND.impI
have h1 : ND (insert p (litCtx v ats)) (~p) :=
weakening (ihp_neg hp) (Set.subset_insert _ _)
have h2 : ND (insert p (litCtx v ats)) p := ND.hyp (Set.mem_insert _ _)
exact ND.botE (ND.negE h1 h2)
· intro hev
have hp : eval v p := by by_contra h; exact hev (fun hpp => absurd hpp h)
have hq : ¬ eval v q := fun hq => hev (fun _ => hq)
apply ND.negI
have hpq : ND (insert (p ⟶ q) (litCtx v ats)) (p ⟶ q) := ND.hyp (Set.mem_insert _ _)
have hpp : ND (insert (p ⟶ q) (litCtx v ats)) p :=
weakening (ihp_pos hp) (Set.subset_insert _ _)
have hnq : ND (insert (p ⟶ q) (litCtx v ats)) (~q) :=
weakening (ihq_neg hq) (Set.subset_insert _ _)
exact ND.negE hnq (ND.impE hpq hpp)

El código sigue, rama por rama, la demostración anterior: las tres ramas de induction φ (atom, neg, impl) son los tres casos ya probados, y cada paso matemático reaparece como una línea de Lean.

Nota (Traducción de tácticas a contenido lógico)

Para el lector que no lee Lean, vale la pena traducir los nombres de las tácticas a su contenido lógico: not_not.1 es la eliminación de la doble negación (¬¬AA\neg\neg A \to A), clásicamente válida, que interviene en el caso de la negación; by_cases hp : eval v p es el análisis por casos según se cumpla o no pv\llbracket p \rrbracket_v, en el caso positivo de la implicación; y las dos líneas by_contra/absurd extraen pv\llbracket p \rrbracket_v y ¬qv\neg\llbracket q \rrbracket_v a partir de ¬(pvqv)\neg(\llbracket p \rrbracket_v \to \llbracket q \rrbracket_v), apoyándose en la equivalencia clásica ¬(PQ)P¬Q\neg(P \to Q) \Leftrightarrow P \wedge \neg Q.

Vista interna (CIC). Aquí induction φ es Formula.rec (no ND.rec): se recurre sobre la sintaxis, con motivo

Cφ  :=  atoms φats(eval v φND Γ φ)(¬eval v φND Γ (¬φ)).C\,\varphi \;:=\; \texttt{atoms}\ \varphi \subseteq \mathit{ats} \to \big(\texttt{eval}\ v\ \varphi \to \texttt{ND}\ \Gamma\ \varphi\big) \wedge \big(\neg\,\texttt{eval}\ v\ \varphi \to \texttt{ND}\ \Gamma\ (\neg\varphi)\big).

En las ramas neg e impl el recursor proporciona las hipótesis de inducción ih, ihp, ihq para las subfórmulas inmediatas; la demostración consume esas hipótesis (a nivel meta) y produce términos ND con constructores (ND.impI, ND.negI, ND.botE, ND.negE, …) y la regla derivada dni. Así, una sola ejecución de Formula.rec convierte el valor de verdad de φ\varphi bajo vv en un árbol de derivación efectivo: este es el contenido de «completitud como programa» del Lema 11.18 hecho literal.