61
|
1 |
theory ExternalSolver
|
|
2 |
imports "../Base"
|
94
|
3 |
uses ("external_solver.ML")
|
61
|
4 |
begin
|
|
5 |
|
94
|
6 |
|
|
7 |
section {* Executing an External Application *}
|
61
|
8 |
|
|
9 |
text {*
|
|
10 |
{\bf Problem:}
|
94
|
11 |
You want to use an external application.
|
|
12 |
\smallskip
|
|
13 |
|
|
14 |
{\bf Solution:} The function @{ML system_out} might be the right thing for
|
|
15 |
you.
|
61
|
16 |
\smallskip
|
|
17 |
|
94
|
18 |
This function executes an external command as if printed in a shell. It
|
|
19 |
returns the output of the program and its return value.
|
|
20 |
|
|
21 |
For example, consider running an ordinary shell commands:
|
|
22 |
|
|
23 |
@{ML_response [display,gray]
|
|
24 |
"system_out \"echo Hello world!\"" "(\"Hello world!\\n\", 0)"}
|
|
25 |
|
|
26 |
Note that it works also fine with timeouts (see Recipe~\ref{rec:timeout}
|
|
27 |
on Page~\pageref{rec:timeout}), i.e. external applications are killed
|
|
28 |
properly. For example, the following expression takes only approximately
|
|
29 |
one second:
|
|
30 |
|
|
31 |
@{ML_response [display,gray]
|
|
32 |
"TimeLimit.timeLimit (Time.fromSeconds 1) system_out \"sleep 30\"
|
|
33 |
handle TimeLimit.TimeOut => (\"timeout\", ~1)" "(\"timeout\", ~1)"}
|
|
34 |
*}
|
|
35 |
|
|
36 |
text {*
|
|
37 |
The function @{ML system_out} can also be used for more reasonable
|
|
38 |
applications, e.g. coupling external solvers with Isabelle. In that case,
|
|
39 |
one has to make sure that Isabelle can find the particular executable.
|
|
40 |
One way to ensure this is by adding a Bash-like variable binding into
|
|
41 |
one of Isabelle's settings file (prefer the user settings file usually to
|
|
42 |
be found at @{text "$HOME/.isabelle/etc/settings"}).
|
|
43 |
|
|
44 |
For example, assume you want to use the application @{text foo} which
|
|
45 |
is here supposed to be located at @{text "/usr/local/bin/"}.
|
|
46 |
The following line has to be added to one of Isabelle's settings file:
|
|
47 |
|
|
48 |
@{text "FOO=/usr/local/bin/foo"}
|
|
49 |
|
|
50 |
In Isabelle, this application may now be executed by
|
|
51 |
|
|
52 |
@{ML_response_fake [display,gray] "system_out \"$FOO\"" "\<dots>"}
|
|
53 |
*}
|
|
54 |
|
|
55 |
|
|
56 |
|
|
57 |
|
|
58 |
|
|
59 |
|
|
60 |
section {* Writing an Oracle\label{rec:external} *}
|
|
61 |
|
|
62 |
text {*
|
|
63 |
{\bf Problem:}
|
|
64 |
You want to use a fast, new decision procedure not based one Isabelle's
|
|
65 |
tactics, and you do not care whether it is sound.
|
|
66 |
\smallskip
|
|
67 |
|
|
68 |
{\bf Solution:} Isabelle provides the oracle mechanisms to bypass the
|
|
69 |
inference kernel. Note that theorems proven by an oracle carry a special
|
|
70 |
mark to inform the user of their potential incorrectness.
|
61
|
71 |
\smallskip
|
|
72 |
|
|
73 |
\begin{readmore}
|
|
74 |
A short introduction to oracles can be found in [isar-ref: no suitable label
|
94
|
75 |
for section 3.11]. A simple example, which we will slightly extend here,
|
|
76 |
is given in @{ML_file "FOL/ex/IffOracle"}. The raw interface for adding
|
|
77 |
oracles is @{ML add_oracle in Thm} in @{ML_file "Pure/thm"}.
|
61
|
78 |
\end{readmore}
|
|
79 |
|
94
|
80 |
For our explanation here, we restrict ourselves to decide propositional
|
|
81 |
formulae which consist only of equivalences between propositional variables,
|
|
82 |
i.e. we want to decide whether @{term "P = (Q = P) = Q"} is a tautology.
|
|
83 |
|
|
84 |
Assume, that we have a decision procedure for such formulae, implemented
|
|
85 |
in ML. Since we do not care how it works, we will use it here as an
|
|
86 |
``external solver'':
|
|
87 |
*}
|
|
88 |
|
|
89 |
use "external_solver.ML"
|
61
|
90 |
|
94
|
91 |
text {*
|
|
92 |
We do, however, know that the solver provides a function
|
|
93 |
@{ML IffSolver.decide}.
|
|
94 |
It takes a string representation of a formula and returns either
|
|
95 |
@{ML true} if the formula is a tautology or
|
|
96 |
@{ML false} otherwise. The input syntax is specified as follows:
|
|
97 |
|
|
98 |
formula $::=$ atom $\mid$ \verb|(| formula \verb|<=>| formula \verb|)|
|
|
99 |
|
|
100 |
and all token are separated by at least one space.
|
|
101 |
|
|
102 |
(FIXME: is there a better way for describing the syntax?)
|
|
103 |
|
|
104 |
We will proceed in the following way. We start by translating a HOL formula
|
|
105 |
into the string representation expected by the solver. The solver's result
|
|
106 |
is then used to build an oracle, which we will subsequently use as a core
|
|
107 |
for an Isar method to be able to apply the oracle in proving theorems.
|
|
108 |
|
|
109 |
Let us start with the translation function from Isabelle propositions into
|
|
110 |
the solver's string representation. To increase efficiency while building
|
|
111 |
the string, we use functions from the @{text Buffer} module.
|
61
|
112 |
*}
|
|
113 |
|
94
|
114 |
ML {*fun translate t =
|
|
115 |
let
|
|
116 |
fun trans t =
|
|
117 |
(case t of
|
|
118 |
@{term "op = :: bool \<Rightarrow> bool \<Rightarrow> bool"} $ t $ u =>
|
|
119 |
Buffer.add " (" #>
|
|
120 |
trans t #>
|
|
121 |
Buffer.add "<=>" #>
|
|
122 |
trans u #>
|
|
123 |
Buffer.add ") "
|
|
124 |
| Free (n, @{typ bool}) =>
|
|
125 |
Buffer.add " " #>
|
|
126 |
Buffer.add n #>
|
|
127 |
Buffer.add " "
|
|
128 |
| _ => error "inacceptable term")
|
|
129 |
in Buffer.content (trans t Buffer.empty) end
|
61
|
130 |
*}
|
|
131 |
|
94
|
132 |
text {*
|
|
133 |
Here is the string representation of the term @{term "p = (q = p)"}:
|
61
|
134 |
|
94
|
135 |
@{ML_response
|
|
136 |
"translate @{term \"p = (q = p)\"}"
|
|
137 |
"\" ( p <=> ( q <=> p ) ) \""}
|
|
138 |
|
|
139 |
Let us check, what the solver returns when given a tautology:
|
|
140 |
|
|
141 |
@{ML_response
|
|
142 |
"IffSolver.decide (translate @{term \"p = (q = p) = q\"})"
|
|
143 |
"true"}
|
|
144 |
|
|
145 |
And here is what it returns for a formula which is not valid:
|
|
146 |
|
|
147 |
@{ML_response
|
|
148 |
"IffSolver.decide (translate @{term \"p = (q = p)\"})"
|
|
149 |
"false"}
|
|
150 |
*}
|
61
|
151 |
|
79
|
152 |
text {*
|
94
|
153 |
Now, we combine these functions into an oracle. In general, an oracle may
|
|
154 |
be given any input, but it has to return a certified proposition (a
|
|
155 |
special term which is type-checked), out of which Isabelle's inference
|
|
156 |
kernel ``magically'' makes a theorem.
|
79
|
157 |
|
94
|
158 |
Here, we take the proposition to be show as input. Note that we have
|
|
159 |
to first extract the term which is then passed to the translation and
|
|
160 |
decision procedure. If the solver finds this term to be valid, we return
|
|
161 |
the given proposition unchanged to be turned then into a theorem:
|
61
|
162 |
*}
|
|
163 |
|
94
|
164 |
oracle iff_oracle = {* fn ct =>
|
|
165 |
if IffSolver.decide (translate (HOLogic.dest_Trueprop (Thm.term_of ct)))
|
|
166 |
then ct
|
|
167 |
else error "Proof failed."*}
|
61
|
168 |
|
94
|
169 |
text {*
|
|
170 |
Here is what we get when applying the oracle:
|
|
171 |
|
|
172 |
@{ML_response_fake "iff_oracle @{cprop \"p = (p::bool)\"}" "p = p"}
|
|
173 |
|
|
174 |
(FIXME: is there a better way to present the theorem?)
|
|
175 |
|
|
176 |
To be able to use our oracle for Isar proofs, we wrap it into a tactic:
|
61
|
177 |
*}
|
|
178 |
|
94
|
179 |
ML{*val iff_oracle_tac =
|
|
180 |
CSUBGOAL (fn (goal, i) =>
|
|
181 |
(case try iff_oracle goal of
|
|
182 |
NONE => no_tac
|
|
183 |
| SOME thm => rtac thm i))*}
|
79
|
184 |
|
94
|
185 |
text {*
|
|
186 |
and create a new method solely based on this tactic:
|
|
187 |
*}
|
79
|
188 |
|
94
|
189 |
method_setup iff_oracle = {*
|
|
190 |
Method.no_args (Method.SIMPLE_METHOD' iff_oracle_tac)
|
|
191 |
*} "Oracle-based decision procedure for chains of equivalences"
|
|
192 |
|
|
193 |
text {*
|
|
194 |
(FIXME: what does @{ML "Method.SIMPLE_METHOD'"} do? ... what do you mean?)
|
79
|
195 |
|
94
|
196 |
Finally, we can test our oracle to prove some theorems:
|
|
197 |
*}
|
61
|
198 |
|
94
|
199 |
lemma "p = (p::bool)"
|
|
200 |
by iff_oracle
|
|
201 |
|
|
202 |
lemma "p = (q = p) = q"
|
|
203 |
by iff_oracle
|
79
|
204 |
|
|
205 |
|
|
206 |
text {*
|
94
|
207 |
(FIXME: say something about what the proof of the oracle is ... what do you mean?)
|
83
|
208 |
*}
|
|
209 |
|
|
210 |
|
61
|
211 |
end |