1\chapter{Strumenti di dimostrazione: Logica Proposizionale}
2\label{chap:proof-tools}
3
4\newcommand{\naive}{na\"\i{}ve}
5
6Gli utenti di \HOL{} possono creare i loro propri strumenti di dimostrazione
7combinando le regole e le tattiche predefinite. La disciplina dei tipi \ML{}
8assicura che solo metodi logicamente validi possono essere usati per creare valori
9di tipo \ml{thm}. In questo capitolo, � descritto un esempio reale.
10
11Sono date due implementazioni dello strumento per illustrare vari stili
12di programmazione della dimostrazione. La prima implementazione � quella ovvia, ma
13� inefficiente a causa del metodo di `forza bruta' utilizzato. La seconda
14implementazione tenta di essere molto pi� intelligente.
15Sono anche discusse estensioni agli strumenti per permettere un'applicabilit�
16pi� generale.
17
18Il problema da risolvere � quello di decidere la verit� di una formula
19chiusa della logica proposizionale. Una tale formula ha la forma generale
20\[
21\begin{array}{ccl}
22\varphi & ::= & v \;|\;\neg\varphi\;|\;\varphi
23\land \varphi \;|\; \varphi \lor \varphi \;|\; \varphi \Rightarrow
24\varphi\;|\;\varphi = \varphi
25\\[1ex]
26\mathit{formula} &::= & \forall \vec{v}. \;\varphi
27\end{array}
28\]
29dove le variabili $v$ sono tutte di tipo booleano, e dove il
30quantificatore universale al livello pi� esterno cattura tutte le
31variabili libere.
32
33\section{Metodo 1: Tabelle di verit�}
34
35\setcounter{sessioncount}{0}
36
37Il primo metodo che deve essere implementato � il metodo di forza bruta di tentare
38tutte le combinazioni booleane possibili. L'unico vero vantaggio di questo approccio
39� che � eccezionalmente semplice da implementare. Prima dimostreremo
40il teorema motivante:
41\begin{hol}
42\begin{verbatim}
43   val FORALL_BOOL = prove(
44     ``(!v. P v) = P T /\ P F``,
45     SRW_TAC [][EQ_IMP_THM] THEN Cases_on `v` THEN SRW_TAC [][]);
46\end{verbatim}
47\end{hol}
48La dimostrazione procede dividendo il goal in due met�, mostrando
49\[
50(\forall v. \;P(v))\Rightarrow P(\top) \land P(\bot)
51\]
52(Goal che � automaticamente mostrato dal semplificatore), e
53\[
54P(\top) \land P(\bot) \Rightarrow P(v)
55\]
56per una variabile booleana arbitraria $v$. Dopo lo splitting dei casi su $v$,
57le assunzioni sono poi sufficienti a mostrare il goal. (Questo teorema �
58di fatto gi� dimostrato nella teoria \theoryimp{bool}.)
59
60Il prossimo, e ultimo passo, � di riscrivere con questo teorema:
61\begin{hol}
62\begin{verbatim}
63   val tautDP = SIMP_CONV bool_ss [FORALL_BOOL]
64\end{verbatim}
65\end{hol}
66
67Questo permette il seguente
68
69\begin{session}
70\begin{verbatim}
71- tautDP ``!p q. p /\ q /\ ~p``;
72> val it = |- (!p q. p /\ q /\ ~p) = F : thm
73
74- tautDP ``!p. p \/ ~p``
75> val it = |- (!p. p \/ ~p) = T : thm
76\end{verbatim}
77\end{session}
78e anche il pi� leggermente intimidatorio
79\begin{session}
80\begin{verbatim}
81- time tautDP
82     ``!p q c a. ~(((~a \/ p /\ ~q \/ ~p /\ q) /\
83                    (~(p /\ ~q \/ ~p /\ q) \/ a)) /\
84                   (~c \/ p /\ q) /\ (~(p /\ q) \/ c)) \/
85                 ~(p /\ q) \/ c /\ ~a``;
86runtime: 0.147s,    gctime: 0.012s,     systime: 0.000s.
87> val it =
88    |- (!p q c a.
89          ~(((~a \/ p /\ ~q \/ ~p /\ q) /\ (~(p /\ ~q \/ ~p /\ q) \/ a)) /\
90            (~c \/ p /\ q) /\ (~(p /\ q) \/ c)) \/ ~(p /\ q) \/ c /\ ~a) =
91       T : thm
92\end{verbatim}
93\end{session}
94
95Questo � un algoritmo terribile per risolvere questo problema. La funzione\linebreak
96\ml{tautLib.TAUT\_CONV} incorporata nel sistema risolve il problema di sopra
97molto pi� velocemente. L'unico merito
98reale in questa soluzione � che richiede di scrivere una sola riga. Questa �
99un'illustrazione generale del fatto che gli strumenti di alto livello di \HOL{},
100in particolare il semplificatore, possono fornire prototipi veloci per una variet�
101di compiti di dimostrazione.
102
103\section{Metodo 2: l'Algoritmo DPLL}
104
105Il metodo Davis-Putnam-Loveland-Logemann~\cite{DPLL-paper} per
106decidere la soddisfacibilit� di formule proposizionali in CNF
107(Conjunctive Normal form) � una tecnica potente, ancora oggi usata nei
108risolutori dell'ultima generazione. Se eliminiamo i quantificatori universali
109dalle nostre formule di input, il nostro compito pu� essere visto come determinare
110la validit� di una formula proposizionale. Testare la negazione di una tale
111formula per la soddisfacibilit� � un test per la validit�: se la negazione
112della formula � soddisfacibile, allora non � valida (l'assegnazione
113che soddisfa render� l'originale falsa): se la negazione della formula �
114insoddisfacibile, allora la formula � valida (nessuna assegnazione la pu� rendere
115falsa).
116
117\smallskip
118\noindent
119(Il codice sorgente per questo esempio � disponibile nel file \texttt{examples/dpll.sml}.)
120
121\subsection*{Preliminari}
122
123Per iniziare, assumiamo che abbiamo gi� del codice per convertire formule
124arbitrarie in CNF{}, e per poi decidere la soddisfacibilit� di queste
125formule. Assumiamo inoltre che se l'input all'ultima procedura �
126insoddisfacibile, allora restituir� un teorema della forma
127\[ \vdash \varphi = \holtxt{F}
128\]
129o se � soddisfacibile, allora restituir� un'assegnazione che soddisfa,
130un mapping da variabili a booleani. Questo mapping sar� una funzione da
131variabili \HOL{} a uno dei termini \HOL{} \holtxt{T} o \holtxt{F}.
132Cos�, assumeremo
133\begin{hol}
134\begin{verbatim}
135   datatype result = Unsat of thm | Sat of term -> term
136   val toCNF : term -> thm
137   val DPLL : term -> result
138\end{verbatim}
139\end{hol}
140(Il teorema restituito da \ml{toCNF} uguaglier� il termine di input a
141un altro in CNF{}.)
142
143\smallskip
144\noindent
145Prima di guardare all'implementazione di queste funzioni, dovremo
146considerare
147\begin{itemize}
148\item come trasformare i nostri input per adattarsi alla funzione; e
149\item come usare gli output dalle funzioni per produrre i nostri risultati
150	desiderati
151\end{itemize}
152
153Stiamo assumendo che il nostro input sia una formula quantificata universalmente.
154Entrambe le procedure CNF e DPLL si aspettano formule senza quantificatori.
155Vogliamo anche passare a queste procedure la negazione della formula
156originaria. Entrambe la manipolazioni di termini richieste si possono fare
157con le funzioni che si trovano nella struttura \ml{boolSyntax}. (in generale,
158le teorie importanti (come \theoryimp{bool}) sono accompangnate da
159moduli \ml{Syntax} che contengono funzioni per manipolare le
160forme di termini associate con quella teoria.)
161
162In questo caso abbiamo bisogno delle funzioni
163\begin{hol}
164\begin{verbatim}
165   strip_forall : term -> term list * term
166   mk_neg       : term -> term
167\end{verbatim}
168\end{hol}
169La funzione \ml{strip\_forall} spoglia un termine di tutte le sue quantificazioni
170universali pi� esterne, restituendo la lista delle variabili spogliate
171e il corpo della quantificazione. La funzione \ml{mk\_neg} prende un
172termine di tipo \holtxt{bool} e restituisce il termine corrispondente alla sua
173negazione.
174
175Usando queste funzioni, � facile vedere come saremo in grado di prendere
176$\forall\vec{v}.\;\varphi$ come input, e passare il termine $\neg\varphi$
177alla funzione \ml{toCNF}. Una domanda pi� significativa � come
178usare i risultati di queste chiamate. La chiamata a \ml{toCNF}  restituir� un
179teorema
180\[
181\vdash \neg\varphi = \varphi'
182\]
183La formula $\varphi'$ � ci� che passeremo poi a \ml{DPLL}. (Possiamo
184estrarla usando le funzioni \ml{concl} e \ml{rhs}.) Se
185\ml{DPLL} restituisce il teorema $\vdash \varphi' = \holtxt{F}$,
186un'applicazione di \ml{TRANS} a questo e al teorema mostrato di sopra
187deriver� la formula $\vdash \neg\varphi = F$. Al fine di derivare il
188risultato finale, avremo bisogno di trasformare questo in $\vdash\varphi$. Questo
189si ottiene meglio fornendo un teorema fatto su misura che incorpora l'uguaglianza (non
190ce n'� gi� uno simile nel sistema):
191\begin{hol}
192\begin{verbatim}
193   val NEG_EQ_F = prove(``(~p = F) = p``, REWRITE_TAC []);
194\end{verbatim}
195\end{hol}
196Per trasformare $\vdash \varphi$ in $\vdash (\forall
197\vec{v}.\;\varphi) = \holtxt{T}$, eseguiremo la seguente dimostrazione:
198\[
199\infer[\texttt{\scriptsize EQT\_INTRO}]{
200  \vdash (\forall \vec{v}.\;\varphi) = \holtxt{T}}{
201  \infer[\texttt{\scriptsize GENL}(\vec{v})]{\vdash \forall \vec{v}.\;\varphi}{
202    \vdash \varphi
203  }
204}
205\]
206L'altra possibilit� � che \ml{DPLL}  restituir� un'assegnazione che soddisfa
207dimostrando che $\varphi'$. Se questo �
208il caso, vogliamo mostrare che $\forall\vec{v}.\;\varphi$ � falsa.
209Possiamo fare questo assumendo questa formula, e specializzando le
210variabili quantificate universalmente in linea con il mapping fornito. In
211questo modo, sar� possibile produrre il teorema
212\[
213\forall \vec{v}.\;\varphi \vdash \varphi[\vec{v} := \mbox{\emph{satisfying
214  assignment}}]
215\]
216Poich� non ci sono variabili libere in $\forall\vec{v}.\;\varphi$, la
217sostituzione produrr� una formula booleana completamente ground. Questa
218si riscriver� direttamente a \holtxt{F} (se l'assegnazione
219rende $\neg\varphi$ vero, deve rendere $\varphi$ falso). Trasformare
220$\phi\vdash \holtxt{F}$ in $\vdash \phi = \holtxt{F}$ � questione di
221richiamare \ml{DISCH} e poi di riscrivere con il teorema incorporato
222\ml{IMP\_F\_EQ\_F}:
223\[
224\vdash \forall t.\;t \Rightarrow \holtxt{F} = (t = \holtxt{F})
225\]
226Mettendo tutto ci� che sta di sopra insieme, possiamo scrivere la nostra funzione wrapper,
227che chiameremo \ml{DPLL\_UNIV}, con il suffisso \ml{UNIV}
228che ci ricorda che l'input deve essere quantificato universalmente.
229\begin{hol}
230\begin{verbatim}
231   fun DPLL_UNIV t = let
232     val (vs, phi) = strip_forall t
233     val cnf_eqn = toCNF (mk_neg phi)
234     val phi' = rhs (concl cnf_eqn)
235   in
236     case DPLL phi' of
237       Unsat phi'_eq_F => let
238         val negphi_eq_F = TRANS cnf_eqn phi'_eq_F
239         val phi_thm = CONV_RULE (REWR_CONV NEG_EQ_F) negphi_eq_F
240       in
241         EQT_INTRO (GENL vs phi_thm)
242       end
243     | Sat f => let
244         val t_assumed = ASSUME t
245         fun spec th =
246             spec (SPEC (f (#1 (dest_forall (concl th)))) th)
247             handle HOL_ERR _ => REWRITE_RULE [] th
248       in
249         CONV_RULE (REWR_CONV IMP_F_EQ_F) (DISCH t (spec t_assumed))
250       end
251   end
252\end{verbatim}
253\end{hol}
254
255La funzione ausiliaria \ml{spec} che � usata nel secondo caso
256si basa sul fatto che \ml{dest\_forall} sollever� un'eccezione
257\ml{HOL\_ERR} se il termine a cui � applicata non � quantificato universalmente.
258Quando l'argomento di \ml{spec} non � quantificato universalmente, questo significa
259che la ricorsione � conclusa, e tutte le variabili universali della
260formula originaria sono state specializzate. Quindi la formula risultante
261pu� essere riscritta a falso (le riscritture incorporate in \ml{REWRITE\_RULE}
262gestiscono tutti i casi necessari).
263
264Anche la funzione \ml{DPLL\_UNIV} usa \ml{REWR\_CONV} in due posti.
265La funzione \ml{REWR\_CONV} applica una singola riscrittura (al primo ordine) al
266livello principale di un termine. Questi usi di \ml{REWR\_CONV} sono fatti con
267chiamate alla funzione \ml{CONV\_RULE}. Questa esegue [lifts ndt] un conversione $c$ (una
268funzione che prende un termine $t$ e che produce un teorema $\vdash t = t'$),
269cos� che $\ml{CONV\_RULE}\;c$ porta il teorema $\vdash t$ a $\vdash t'$.
270
271
272\subsection{Conversione in Forma Normale Congiuntiva}
273\label{sec:conv-conj-norm}
274
275Una formula in Forma Normale Congiuntiva � una congiunzione di disgiunzioni
276di letterali (cio� o variabili, o variabili negate). E' possibile
277convertire formule della forma che ci attendiamo in CNF semplicemente
278riscrivendo con il seguenti teoremi
279\begin{eqnarray*}
280\neg (\phi \land \psi) &=& \neg\phi \lor \neg\psi\\
281\neg (\phi \lor \psi) &=& \neg\phi \land \neg\psi\\
282\phi \lor (\psi \land \xi) &=& (\phi \lor \psi) \land (\phi \lor \xi)\\
283(\psi \land \xi)\lor\phi \ &=& (\phi \lor \psi) \land (\phi \lor
284\xi)\\[1ex]
285\phi \Rightarrow\psi &=& \neg\phi \lor \psi\\
286(\phi = \psi) &=& (\phi \Rightarrow \psi) \land (\psi \Rightarrow
287\phi)
288\end{eqnarray*}
289Sfortunatamente, usare questi teoremi come riscritture pu� risultare in un
290incremento esponenziale nella dimensione di una formula. (Si consideri di usarli
291per convertire un input in Forma Normale Disgiuntiva, una disgiunzione
292di congiunzioni di letterali, in CNF{}.)
293
294Un approccio migliore � di convertire in ci� che � conosciuta come una ``CNF
295definizionale''. \HOL{} include funzioni per fare questo nella structure
296\ml{defCNF}. Sfortunatamente, questo approccio aggiunge quantificatori, esistenziali,
297extra alla formula. Per esempio
298\begin{session}
299\begin{verbatim}
300- defCNF.DEF_CNF_CONV ``p \/ (q /\ r)``;
301> val it =
302    |- p \/ q /\ r =
303       ?x. (x \/ ~q \/ ~r) /\ (r \/ ~x) /\ (q \/ ~x) /\ (p \/ x) : thm
304\end{verbatim}
305\end{session}
306Sotto la \holtxt{x} legata esistenzialmente, il codice ha prodotto una
307formula in CNF{}. Con esempio piccolo come questo, la formula � di fatto
308pi� grande di quella prodotto dalla traduzione ingenua, ma con esempi
309pi� realistici, la differenza diventa velocemente significativa. L'ultimo
310esempio usato con \ml{tautDP} � 20 volte pi� grande quando tradotto
311ingenuamente di quando usando \ml{defCNF}, e la traduzione richiede 150
312volte pi� tempo per essere eseguita.
313
314Ma cosa facciamo di queste variabili quantificate esistenzialmente extra? Di fatto,
315possiamo ignorare la quantificazione quando chiamiamo la procedura core DPLL.
316Se passiamo il corpo non quantificato a DPLL, otteniamo indietro o un
317verdetto di insoddisfacibilit� della forma $\vdash \varphi' = \holtxt{F}$, o una
318assegnazione per tutte le variabili libere che soddisfa. Se capita la
319seconda, la stessa assegnazione soddisfer� anche
320l'originale. Se capita la prima, eseguiremo la seguente dimostrazione
321\[
322\infer{\vdash (\exists \vec{x}.\;\varphi') = \holtxt{F}}{
323  \infer{\vdash (\exists \vec{x}.\;\varphi') \Rightarrow \holtxt{F}}{
324    \infer{\vdash\forall \vec{x}.\;\varphi' \Rightarrow \holtxt{F}}{
325      \infer{\vdash\varphi' \Rightarrow \holtxt{F}}{
326        \vdash \varphi' = \holtxt{F}
327      }
328    }
329  }
330}
331\]
332producendo un teorema della forma attesa dalla nostra funzione
333\ml{wrapper}.
334
335Di fatto, c'� una funzione alternativa nell'API \ml{defCNF} che
336useremo preferendola a \ml{DEF\_CNF\_CONV}. Il problema con
337\ml{DEF\_CNF\_CONV} � che pu� produrre una grande quantificazione,
338che coinvolge molte variabili. Useremo piuttosto
339\ml{DEF\_CNF\_VECTOR\_CONV}. Al posto di un output della forma
340\[
341\vdash \varphi = (\exists \vec{x}.\; \varphi')
342\]
343questa seconda funzione produce
344\[
345\vdash \varphi = (\exists (v : \textsf{num} \rightarrow
346\textsf{bool}).\; \varphi')
347\]
348dove le variabili individuali $x_i$ della prima formula sono sostituite
349da chiamate alla funzione $v$ $v(i)$, e c'� una sola variabile
350quantificata, $v$. Questa variabile non influenzer� l'operazione della
351dimostrazione abbozzata di sopra. E fino a quando non richiediamo che i letterali siano
352variabili o le loro negazioni, ma permettiamo anche che siano termini della
353forma $v(i)$ e $\neg v(i)$, allora anche l'azione della procedura
354DPLL sulla formula $\varphi'$ non sar� influenzata.
355
356Sfortunatamente per uniformit�, in casi semplici, le funzioni di conversione
357a una CNF definizionale possono risultate in nessuna quantificazione
358esistenziale del tutto. Questo rende la nostra implementazione di \ml{DPLL}
359in qualche modo pi� complicata. Calcoliamo una variabile \ml{body} che
360sar� passata in cima alla funzione \ml{CoreDPLL}, esattamente come una
361funzione \ml{transform} che trasformer� un risultato di insoddisfacibilit�
362in qualcosa della forma desiderata. Se il risultato della conversione a
363CNF produce una quantificazione esistenziale, usiamo la dimostrazione abbozzata
364di sopra. Altrimenti, la trasformazione pu� essere la funzione identit�,
365\ml{I}:
366\begin{hol}
367\begin{verbatim}
368   fun DPLL t = let
369     val (transform, body) = let
370       val (vector, body) = dest_exists t
371       fun transform body_eq_F = let
372         val body_imp_F = CONV_RULE (REWR_CONV (GSYM IMP_F_EQ_F)) body_eq_F
373         val fa_body_imp_F = GEN vector body_imp_F
374         val ex_body_imp_F = CONV_RULE FORALL_IMP_CONV fa_body_imp_F
375       in
376         CONV_RULE (REWR_CONV IMP_F_EQ_F) ex_body_imp_F
377       end
378     in
379       (transform, body)
380     end handle HOL_ERR _ => (I, t)
381   in
382     case CoreDPLL body of
383       Unsat body_eq_F => Unsat (transform body_eq_F)
384     | x => x
385   end
386\end{verbatim}
387\end{hol}
388dove dobbiamo ancora implementare la procedura DPLL core (chiamata
389\ml{CoreDPLL} di sopra). Il codice di sopra usa \ml{REWR\_CONV} con il
390teorema \ml{IMP\_F\_EQ\_F} per influenzare due delle trasformazioni
391della dimostrazione. La funzione \ml{GSYM} � usata per invertire
392le uguaglianza di primo livello di un teorema. Infine,
393la conversione \ml{FORALL\_IMP\_CONV} prende un termine della forma
394\[
395\forall x.\;P(x) \Rightarrow Q
396\]
397e restituisce il teorema
398\[
399\vdash (\forall x.\;P(x) \Rightarrow Q) = ((\exists
400x.\;P(x))\Rightarrow Q)
401\]
402
403
404
405
406\subsection{La Procedura DPLL Core}
407\label{sec:dpll-procedure}
408
409La procedura pu� essere vista come una piccola variazione sulla tecnica
410di base ``tabelle di verit�'' che abbiamo gi� visto. Come con quella
411procedura, l'operazione core � un case-split su una variabile booleana.
412Ci sono tuttavia due differenze significative: DPLL pu� essere vista come
413una ricerca per un'assegnazione soddisfacente, cos� che se selezionando una variabile
414in modo da avere un particolare valore risulta in un'assegnazione soddisfacente, non abbiamo
415bisogno di controllare anche cosa accade se alla stessa variabile � dato il
416valore di verit� opposto. In secondo luogo, DPLL fa una certa attenzione a selezionare delle variabili
417opportune su cui fare lo split. In particolare � usata la \emph{unit propagation}
418per eliminare variabili che non causeranno ramificazioni nello
419spazio di ricerca.
420
421La nostra implementazione della procedura DPLL core � una funzione che prende
422un termine e restituisce un valore del tipo \ml{result}: o un teorema
423che uguaglia il termine originario a falso, o un'assegnazione soddisfacente (nella
424forma di una funzione da termini a termini). Mentre la ricerca DPLL per
425un'assegnazione soddisfacente procede, � costruita in modo incrementale
426un'assegnazione. Questo suggerisce che il nucleo ricorsivo della nostra funzione
427avr� bisogno di prendere un termine (la formula corrente) e un contesto (l'assegnazione
428corrente) come parametri. L'assegnazione pu� essere rappresentata in modo
429naturale come un insieme di equazioni, dove ciascuna equazione � o either $v =
430\holtxt{T}$ o $v = \holtxt{F}$.
431
432Questo suggerisce che una rappresentazione per la dichiarazione del nostro programma �
433un teorema: le ipotesi rappresenteranno l'assegnazione, e la conclusione
434pu� essere la formula corrente. Naturalmente, i teoremi \HOL{}
435non possono semplicemente essere desiderati esistere. In questo caso, possiamo rendere
436ogni cosa valida assumendo anche la formula iniziale. Cos�, quando
437iniziamo la nostra dichiarazione iniziale sar� $\phi\vdash\phi$. Dopo aver effettuato lo splitting sulla
438$v$, genereremo due nuove dichiarazioni
439$\phi,(v\!=\!\holtxt{T})\vdash \phi_1$, e
440$\phi,(v\!=\!\holtxt{F})\vdash \phi_2$, dove i $\phi_i$ sono il
441risultato di semplificare $\phi$ sotto l'assunzione aggiuntiva
442che vincola $v$.
443
444Il modo pi� semplice per aggiungere un'assunzione a un teorema � usare la
445regola \ml{ADD\_ASSUM}. Ma in questa situazione, vogliamo anche
446semplificare la conclusione del teorema con la stessa assunzione. Questo
447significa che sar� sufficiente riscrivere con il teorema
448$\psi\vdash\psi$, dove $\psi$ � la nuova assunzione. L'azione di
449riscrivere con un tale teorema far� apparire la nuova assunzione
450tra le assunzioni del risultato.
451
452La funzione \ml{casesplit} � cos�:
453\begin{hol}
454\begin{verbatim}
455   fun casesplit v th = let
456     val eqT = ASSUME (mk_eq(v, boolSyntax.T))
457     val eqF = ASSUME (mk_eq(v, boolSyntax.F))
458   in
459     (REWRITE_RULE [eqT] th, REWRITE_RULE [eqF] th)
460   end
461\end{verbatim}
462\end{hol}
463
464Un case split pu� risultare in una formula che � stata riscritta fino al
465vero o al falso. Questi sono i casi base della ricorsione. Se la
466formula � stata riscritta a vero, allora abbiamo trovato un'assegnazione
467soddisfacente, una che � ora archiviata per noi nelle ipotesi del
468teorema stesso. La seguente funzione \ml{mk\_satmap}, estrae
469quelle ipotesi in una map finita, e poi restituisce la funzione
470di lookup per quella map finita.
471\begin{hol}
472\begin{verbatim}
473   fun mk_satmap th = let
474     val hyps = hypset th
475     fun foldthis (t,acc) = let
476       val (l,r) = dest_eq t
477     in
478       Binarymap.insert(acc,l,r)
479     end handle HOL_ERR _ => acc
480     val fmap = HOLset.foldl foldthis (Binarymap.mkDict Term.compare) hyps
481   in
482     Sat (fn v => Binarymap.find(fmap,v)
483                  handle Binarymap.NotFound => boolSyntax.T)
484   end
485\end{verbatim}
486\end{hol}
487La funzione \ml{foldthis} di sopra aggiunge le equazioni che sono archiviate come
488ipotesi nella map finita. La gestione delle eccezione in
489\ml{foldthis} � necessario perch� una delle ipotesi sar� la
490formula originaria. La gestione delle eccezioni nella funzione che cerca
491i binding delle variabili � necessaria perch� una formula pu� essere ridotta a
492vero senza che tutte le variabili siano state assegnate a un valore qualsiasi, cos�
493mappiamo in modo arbitrario tali variabili a \holtxt{T}.
494
495Se la formula � stata riscritta a falso, allora possiamo semplicemente restituire
496questo teorema direttamente. Un tale teorema non � del tutto nella forma giusta
497per il chiamante esterno, che si aspetta un'equazione, cos� se il
498risultato finale � della forma $\phi\vdash \holtxt{F}$, dovremo
499trasformare questo in $\vdash \phi = \holtxt{F}$.
500
501La domanda successiva da affrontare � cosa fare con i risultati delle
502chiamate ricorsive. Se un case split restituisce un'assegnazione soddisfacente questa
503pu� essere restituita senza modifiche. Ma se una chiamata ricorsiva restituisce un teorema
504che uguaglia l'input a falso, c'� bisogno di fare di pi�. Se questa � la
505prima chiamata, allora � necessario controllare l'altro ramo. Se anche questo
506restituisce che il teorema � insoddisfacibile, allora abbiamo due teoremi.
507\[
508\phi_0,\Delta,(v\!=\!\holtxt{T})\vdash \holtxt{F} \qquad
509\phi_0,\Delta,(v\!=\!\holtxt{F})\vdash \holtxt{F}
510\] dove $\phi_0$ � la formula originaria, $\Delta$ � il resto
511dell'assegnazione corrente, e $v$ � la variabile su cui � stato appena eseguito
512uno split. Per trasformare questi due teoremi nel desiderato
513\[
514\phi_0,\Delta\vdash \holtxt{F}
515\]
516useremo la regola d'inferenza \ml{DISJ\_CASES}:
517\[
518\infer{\Gamma \cup \Delta_1 \cup \Delta_2 \vdash \phi}{
519  \Gamma \vdash \psi \lor \xi &
520  \Delta_1 \cup \{\psi\}\vdash \phi &
521  \Delta_2 \cup \{\xi\} \vdash \phi
522}
523\]
524e il teorema \ml{BOOL\_CASES\_AX}:
525\[
526\vdash \forall t.\;(t = \holtxt{T}) \lor (t = \holtxt{F})
527\]
528
529Possiamo mettere insieme questi frammenti e scrivere la funzione \ml{CoreDPLL}
530di alto livello, nella Figura~\ref{fig:coredpll}.
531\begin{figure}[htbp]
532\begin{holboxed}
533\begin{verbatim}
534fun CoreDPLL form = let
535  val initial_th = ASSUME form
536  fun recurse th = let
537    val c = concl th
538  in
539    if c = boolSyntax.T then
540      mk_satmap th
541    else if c = boolSyntax.F then
542      Unsat th
543    else let
544        val v = find_splitting_var c
545        val (l,r) = casesplit v th
546      in
547        case recurse l of
548          Unsat l_false => let
549          in
550            case recurse r of
551              Unsat r_false =>
552                Unsat (DISJ_CASES (SPEC v BOOL_CASES_AX) l_false r_false)
553            | x => x
554          end
555        | x => x
556      end
557  end
558in
559  case (recurse initial_th) of
560    Unsat th => Unsat (CONV_RULE (REWR_CONV IMP_F_EQ_F) (DISCH form th))
561  | x => x
562end
563\end{verbatim}
564\end{holboxed}
565\caption{Il nucleo della funzione DPLL}
566\label{fig:coredpll}
567\end{figure}
568
569
570Tutto ci� che rimane da fare � capire su quale variabile effettuare
571il case split. Le variabili pi� importanti su cui effettuare lo split sono quelle
572che appaiono in quelle che sono chiamate ``unit clauses'', clausole che contengono
573solo un letterale. Se c'� una unit clause in una formula allora �
574della forma
575\[
576\phi \land v \land \phi'
577\]
578o
579\[
580\phi \land \neg v \land \phi'
581\]
582In entrambe le situazioni, effettuare lo split su $v$ risulter� sempre in un ramo
583che valuta direttamente a falso. Cos� eliminiamo una variabile
584senza accrescere la dimensione del problema. Il processo di
585eliminare clausole unitarie � di solito chiamato ``unit propagation''.
586La unit propagation di solito non � pensata come un'operazione
587di case splitting, ma eseguirla in questo modo render� il nostro codice pi� semplice.
588
589Se una formula non include una clausola unitaria, allora la scelta della prossima
590variabile su cui eseguire lo split � molto pi� di una magia nera. Qui
591implementeremo una scelta molto semplice: per eseguire lo split sulla variabile che occorre
592pi� di frequente. La nostra funzione \ml{find\_splitting\_var} prende una formula
593e restituisce la variabile su cui eseguire lo split.
594\begin{hol}
595\begin{verbatim}
596   fun find_splitting_var phi = let
597     fun recurse acc [] = getBiggest acc
598       | recurse acc (c::cs) = let
599           val ds = strip_disj c
600         in
601           case ds of
602             [lit] => (dest_neg lit handle HOL_ERR _ => lit)
603           | _ => recurse (count_vars ds acc) cs
604         end
605   in
606     recurse (Binarymap.mkDict Term.compare) (strip_conj phi)
607   end
608\end{verbatim}
609\end{hol}
610Questa funzione lavora passando una lista di clausole alla funzione
611pi� interna \ml{recurse}. Questa spoglia ciascuna clausola una alla volta. Se una
612clausola ha un solo disgiunto � una clausola unitaria e la variabile pu�
613essere restituita direttamente. Altrimenti, le variabili nelle clausole sono
614contate e aggiunte da \ml{count\_vars} alla map che si accumula, e la
615ricorsione pu� continuare.
616
617La funzione \ml{count\_vars} ha la seguente implementazione:
618\begin{hol}
619\begin{verbatim}
620   fun count_vars ds acc =
621     case ds of
622       [] => acc
623     | lit::lits => let
624         val v = dest_neg lit handle HOL_ERR _ => lit
625       in
626         case Binarymap.peek (acc, v) of
627           NONE => count_vars lits (Binarymap.insert(acc,v,1))
628         | SOME n => count_vars lits (Binarymap.insert(acc,v,n + 1))
629       end
630\end{verbatim}
631\end{hol}
632
633L'uso di un albero binario per archiviare dati variabili rende efficiente
634aggiornare i dati mentre sono raccolti. Estrarre la variabile
635con il conteggio pi� grande � poi una scansione lineare dell'albero, che possiamo
636fare con la funzione \ml{foldl}:
637\begin{hol}
638\begin{verbatim}
639   fun getBiggest acc =
640     #1 (Binarymap.foldl(fn (v,cnt,a as (bestv,bestcnt)) =>
641                            if cnt > bestcnt then (v,cnt) else a)
642                        (boolSyntax.T, 0)
643                        acc
644\end{verbatim}
645\end{hol}
646
647\subsection{Performance}
648\label{sec:dpll-performance}
649
650Quando vengono dati input un p� oltre quelli chiaramente banali, la funzione
651che abbiamo scritto (al livello alto, \ml{DPLL\_UNIV}) ha delle performance considerevolmente
652migliori rispetto all'implementazione con le tavole di verit�. Per esempio, la
653generalizzazione del seguente termine, con 29 variabili, richiede a
654\ml{wrapper} tre secondi e mezzo per essere dimostrata come tautologia:
655\begin{hol}
656\begin{verbatim}
657   (s0_0 = (x_0 = ~y_0)) /\ (c0_1 = x_0 /\ y_0) /\
658   (s0_1 = ((x_1 = ~y_1) = ~c0_1)) /\
659   (c0_2 = x_1 /\ y_1 \/ (x_1 \/ y_1) /\ c0_1) /\
660   (s0_2 = ((x_2 = ~y_2) = ~c0_2)) /\
661   (c0_3 = x_2 /\ y_2 \/ (x_2 \/ y_2) /\ c0_2) /\
662   (s1_0 = ~(x_0 = ~y_0)) /\ (c1_1 = x_0 /\ y_0 \/ x_0 \/ y_0) /\
663   (s1_1 = ((x_1 = ~y_1) = ~c1_1)) /\
664   (c1_2 = x_1 /\ y_1 \/ (x_1 \/ y_1) /\ c1_1) /\
665   (s1_2 = ((x_2 = ~y_2) = ~c1_2)) /\
666   (c1_3 = x_2 /\ y_2 \/ (x_2 \/ y_2) /\ c1_2) /\
667   (c_3 = ~c_0 /\ c0_3 \/ c_0 /\ c1_3) /\
668   (s_0 = ~c_0 /\ s0_0 \/ c_0 /\ s1_0) /\
669   (s_1 = ~c_0 /\ s0_1 \/ c_0 /\ s1_1) /\
670   (s_2 = ~c_0 /\ s0_2 \/ c_0 /\ s1_2) /\ ~c_0 /\
671   (s2_0 = (x_0 = ~y_0)) /\ (c2_1 = x_0 /\ y_0) /\
672   (s2_1 = ((x_1 = ~y_1) = ~c2_1)) /\
673   (c2_2 = x_1 /\ y_1 \/ (x_1 \/ y_1) /\ c2_1) /\
674   (s2_2 = ((x_2 = ~y_2) = ~c2_2)) /\
675   (c2_3 = x_2 /\ y_2 \/ (x_2 \/ y_2) /\ c2_2) ==>
676   (c_3 = c2_3) /\ (s_0 = s2_0) /\ (s_1 = s2_1) /\ (s_2 = s2_2)
677\end{verbatim}
678\end{hol}
679(Se si desidera la velocit� reale, la funzione incorporata \ml{tautLib.TAUT\_PROVE} esegue ci� che sta sopra in meno di un centesimo di secondo, usando uno strumento esterno per generare la dimostrazione di insoddisfacibilit�, e per poi tradurre quella dimostrazione indietro in HOL.)
680
681\section{Estendere l'Applicabilit� della nostra Procedura}
682\label{sec:dpll-applicability-extension}
683
684La funzione \ml{DPLL\_UNIV} richiede che il suo input sia quantificato
685universalmente, con tutte le variabili libere legate, e che ciascun letterale sia
686una variabile o la negazione di una variabile. Questo rende \ml{DPLL\_UNIV}
687poco facile da usare quando viene ad essere usata come parte della dimostrazione di
688un goal. In questa sezione, scriveremo un ulteriore livello
689``wrapper'' per avvolgere \ml{DPLL\_UNIV}, producendo uno strumento che pu�
690essere applicato a molti pi� goal.
691
692\paragraph{Mitigare il Requisito di Quantificazione}  Il primo passo �
693permettere formule che non sono chiuse. Al fine di trasmettere una formula
694che \emph{�} chiusa a \ml{DPLL\_UNIV}, possiamo semplicemente generalizzare
695sulle variabili libere della formula. Se \ml{DPLL\_UNIV} dice poi che
696la nuova, formula ground � vera, allora lo sar� anche l'originale. Dall'altro
697lato, se \ml{DPLL\_UNIV} dice che la formula ground �
698falsa, allora non possiamo concludere niente di pi� e dovremo sollevare
699un'eccezione.
700
701Il codice che implementa questo � mostrato di sotto:
702\begin{hol}
703\begin{verbatim}
704   fun nonuniv_wrap t = let
705     val fvs = free_vars t
706     val gen_t = list_mk_forall(fvs, t)
707     val gen_t_eq = DPLL_UNIV gen_t
708   in
709     if rhs (concl gen_t_eq) = boolSyntax.T then let
710         val gen_th = EQT_ELIM gen_t_eq
711       in
712         EQT_INTRO (SPECL fvs gen_th)
713       end
714     else
715       raise mk_HOL_ERR "dpll" "nonuniv_wrap" "No conclusion"
716   end
717\end{verbatim}
718\end{hol}
719
720\paragraph{Permettere Foglie Non Letterali}
721Possiamo fare meglio di \ml{nonuniv\_wrap}: piuttosto che quantificare solamente
722solo sulle variabili libere (che abbiamo convenientemente assunto che saranno solo
723booleane), possiamo trasformare ogni parte foglia del termine che non � una
724variabile o la negazione di una variabile in una nuova variabile. Prima
725estraiamo quelle foglie con valore booleano che non sono le costanti vero o
726falso.
727\begin{hol}
728\begin{verbatim}
729   fun var_leaves acc t = let
730     val (l,r) = dest_conj t handle HOL_ERR _ =>
731                 dest_disj t handle HOL_ERR _ =>
732                 dest_imp t handle HOL_ERR _ =>
733                 dest_bool_eq t
734   in
735     var_leaves (var_leaves acc l) r
736   end handle HOL_ERR _ =>
737     if type_of t <> bool then
738       raise mk_HOL_ERR "dpll" "var_leaves" "Term not boolean"
739     else if t = boolSyntax.T then acc
740     else if t = boolSyntax.F then acc
741     else HOLset.add(acc, t)
742\end{verbatim}
743\end{hol}
744Si noti che non abbiamo tentato esplicitamente di separare le negazioni
745booleane (il che si potrebbe fare con \ml{dest\_neg}). Questo perch�
746anche \ml{dest\_imp} distrugge termini \holtxt{\~{}p}, restituendo
747\holtxt{p} e \holtxt{F} come l'antecedente e la conclusione. Abbiamo
748anche usato una funzione \ml{dest\_bool\_eq} progettata per dividere solo
749quelle uguaglianze che sono su valori booleani. La sua definizione �
750\begin{hol}
751\begin{verbatim}
752   fun dest_bool_eq t = let
753     val (l,r) = dest_eq t
754     val _ = type_of l = bool orelse
755             raise mk_HOL_ERR "dpll" "dest_bool_eq" "Eq not on bools"
756   in
757     (l,r)
758   end
759\end{verbatim}
760\end{hol}
761
762Ora possiamo scrivere la nostra funzione finale \ml{DPLL\_TAUT}:
763\begin{hol}
764\begin{verbatim}
765   fun DPLL_TAUT tm =
766     let val (univs,tm') = strip_forall tm
767         val insts = HOLset.listItems (var_leaves empty_tmset tm')
768         val vars = map (fn t => genvar bool) insts
769         val theta = map2 (curry (op |->)) insts vars
770         val tm'' = list_mk_forall (vars,subst theta tm')
771     in
772         EQT_INTRO (GENL univs
773                      (SPECL insts (EQT_ELIM (DPLL_UNIV tm''))))
774     end
775\end{verbatim}
776\end{hol}
777Si noti come questo codice prima tira fuori tutte le quantificazioni universali
778esterne (con \ml{strip\_forall}), e poi ri-generalizza
779(con \ml{list\_mk\_forall}). Le chiamate a \ml{GENL} e \ml{SPECL}
780annullano queste manipolazioni, ma al livello dei teoremi. Questo produce
781un teorema che uguaglia l'input originale a vero. (Se il termine di input non
782� un'istanza di una formula proposizionale valida, la chiamata a
783\ml{EQT\_ELIM} sollever� un'eccezione.)
784
785\section*{Esercizi}
786
787\begin{enumerate}
788\item Estendere la procedura cos� che gestisca le espressioni condizionali
789	(entrambi i rami dei termini devono essere di tipo booleano).
790\end{enumerate}
791
792
793%%% Local Variables:
794%%% mode: latex
795%%% TeX-master: "tutorial"
796%%% End:
797