File: DerivingProofs.v

package info (click to toggle)
coq-quickchick 2.1.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 2,432 kB
  • sloc: ml: 4,367; ansic: 789; makefile: 388; sh: 27; python: 4; perl: 2; lisp: 2
file content (391 lines) | stat: -rw-r--r-- 12,554 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
From Coq Require Import Init.Nat List.
Import ListNotations.

From QuickChick Require Import QuickChick.
Import QcNotation. Import QcDefaultNotation.

Require Import Coq.Strings.Ascii.

Open Scope qc_scope.
Open Scope nat_scope.

(* First of all, we need to import the proof-related modules, as they
   are not exported by default. *)
From QuickChick Require Import EnumProofs CheckerProofs.

(** Regexp Matching *)

(* This example is taken from the Logical Foundations Volume of Software Foundations textbook *) 

Definition string := list ascii.

(* We start with an inductive definion of the regular expression data type *)
Inductive reg_exp (T : Type) : Type :=
  | EmptySet
  | EmptyStr
  | Char (t : T)
  | App (r1 r2 : reg_exp T)
  | Union (r1 r2 : reg_exp T)
  | Star (r : reg_exp T).

(* We can then derive an enumerator for inhabitants of this data type *) 
Derive EnumSized for reg_exp.

(* Prints: EnumSizedreg_exp is defined *)

About EnumSizedreg_exp.

(* EnumSizedreg_exp : forall {T : Type}, Enum T -> EnumSized (reg_exp T) *) 

(* The [EnumSizedreg_exp] definition is a function that for each type
   T that can be enumerated (reflected in the typeclass constrain
   [Enum T]), returns an Instance of the typeclass [EnumSized]. *)

(* Let's look more closely at these two typeclasses.

   First, what is an enumerator?

   The enumerator monad
   --------------------

   Inductive EnumType (A : Type) : Type :=
    MkEnum : (nat -> LazyList A) -> EnumType A

   The type of enumerators is [EnumType] (or for short, just [E]).
   This type encapsulates a function of type [nat -> LazyList A].
   The [nat] parameter is an upper bound for the enumeration process. 
   The return type is a lazy list that contains all inhabitants of
   type A up to the given size.

   The Enum typeclass
   ------------------

   Class Enum (A : Type) : Type := { enum : E A }


   The [Enum] typeclass then is the class of all types that have an enumerator.  


   The EnumSized typeclass
   -----------------------
   The [EnumSized] typeclass is similar.

   Class EnumSized (A : Type) := { enumSized : nat -> E A }.

   The difference is that the enumerators of the EnumSized class have an
   additional [nat] parameter that is commonly used to bound the recursion
   depth. 
   
   From EnumSized to Enum
   ----------------------
   We can go from [EnumSized] to [Enum] using the [sized] combinator
   that internalizes the size parameter of [EnumSized]. Given an
   [EnumSized] instance, we can automatically derive an [Enum]
   instance with typeclass resolution.

*) 

(* After automatically generating the [EnumSized] instance, we can
   generate correctness proofs. To do this proof we first define a
   "set-of-outcomes" semantics for our enumerator.

   In particular, the combinator [semEnumSize], with signature:

   semEnumSize : forall {A : Type}, E A -> nat -> set A

   maps an enumerator of type A to set indexed by a size. This is the
   set of element that can be generated for each value of the internal
   size parameter. We will use this to state properties of enumerators.

   Before generating correctness we need some crucial monotonicity
   properties. 


   Monotonicity in the external size parameter
   --------------------------------------
   First, we prove that the enumerator is monotonic in the external size
   parameter. That is, 
   
   forall s s1 s2 : nat,
   s1 <= s2 -> semProdSize (enumSized s) s1 \subset (enumSized s) s2

   This property is captured by the [SizeMonotonic] typeclass. Again.
   we automatically instances of these typeclass.

*)


#[local] Instance EnumSizedreg_exp_SizedMonotonic T {_ : Enum T} :
  SizedMonotonic (@enumSized _ (@EnumSizedreg_exp T _)).
Proof. derive_enum_SizedMonotonic. Qed. 


(* Monotonicity in the internal size parameter
   --------------------------------------
   We also prove that the enumerator is monotonic in the internal size
   parameter. That is, 
   
   forall s s1 s2 : nat,
   s1 <= s2 -> semEnumSize (enumSized s1) s \subset semEnumSize (enumSized s2) s

   This property is captured by the [SizeMonotonic] typeclass. We automatically
   derive instances of this typeclass using our Ltac2 proofscript
   [derive_enum_SizedMonotonic]. 
*) 

#[local] Instance EnumSizedreg_exp_SizeMonotonic T `{EnumMonotonic T}:
  forall s, SizeMonotonic (@enumSized _ (@EnumSizedreg_exp T _) s).
