-
Notifications
You must be signed in to change notification settings - Fork 0
/
counterexample.ml
311 lines (263 loc) · 11.1 KB
/
counterexample.ml
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
(*
* Copyright © 2009-12 The Regents of the University of California. All rights reserved.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
* FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION
* TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
*)
(***************************************************************)
(* Counterexample Generation (cf. Lahiri-Vanegue, VMCAI 2011) **)
(***************************************************************)
module F = Format
module BS = BNstats
module C = FixConstraint
module IM = Misc.IntMap
module IS = Misc.IntSet
module A = Ast
module Sy = A.Symbol
module SM = Sy.SMap
module SS = Sy.SSet
module P = A.Predicate
module Su = A.Subst
module Q = Qualifier
module TP = TpNull.Prover
open Misc.Ops
let mydebug = false
(***************************************************************************)
(************** Type Aliases ***********************************************)
(***************************************************************************)
type step = int
type kvar = Sy.t
type fact = Abs of kvar * Qualifier.t | Conc of C.id
type cause = step * C.id * ((Sy.t * fact) list)
(* [k |-> [(i0, [q_i0_1...]),...]]
* where q_i0_1... are killed at timestep i0 for kvar k sorted by step *)
type lifespan = (step * Q.t list) list SM.t
(* [id |-> i0,...]
* where the constraint id is selected at steps i0... by solver *)
type ctrace = step list IM.t
(* [((x1,k1,q1), c1);...;((xn,kn,qn),cn)]
* where each k_i+1, q_i+1, c_i+1 is the "cause" for why k_i, q_i is killed *)
type cex = (Sy.t * fact * C.id) list
let print_fact ppf = function
| Abs (k, q) -> F.fprintf ppf "(%a/%a)" Sy.print k Q.print_args q
| Conc i -> F.fprintf ppf "(id %d)" i
let print_step ppf (x, f, cid) =
F.fprintf ppf "%a: %a @@ %d" Sy.print x print_fact f cid
let print_cex =
Misc.pprint_many_box true "" "---> " "" print_step
let compare_fact f1 f2 =
compare (Misc.fsprintf print_fact f1) (Misc.fsprintf print_fact f2)
module FactMap = Misc.EMap (struct
type t = fact
let compare = compare_fact
let print = print_fact
end)
type t = { tpc : TP.t
; n : int (* number of solver iters *)
; s : FixConstraint.soln
; cm : FixConstraint.t IM.t
; ctrace : ctrace
; lifespan : lifespan (* builds soln at n *)
(* ; sfm : fact list IM.t (* step |-> facts killed at a step *)
*)
; fsm : step FactMap.t (* fact |-> step at which killed *)
; scm : int IM.t (* step |-> constr at step *)
}
let scm_of_ctrace ctrace =
ctrace
|> IM.to_list
|> Misc.flap (fun (cid, is) -> List.map (fun i -> (i, cid)) is)
|> Misc.fsort fst
|> IM.of_list
let fsm_of_lifespan lifetime =
SM.fold begin fun k sqs fsm ->
List.fold_left begin fun fsm (step, qs) ->
List.fold_left begin fun fsm q ->
FactMap.add (Abs (k, q)) step fsm
end fsm qs
end fsm sqs
end lifetime FactMap.empty
(***************************************************************************)
(************** Helpers to Reconstitute Solutions and Candidates ***********)
(***************************************************************************)
let constrOfId me cid =
IM.safeFind cid me.cm "Cex.constrOfId"
let solutionAt me n k =
SM.safeFind k me.lifespan "solutionAt: bad kvar"
|> List.filter (fun (m,_) -> n <= m)
|> Misc.flap snd
|> Misc.map Q.pred_of_t
|> (++) (me.s k)
(************************************************************************)
(************************************************************************)
(************************************************************************)
let isUnsatAt me c n =
let s = solutionAt me n in
let rhsp = A.pAnd <| C.preds_of_reft s (C.rhs_of_t c) in
let query = A.pAnd <| (A.pNot rhsp) :: (C.preds_of_lhs s c) in
not <| TP.is_contra me.tpc (C.senv_of_t c) query
let prevStep_conc me c : int =
let _ = asserts (C.is_conc_rhs c) in
let no = Misc.find_first_true (isUnsatAt me c) 0 me.n in
Misc.maybe_apply (+) no (-1)
let prevStep_abs me cid n : int =
let rec go n = function
| m1 :: _ when m1 = n -> (-1)
| m1 :: (m2 :: _ as rest) -> if n = m2 then m1 else go n rest
| _ -> assertf "prevStep with bad ctrace"
in go n (IM.safeFind cid me.ctrace "prevStep: bad cid")
let prevStep me c n =
if C.is_conc_rhs c then
prevStep_conc me c
else
prevStep_abs me (C.id_of_t c) n
let killstep_of_fact me f =
FactMap.safeFind f me.fsm "Cex.killstep_of_fact"
let delta me c n k : fact list =
let _n = prevStep me c n in
SM.safeFind k me.lifespan "delta: bad kvar"
|> List.filter (fun (m,_) -> _n <= m && m < n)
|> Misc.flap snd
|> Misc.map (fun q -> Abs (k, q))
(************************************************************************)
(************************************************************************)
(************************************************************************)
let killerCands me c n : (int * (((Sy.t * fact) * A.pred)) list) list =
foreach (C.kbindings_of_lhs c) begin fun (x, (vv, t, ras)) ->
foreach ras begin function C.Kvar (su, k) ->
foreach (delta me c n k) begin function (Abs (_, q) as f) ->
let su' = Su.extend su (vv, A.eVar x) in
let p' = A.substs_pred (Q.pred_of_t q) su' in
(x, f), p'
end
end |> Misc.flatten
end |> Misc.flatten
|> Misc.kgroupby (fst <+> snd <+> killstep_of_fact me)
(************************************************************************)
(************************************************************************)
(************************************************************************)
let print_fact_causes n ppf (f, xfs) =
F.fprintf ppf "fact %a killed at %d by: %a \n"
print_fact f
n
(Misc.pprint_many_brackets false (Misc.pprint_tuple Sy.print print_fact)) xfs
let is_bot_killer = function
| (f, p) when P.is_contra p -> Some f
| _ -> None
(********************************************************************)
(*********************** API ****************************************)
(********************************************************************)
(* API *)
let create s cs ctrace lifespan tpc =
let scm = scm_of_ctrace ctrace in
{ tpc = tpc
; s = s
; cm = cs |>: Misc.pad_fst C.id_of_t |> IM.of_list
; n = 1 + Misc.list_max 0 (IM.domain scm)
; ctrace = IM.map Misc.sort_and_compact ctrace
; lifespan = lifespan
; fsm = fsm_of_lifespan lifespan
; scm = scm
}
(*****************************************************************)
(**************** Lazy Explanations ******************************)
(*****************************************************************)
let killedPred me c f =
match f, C.rhs_of_t c with
| Abs (k, q), (_,_, [C.Kvar (su, k')])
when k = k'
-> A.substs_pred (Q.pred_of_t q) su
| Conc cid, (_,_,[C.Conc p])
when C.id_of_t c = cid
-> p
| _ -> failwith "Counterexample.killed"
let getKillStep me c bgp iks =
let iks = Misc.fsort fst iks in
let ps = iks |>: (snd <+> List.map snd <+> A.pAnd) in
match TP.unsat_suffix me.tpc (C.senv_of_t c) bgp ps with
| Some j when 0 <= j && j < List.length iks
-> List.nth iks j
| io -> let _ = F.printf
"getKillStep failure: (cid = %d) (|iks| = %d) (io = %a)\n bgp = %a\nps = %a\n"
(C.id_of_t c)
(List.length iks)
(Misc.pprint_maybe Misc.pprint_int) io
P.print bgp
(Misc.pprint_many_brackets true P.print) ps
in assertf "getKillStep"
let killinfo me = function
| Conc cid -> me.n, cid
| f -> let n = killstep_of_fact me f in
(n, IM.safeFind n me.scm "Cex.killinfo")
(* ORIGINAL: simply use unsat core.
let getKillers_cands me c bgp cands =
match cands, Misc.exists_maybe is_bot_killer cands with
| [], _ ->
None
| _, Some g ->
Some g
| _, _ ->
TP.unsat_core me.tpc (C.senv_of_t c) bgp cands
|> Misc.do_catch "ERROR: empty unsat core" List.hd
|> some
let getKillers_fact (me: t) (f: fact) =
let n, cid = killinfo me f in
let c = IM.safeFind cid me.cm "Cex.getKillers 3" in
match killerCands me c n with [] -> (cid, None) | iks ->
let bgps = C.preds_of_lhs (solutionAt me n) c
|> (++) [A.pNot (killedPred me c f)] in
let (j, cands) = getKillStep me c (A.pAnd bgps) iks in
let bgps' = iks
|> List.filter (fun (i,_) -> j < i)
|> Misc.flap (snd <+> List.map snd) in
(cid, getKillers_cands me c (A.pAnd (bgps ++ bgps')) cands)
let maxCubeSize = 1
let underApproxCubes me (p:pred) (q:pred) (rs: ('a * pred) list) : 'a option =
Misc.exists_maybe begin fun (f, fp) ->
if SAT (p /\ fp) && UNSAT (p /\ fp /\ q)
then Some f
else None
end rs
*)
let getKillers_cands me c p q rs =
let env = C.senv_of_t c in
let contra = fun p -> TP.is_contra me.tpc env p in
Misc.exists_maybe begin fun (f, fp) ->
if contra (A.pAnd [p; fp; q]) && not (contra (A.pAnd [p; fp]))
then Some f
else None
end rs
let getKillers_fact (me: t) (f: fact) =
let n, cid = killinfo me f in
let c = IM.safeFind cid me.cm "Cex.getKillers 3" in
match killerCands me c n with [] -> (cid, None) | iks ->
let bgps = C.preds_of_lhs (solutionAt me n) c in
(* |> (++) [A.pNot (killedPred me c f)] *)
let killedp = A.pNot (killedPred me c f) in
let (j, cands) = getKillStep me c (A.pAnd bgps) iks in
let bgps' = iks
|> List.filter (fun (i,_) -> j < i)
|> Misc.flap (snd <+> List.map snd) in
(cid, getKillers_cands me c (A.pAnd (bgps ++ bgps')) killedp cands)
let rec explain me acc f =
match getKillers_fact me f with
| cid, Some (x', f') -> explain me ((x', f', cid) :: acc) f'
| _ , None -> acc
let explain me c =
explain me [] (Conc (C.id_of_t c))