forked from Eelis/hybrid
-
Notifications
You must be signed in to change notification settings - Fork 0
/
digraph.v
358 lines (315 loc) · 11.9 KB
/
digraph.v
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
Require Import List util list_util Program Relations.
Require EquivDec.
Set Implicit Arguments.
Record DiGraph: Type := Build
{ Vertex: Type
; Vertex_eq_dec: EquivDec.EqDec Vertex eq
; vertices: ExhaustiveList Vertex
; edges: Vertex -> list Vertex
; edges_NoDup: forall v, NoDup (edges v)
}.
Existing Instance Vertex_eq_dec.
Existing Instance vertices.
Hint Resolve edges_NoDup.
Hint Immediate edges_NoDup.
Implicit Arguments edges [d].
Definition flat_edge_list (g: DiGraph): list (Vertex g * Vertex g) :=
flat_map (fun v => map (pair v) (edges v)) (vertices g).
Section reachability.
Variable g: DiGraph.
Definition edge: relation (Vertex g) := fun v w => In w (edges v).
Let ved := Vertex_eq_dec g.
Definition Edge := (Vertex g * Vertex g)%type.
Definition Edge_eq_dec: forall (e e': Edge), decision (e=e')
:= pair_eq_dec ved ved.
Variable start: list (Vertex g).
Hypothesis NoDup_start: NoDup start.
Definition reachable v: Prop := exists s, In s start /\ trans_refl_closure.R edge s v.
Program Fixpoint unreachables_worker (unvisited: list (Vertex g))
(tovisit: { l | NoDup l /\ incl l unvisited
/\ (forall v, ~ In v unvisited -> forall w, In w unvisited -> edge v w -> In w l)
/\ (forall v, ~ In v unvisited -> reachable v)
/\ (forall v, In v l -> reachable v)}) {measure (length unvisited)}:
{ l | incl l (subtr ved unvisited tovisit)
/\ (forall v, ~ In v l -> forall w, edge v w -> ~ In w l)
/\ (forall v, ~ In v l -> reachable v) } :=
match tovisit with
| nil => unvisited
| h :: t => @unreachables_worker (remove ved h unvisited)
(t ++ intersection ved unvisited (subtr ved (edges h) (h :: t))) _
end.
Next Obligation. Proof with auto.
(* the result in the nil case meets the result spec *)
split. apply incl_refl.
split...
repeat intro.
destruct (i0 v H w H1 H0).
Qed.
Next Obligation. Proof with simpl; auto.
(* the invariant is maintained in the recursive call *)
clear unreachables_worker.
inversion_clear n.
repeat split; intros.
(* we don't introduce duplicates *)
apply NoDup_app...
repeat intro.
destruct (intersection_In ved x _ _ H2).
destruct (snd (In_remove ved (subtr ved (edges h) t) x h) H4).
destruct (In_subtr _ _ _ _ H5)...
(* the new tovisit is still included in the new unvisited *)
apply incl_app.
repeat intro.
apply (fst (In_remove ved unvisited a h)).
split... intro. subst...
repeat intro.
destruct (intersection_In ved a _ _ H1).
apply (fst (In_remove ved unvisited a h)).
split...
destruct (In_remove ved (subtr ved (edges h) t) a h).
firstorder.
(* the new tovisit is still the border between visiteds and unvisiteds *)
destruct (snd (In_remove ved _ _ _) H2).
rewrite remove_eq_filter in H1.
destruct (not_In_filter' ved _ _ unvisited H1).
destruct (i0 v H6 w H4 H3)...
firstorder.
destruct (ved h v); [idtac | discriminate].
unfold Equivalence.equiv in e. subst v.
destruct (In_dec ved w t)...
apply in_or_app...
(* things not in the new unvisited are still reachable *)
rewrite remove_eq_filter in H1.
destruct (not_In_filter' ved v _ unvisited H1)...
destruct (ved h v); [idtac | discriminate].
firstorder.
(* things in the new tovisit are still reachable *)
destruct (in_app_or _ _ _ H1). apply r0...
destruct (intersection_In _ _ _ _ H2).
destruct (snd (In_remove ved (subtr ved (edges h) t) v h) H4).
destruct (r0 h (in_eq _ _)). destruct H7.
exists x. split...
apply trans_refl_closure.step with h...
unfold edge.
destruct (In_subtr _ _ _ _ H5)...
Qed.
Next Obligation. Proof with simpl; auto.
(* the length decreases in the recursive call *)
apply remove_length_lt, i...
Qed.
Next Obligation. Proof with auto.
(* the result in the recursive case meets the result spec *)
destruct_call unreachables_worker.
simpl proj1_sig in *.
clear unreachables_worker.
destruct a. destruct H0.
split...
(*subst tovisit.*)
simpl.
repeat intro.
specialize (H a H2).
destruct (In_subtr _ _ _ _ H).
clear H.
destruct (snd (In_remove ved unvisited a h) H3).
apply (In_remove ved (subtr ved unvisited t) a h)...
split... apply subtr_In...
Qed.
Program Definition unreachables:
{ l: list (Vertex g) | forall w, ~ In w l <-> reachable w }
:= @unreachables_worker (vertices g) start.
Next Obligation. Proof with auto.
(* the invariant holds at the start *)
split...
split. repeat intro...
split. intros. elimtype False...
split. intros. elimtype False...
intros. exists v. split...
Qed.
Obligation Tactic := idtac.
Next Obligation. Proof with auto.
(* worker's result implies our desired result *)
destruct (unreachables_worker (exist (fun l => NoDup l /\ incl l (vertices g) /\
(forall v, ~ In v (vertices g) -> forall w0, In w0 (vertices g) -> edge v w0 -> In w0 l) /\
(forall v, ~ In v (vertices g) -> reachable v) /\
(forall v, In v l -> reachable v)) start unreachables_obligation_1)).
simpl in *.
destruct a. destruct H0.
intro. split... repeat intro.
destruct H2. destruct H2.
destruct (trans_refl_closure.flip_inv (fun j => In j x) (fun j => In_dec ved j x) H4)...
intro.
apply (snd (In_subtr ved (vertices g) start _ (H _ H5)) H2).
destruct H5. destruct H5. destruct H6.
apply (H0 _ H5 _ H7)...
Qed.
Obligation Tactic := program_simpl.
(* let's do it again, this time using James' method *)
Inductive result_rel:
forall (unvisited tovisit result: list (Vertex g)), Prop :=
| result_nil unvisited: result_rel unvisited nil unvisited
| result_cons unvisited h t r:
result_rel (remove ved h unvisited)
(t ++ intersection ved unvisited (subtr ved (edges h) (h :: t))) r ->
result_rel unvisited (h :: t) r.
Hint Constructors result_rel.
Hint Unfold reachable.
Lemma half_correct (unvisited tovisit r: list (Vertex g)):
result_rel unvisited tovisit r ->
(forall v, ~ In v unvisited -> reachable v)
-> (forall v, In v tovisit -> reachable v)
-> (forall v, ~ In v r -> reachable v).
(* todo: is this soundness or completeness? i can't tell because
of this complement stuff.. *)
Proof with simpl; auto.
intros P.
induction P...
repeat intro.
apply IHP; intros...
(* things not in the new unvisited are still reachable *)
rewrite remove_eq_filter in H2.
destruct (not_In_filter' ved _ _ unvisited H2)...
destruct (ved h v0); [idtac | discriminate].
clear H3. subst...
(* things in the new tovisit are still reachable *)
destruct (in_app_or _ _ _ H2). apply H0...
destruct (intersection_In _ _ _ _ H3). clear H3.
destruct (snd (In_remove ved (subtr ved (edges h) t) v0 h) H5).
destruct (H0 h (in_eq _ _)). destruct H7.
destruct (In_subtr _ _ _ _ H5).
eauto.
Qed.
Lemma other_half (unvisited tovisit r: list (Vertex g)):
result_rel unvisited tovisit r ->
(forall v, ~ In v unvisited -> forall w,
In w unvisited -> edge v w -> In w tovisit) ->
(forall v, ~ In v r -> forall w, edge v w -> ~ In w r)
/\ incl r (subtr ved unvisited tovisit).
Proof with simpl; auto.
intros P.
induction P; intros.
split.
repeat intro.
destruct (H _ H0 _ H2 H1).
unfold incl...
destruct IHP.
intros.
(* the new tovisit is still the border between visiteds and unvisiteds *)
destruct (snd (In_remove ved _ _ _) H1).
rewrite remove_eq_filter in H0.
destruct (not_In_filter' ved _ _ unvisited H0).
destruct (H _ H5 _ H3 H2)...
firstorder.
destruct (ved h v); [idtac | discriminate].
clear H5. unfold Equivalence.equiv in e. subst v.
destruct (In_dec ved w t)...
apply in_or_app...
split...
repeat intro.
destruct (In_subtr _ _ _ _ (H1 _ H2)).
simpl.
apply (fst (In_remove ved (subtr ved unvisited t) a h)).
destruct (snd (In_remove ved unvisited a h) H3).
split...
apply subtr_In...
Qed.
(*
Program Fixpoint result_rel_exists (unvisited: list (Vertex g))
(tovisit: { l | NoDup l /\ incl l unvisited })
{measure (length unvisited)}:
{ r | result_rel unvisited tovisit r } :=
match tovisit with
| nil => unvisited
| h :: t => result_rel_exists (remove ved h unvisited)
(t ++ intersection ved unvisited (subtr ved (edges h) (h :: t)))
end.
Next Obligation. (* length decrease *)
intros.
apply remove_length_lt. destruct H.
apply H0. auto.
Qed.
Next Obligation. Proof with auto. (* NoDup/incl for recursive call. *)
destruct H. inversion_clear H.
split.
(* we don't introduce duplicates *)
apply NoDup_app...
repeat intro.
destruct (intersection_In ved x _ _ H3).
destruct (snd (In_remove ved (subtr ved (edges h) t) x h) H5).
destruct (In_subtr _ _ _ _ H6)...
(* the new tovisit is still included in the new unvisited *)
apply incl_app.
repeat intro.
apply (fst (In_remove ved unvisited a h)).
split... intro. subst...
repeat intro.
destruct (intersection_In ved a _ _ H).
apply (fst (In_remove ved unvisited a h)).
split...
destruct (In_remove ved (subtr ved (edges h) t) a h).
firstorder.
Qed.
Next Obligation. Proof with auto. (* recursive result meets the spec *)
(* ugly trivial thing, really just a silly Program artifact. *)
destruct (result_rel_exists
(exist
(fun unvisited' : list (Vertex g) =>
length unvisited' < length unvisited) (remove ved h unvisited)
(result_rel_exists_obligation_2 result_rel_exists
(exist (fun l : list (Vertex g) => NoDup l /\ incl l unvisited)
tovisit H) Heq_tovisit))
(exist
(fun l : list (Vertex g) =>
NoDup l /\ incl l (remove ved h unvisited))
(t ++
intersection ved unvisited (remove ved h (subtr ved (edges h) t)))
(result_rel_exists_obligation_3 result_rel_exists
(exist (fun l : list (Vertex g) => NoDup l /\ incl l unvisited)
tovisit H) Heq_tovisit))).
simpl in *. subst...
Qed.
Program Definition unreachables':
{ l: list (Vertex g) | forall w, ~ In w l <-> reachable w }
:= @result_rel_exists (vertices g) start.
Next Obligation. Proof with auto. split... repeat intro... Qed.
Next Obligation. Proof with auto.
destruct (result_rel_exists
(exist (fun l : list (Vertex g) => NoDup l /\ incl l (vertices g))
start unreachables'_obligation_1)).
simpl in *.
split.
apply (half_correct r); intros...
elimtype False...
exists v. split...
destruct (other_half r).
intros. elimtype False...
repeat intro.
destruct H1. destruct H1.
destruct (trans_refl_closure.flip_inv (fun j => In j x) (fun j => In_dec ved j x) H3)...
intro.
apply (snd (In_subtr ved (vertices g) start _ (H0 x0 H4)) H1).
destruct H4. destruct H4. destruct H5.
apply (H _ H4 _ H6)...
Qed.
(* This concludes James' approach. *)
*)
Program Definition reachables:
{ l: list (Vertex g) | forall w, In w l <-> reachable w }
:= subtr ved (vertices g) unreachables.
Obligation Tactic := idtac.
Next Obligation. Proof with auto.
destruct unreachables.
intros.
split; intro.
destruct (In_subtr _ _ _ _ H).
simpl in H1.
apply (i w)...
apply subtr_In...
apply (i w)...
Qed.
Definition reachable_dec (v: Vertex g): decision (reachable v) :=
let (x, i) := unreachables in
match In_dec ved v x with
| left i0 => right (fun H => snd (i v) H i0)
| right n => left (fst (i v) n)
end.
End reachability.
Implicit Arguments reachable [[g]].