Proof. derive_enum_SizeMonotonic. Qed.


(* Correctness
   -----------
   We use the two monotonicity properties to prove correctness.
   For simple enumerators like this one, correctness states:

   { x | exists s s', x \in semEnumSize (enumSized s) s' } <--> [set: A]

   That is, the set of elements that belongs to [semEnumSize (enumSized s) s']
   for some size parameters s (external) and s' (internal), is exactly the set
   of elements of type A (denoted [set: A]). The notation <--> denotes set
   equality.

 *)


#[local] Instance EnumSizedreg_expCorrect T `{EnumMonotonicCorrect T}:
  CorrectSized (@enumSized _ EnumSizedreg_exp).
Proof. derive_enum_Correct. Qed.

(* Now that we have seen how enumeration of simple data types works,
   we can move on to enumeration and checking of inductive relations.

   We focus on regular expression matching. 
 *)

Reserved Notation "s =~ re" (at level 80).

Definition app := @app.

Arguments EmptySet {T}.
Arguments EmptyStr {T}.
Arguments Char {T} _.
Arguments App {T} _ _.
Arguments Union {T} _ _.
Arguments Star {T} _.

(* The following inductive relation holds whenever a string of
   characters drawn from a set [T] matches a regular expression.
 *) 

Inductive exp_match {T: Type} : list T -> reg_exp T -> Prop :=
  | MEmpty : [] =~ EmptyStr
  | MChar x : [x] =~ (Char x)
  | MApp s1 re1 s2 re2 :
      s1 =~ re1 ->
      s2 =~ re2 ->
      s1 ++ s2 =~ (App re1 re2)
  | MUnionL s1 re1 re2 : 
      s1 =~ re1 ->
      s1 =~ (Union re1 re2)
  | MUnionR re1 s2 re2 : 
      s2 =~ re2 ->
      s2 =~ (Union re1 re2)
  | MStar0 re : [] =~ (Star re)
  | MStarApp s1 s2 re : 
      s1 =~ re ->
      s2 =~ (Star re) ->
      s1 ++ s2 =~ (Star re)
  where "s =~ re" := (exp_match s re).

(** Checkers *) 

(* First, we can generate a checker for [exp_match]. *)

(*
Instance EnumTest (y : nat) : EnumSizedSuchThat _ (fun x => eq x y).
Admitted.

Instance Dec (x y : nat) : DecOpt (x = y).
Admitted.
 *)

Derive DecOpt for (exp_match l e).
(* DecOptexp_match is defined *)

Derive EnumSizedSuchThat for (fun l => exp_match l e). 

About DecOptexp_match.
(* DecOptexp_match :
   forall {T : Type},
     Dec_Eq T ->
     Enum T ->
     forall (l : list T) (e : reg_exp T), DecOpt (l =~ e) *)

(* That is, for all types [T] that have decidable equality and are
   enumerable (this constraint is always present even though the
   checker does now always use it), for all inputs [l] and [e], we can
   decide weather the string [l] matches the regular expression [e].

   Checkers are described with the DecOpt typeclass. 

   The DecOpt typeclass
   --------------------

   Class DecOpt (P : Prop) := { decOpt : nat -> option bool }.

   This typeclass describes [Prop]'s  that have a semi-decision
   procedures of type [nat -> option bool]. The natural number
   is the fuel that bounds the recursion depth of the generated
   procedure. Conceptually, the procedure can decide the validity
   of [Prop]'s whose proof derivation have depth less or equal to
   this number.

 *)

(* We can now prove correctness of the derived checker.

   Monotonicity
   ------------

   As before, we need to show monotonicity. In particular, we show that
   if the validity of a proposition has been decided, then the decision
   will not change by providing more fuel. In particular:

   forall (s1 s2 : nat) (b : bool),
     s1 <= s2 -> decOpt s1 = Some b -> decOpt s2 = Some b


   This is capture by the [DecOptSizeMonotonic] typeclass. As before, an instance
   is derived automatically. 

 *)

#[local] Instance DecOptexp_match_monotonic {T} `{_ : Dec_Eq T} `{_ : EnumMonotonic T} (m : list T) n :
  DecOptSizeMonotonic (exp_match m n).
Proof. derive_mon. Qed.

(* Soundness and Completeness
   --------------------------

   Using monotonicity we can prove soundness and completeness.

   Soundness states:

   forall (P : Prop) (H : DecOpt P) (s : nat), decOpt s = Some true -> P

   That is, is [decOpt s] is [true] for some [s], then [P] holds.

   It is captured by the [DecOptSoundPos] typeclass.

   Completeness states:

   forall (P : Prop) (H : DecOpt P), P -> exists s : nat, decOpt s = Some true

   That is, if P holds then there exists some fuel [s] such that [decOpt s] is true.

   Again, we derive these instances automatically. *)


#[local] Instance DecOptexp_match_sound {T} `{_ : Dec_Eq T} `{_ : EnumMonotonicCorrect T} (m : list T) n :
  DecOptSoundPos (exp_match m n).
Proof. derive_sound. Qed.

#[local] Instance DecOptexp_match_complete {T} `{_ : Dec_Eq T} `{_ : EnumMonotonicCorrect T} (m : list T) n :
  DecOptCompletePos (exp_match m n).
Proof. derive_complete. Qed. 


(* Enumeration for the Eq predicate (TODO move) (required by [exp_match]) *)

Derive EnumSizedSuchThat for (fun n => eq x n).

#[local] Instance EnumSizedSuchThateq_SizedMonotonic X {_ : Enum X} {_ : Dec_Eq X} (n : X) :
  SizedMonotonicOptFP (@enumSizeST _ _ (EnumSizedSuchThateq n)).
Proof. derive_enumST_SizedMonotonicFP. Qed.

#[local] Instance EnumSizedSuchThateq_SizeMonotonic X `{_ : EnumMonotonic X} {_ : Dec_Eq X} (n : X) :
  forall s, SizeMonotonicOptFP (@enumSizeST _ _ (EnumSizedSuchThateq n) s).
Proof. derive_enumST_SizeMonotonicFP. Qed.

(* TODO: FIX:
#[local] Instance EnumSizedSuchThateq_Correct X `{_ : EnumMonotonicCorrect X} `{_ : Dec_Eq X} (n : X) :
  CorrectSizedST (fun m => eq n m) (@enumSizeST _ _ (EnumSizedSuchThateq n)).
Proof. derive_enumST_Correct. Qed.
*)

(* We can also derive an enumerator that given a regular expression, enumerates
   all strings that match the regular expression.

 *)

Derive EnumSizedSuchThat for (fun l => exp_match l e). 
(* EnumSizedSuchThatexp_match is defined. *)

About EnumSizedSuchThatexp_match.

(*
EnumSizedSuchThatexp_match :
  forall {T : Type},
    Dec_Eq T ->
    Enum T ->
    forall e : reg_exp T, EnumSizedSuchThat (list T) (fun l : list T => l =~ e)
*)

(* For all types [T] that have decidable equality and are enumerable, and for all
   input regular expressions [e], we derive an enumerator.

    The [EnumSizedSuchThat] typeclass
    ---------------------------------

    [EnumSizedSuchThat] is similar to [EnumSized] but it is also parameterized by
    a predicate that the enumerated elements must satisfy.

    Class EnumSizedSuchThat (A : Type) (P : A -> Prop) := { enumSizeST : nat -> E (option A) }

    The type of the enumerator is [nat -> E (option A)]. Again, it has a [nat] parameter
    that bounds the recursion depth. The type of the enumerator is [E (option A)].
    The semantics of [None] in the output of enumerator is that enumeration is not
    complete and there might be more elements that satisfy the predicate.

*)


(* As with the simple enumeration, before deriving correctness, we need to derive
   monotonicity. *)

#[local] Instance EnumSizedSuchThatexp_match_SizedMonotonic {T} `{_ : Dec_Eq T} `{_ : EnumMonotonic T} e:
  SizedMonotonicOptFP (@enumSizeST _ _ (EnumSizedSuchThatexp_match e)).
Proof. derive_enumST_SizedMonotonicFP. Qed.

#[local] Instance EnumSizedSuchThatexp_match_SizeMonotonic {T} `{_ : Dec_Eq T} `{_ : EnumMonotonic T} e :
  forall s, SizeMonotonicOptFP (@enumSizeST _ _ (EnumSizedSuchThatexp_match e) s).
Proof. derive_enumST_SizeMonotonicFP. Qed.

(* Correctness
   -----------

  Correctness of enumerators states that all elements that are
  generated satisfy the predicate (soundness) and that all elements
  that satisfy the predicate can be enumerated. Using the set of outcomes
  semantics, this can be states as:

   { x | exists s s',  Some x \in semEnumSize (enumSizeST s) s' } <--> { x | P x }

   This is captured by the [CorrectSizedST] typeclass. We derive this instance automatically.

*)

(* TODO: Fix:
#[local] Instance EnumSizedSuchThatexp_match_Correct {T} `{_ : Dec_Eq T} `{_ : EnumMonotonicCorrect T} e :
  CorrectSizedST (fun l => exp_match l e)  (@enumSizeST _ _ (EnumSizedSuchThatexp_match e)).
Proof. derive_enumST_Correct. Qed.
*)

(* Νote that we can also generate a enumerator for all the regular
   expressions that can match a string (i.e., the input is the string
   and the output is the regular expression)
 *)

Derive EnumSizedSuchThat for (fun e => exp_match l e).