Source file satml_frontend_hybrid.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
(******************************************************************************)
(*                                                                            *)
(*     Alt-Ergo: The SMT Solver For Software Verification                     *)
(*     Copyright (C) 2013-2018 --- OCamlPro SAS                               *)
(*                                                                            *)
(*     This file is distributed under the terms of the license indicated      *)
(*     in the file 'License.OCamlPro'. If 'License.OCamlPro' is not           *)
(*     present, please contact us to clarify licensing.                       *)
(*                                                                            *)
(******************************************************************************)


module Make (Th : Theory.S) = struct

  open Options
  open Format

  module E = Expr
  module ME = E.Map
  module SE = E.Set
  module Ex = Explanation
  module Atom = Satml_types.Atom
  module MA = Atom.Map
  module SAT = Satml.Make(Th)
  module PF = Satml_types.Proxy_formula

  type t =
    {sat : SAT.t;
     assumed : SE.t;
     proxies : Atom.atom ME.t;
     inv_proxies : E.t MA.t;
     hcons_env : Atom.hcons_env;
     decisions : (int * E.t) list;
     pending : (E.gformula * Ex.t) list list;
    }

  exception Bottom of Explanation.t * E.Set.t list * t

  let empty () =
    {sat = SAT.empty ();
     assumed = SE.empty;
     proxies = ME.empty;
     inv_proxies = MA.empty;
     hcons_env = Atom.empty_hcons_env ();
     decisions=[];
     pending=[]}

  let formula_of_atom env a =
    try let p = MA.find a env.inv_proxies in p
    with Not_found -> assert false

  let is_true env f =
    match PF.get_proxy_of f env.proxies with
    | Some p ->
      assert (not (Atom.is_true p) || (Atom.level p) >= 0);
      if (Atom.is_true p) then
        let l_ex =
          let r = Atom.Set.fold (fun a acc ->
              SE.add (formula_of_atom env a) acc)
              (SAT.reason_of_deduction p) SE.empty in
          lazy (Ex.make_deps r) in
        Some (l_ex,Atom.level p)
      else None
    | None -> assert false

  let forget_decision env f lvl =
    let l_ok, _ =
      List.partition (fun (dlvl, _) -> dlvl < lvl) env.decisions in
    let env = { env with decisions = l_ok } in
    let a = match PF.get_proxy_of f env.proxies with
      | Some p -> p
      | None -> (* TODO *)
        match PF.get_proxy_of (E.neg f) env.proxies with
        | Some p -> p
        | None -> assert false
    in
    if Atom.reason a == None && Atom.level a > 0 then
      (* right level of f in SAT because of other decisions that may be
         propagated*)
      SAT.cancel_until env.sat ((Atom.level a) - 1);
    env

  let reset_decisions env = SAT.cancel_until env.sat 0;
    {env with decisions = []}

  let get_decisions env = env.decisions

  let decide_aux env (dlvl,f) =
    begin
      match is_true env f, is_true env (E.neg f) with
      | None, None -> begin
          match PF.get_proxy_of f env.proxies  with
          | Some p -> SAT.decide env.sat p
          | None -> assert false
        end
      | Some _, Some _ -> assert false
      | Some _, None ->
        if verbose () && debug_sat () then
          fprintf fmt "!!! [dlvl=%d] %a becomes true before deciding@."
            dlvl E.print f;
      | None, Some (ex, _) ->
        if verbose () && debug_sat () then
          fprintf fmt "!!! [dlvl=%d] %a becomes false before deciding@."
            dlvl E.print f;(* Satml_types.Atom.pr_atom (fst f); *)
        let ex = Ex.union (Ex.singleton (Ex.Bj f)) (Lazy.force ex) in
        raise (Bottom (ex, [], env))
        (*SAT.print_env ();*)
    end

  let assume delay env l =
    let env = {env with pending = l :: env.pending} in
    if delay then env
    else
      let ll = List.rev env.pending in
      let env = {env with pending = []} in
      let env, pfl, cnf, new_vars =
        List.fold_left (fun acc l ->
            List.fold_left
              (fun ((env, pfl, cnf, vars) as acc) ({ E.ff = f; _ }, ex) ->
                 if SE.mem f env.assumed then acc
                 else
                 if Ex.has_no_bj ex then begin
                   let pf, (added_proxy, inv_proxies, new_vars, cnf) =
                     PF.mk_cnf env.hcons_env f
                       (env.proxies, env.inv_proxies, vars, cnf) in
                   {env with assumed = SE.add f env.assumed;
                             proxies = added_proxy; inv_proxies},
                   [pf] :: pfl, cnf, new_vars
                 end
                 else acc
              ) acc l
          )(env, [], [], []) ll
      in
      if pfl != [] then env
      else
        let () =
          try
            let _ =
              SAT.new_vars env.sat ~nbv:(Atom.nb_made_vars env.hcons_env)
                new_vars [] []
            in
            SAT.assume_simple env.sat cnf;
            SAT.assume_simple env.sat pfl;

            List.iter (decide_aux env)
              (List.rev env.decisions);
          with
          | Satml.Sat ->
            assert false
          (* Uncomment if Sat.solve is called *)
          (* SAT.cancel_until env.sat 0;
           * env *)

          | Satml.Unsat _ ->
            assert (Options.tableaux_cdcl () && SAT.decision_level env.sat = 0);
            raise (Bottom (Ex.empty, [], env))

          | Satml.Last_UIP_reason r ->
            let r =
              Atom.Set.fold (fun a acc ->
                  SE.add (formula_of_atom env a) acc) r SE.empty
            in
            raise (Bottom (Ex.make_deps r, [], env))
        in
        env

  let decide env f dlvl =
    try
      decide_aux env (dlvl, f);
      assert (dlvl = List.length env.decisions + 1);
      {env with decisions = (dlvl, f) :: env.decisions}
    with
    | Satml.Unsat _ ->
      assert (Options.tableaux_cdcl () && SAT.decision_level env.sat = 0);
      raise (Bottom (Ex.empty, [], env))

    | Satml.Last_UIP_reason r ->
      let r =
        Atom.Set.fold (fun a acc ->
            SE.add (formula_of_atom env a) acc) r SE.empty
      in
      raise (Bottom (Ex.make_deps r, [], env))
end