Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model generation support for SatML #829

Merged
merged 28 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
914d283
Theory: harden 'compute_concrete_model' function
iguerNL May 1, 2021
982bb92
Push the test 'get_case_split_policy() == origin' into Theory.do_case…
iguerNL May 1, 2021
de26aae
CS: functions return all the env instead of just 'gamma_finite'.
iguerNL May 1, 2021
fed58a7
Fun_sat: refactor the way models are handled/printed
iguerNL May 1, 2021
7447ca3
satML: add missing case-split strategies
iguerNL May 1, 2021
0c37f66
satML: implement models generation.
iguerNL May 1, 2021
1ace3d9
Make model tests negative
Halbaroth Sep 15, 2023
3ac006b
Reindent
Halbaroth Sep 21, 2023
4b03759
Update documentation about `output_concrete_model`
Halbaroth Sep 22, 2023
0251733
Document the timeout_reason ADT
Halbaroth Sep 22, 2023
9496cf6
poetry
Halbaroth Sep 22, 2023
115a16a
Restore the comment about `reinit_cpt`
Halbaroth Sep 22, 2023
986321d
Fix spelling
Halbaroth Sep 22, 2023
b39ecb8
Remainder to the issue 834
Halbaroth Sep 22, 2023
9507d63
Simplify get-model processing
Halbaroth Sep 22, 2023
1ca2286
Remove SAT solver choices while model generating
Halbaroth Sep 21, 2023
3d75176
Fix model output
Halbaroth Sep 21, 2023
be505b4
All the model tests are positive
Halbaroth Sep 21, 2023
afb8e44
Remove deprecated cram tests
Halbaroth Sep 21, 2023
e0c9b34
Test models with all the SAT solvers
Halbaroth Sep 21, 2023
b45ee04
Restoring dump-models option
Halbaroth Sep 22, 2023
a155756
Use the right post-solve SAT environment for models
Halbaroth Sep 22, 2023
709dc42
Print dump models on the channel models
Halbaroth Sep 26, 2023
cd998e7
Prevent printing internal names in models
Halbaroth Sep 26, 2023
819935b
Promote tests
Halbaroth Sep 26, 2023
a6caf6a
Revert "Simplify get-model processing"
Halbaroth Sep 26, 2023
d4301df
Review changes
Halbaroth Sep 26, 2023
3299ff7
Test only models with FunSAT
Halbaroth Sep 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions docs/sphinx_docs/Usage/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ Model generation is disabled by default. There are two recommended ways to enabl
on demand using the statement `(get-model)`.

Alternatively, you can enable model generation using the statement
`(set-option :produce-models true)`. This currently requires using the options
`--sat-solver tableaux` and `--frontend dolmen`.
`(set-option :produce-models true)`. This currently requires using the option
`--frontend dolmen`.

#### Examples

Expand Down Expand Up @@ -142,7 +142,7 @@ Model generation is disabled by default. There are two recommended ways to enabl
(get-model)

```
and the command `alt-ergo --frontend dolmen --sat-solver tableaux INPUT.smt2` produces
and the command `alt-ergo --frontend dolmen INPUT.smt2` produces
the output model
```
unknown
Expand All @@ -154,10 +154,8 @@ Model generation is disabled by default. There are two recommended ways to enabl
```

```{admonition} Note
You need to select the Dolmen frontend and the SAT solver Tableaux as the
model generation is not supported yet by the other SAT solvers. The options
`--dump-models` and `--produce-models` select the right frontend and SAT solver
for you.
You need to select the Dolmen frontend. The options `--dump-models` and
`--produce-models` select the right frontend for you.
```

- As a more didactic example, let us imagine while checking the loop invariant
Expand Down
74 changes: 27 additions & 47 deletions src/bin/common/parse_command.ml
Original file line number Diff line number Diff line change
Expand Up @@ -943,58 +943,38 @@ let parse_output_opt =
frontend
in

(* Use the --sat-solver to determine the sat solver.

If an interpretation is provided, the solver is forced to be Tableaux,
because generation of models requires OptimAE for the other solvers.

See https://github.com/OCamlPro/alt-ergo/pull/553 *)
(* Use the --sat-solver to determine the sat solver. *)
let sat_solver =
let sat_solver_arg =
let sat_solver : _ Arg.conv =
let parse = function
| "CDCL" | "satML" ->
Ok Util.CDCL
| "CDCL-Tableaux" | "satML-Tableaux"
| "CDCL-tableaux" | "satML-tableaux" ->
Ok Util.CDCL_Tableaux
| "tableaux" | "Tableaux"
| "tableaux-like" | "Tableaux-like" ->
Ok Util.Tableaux
| "tableaux-cdcl" | "Tableaux-CDCL"
| "tableaux-CDCL" | "Tableaux-cdcl" ->
Ok Util.Tableaux_CDCL
| sat_solver ->
Error ("Args parsing error: unkown SAT solver " ^ sat_solver)
let sat_solver : _ Arg.conv =
let parse = function
| "CDCL" | "satML" ->
Ok Util.CDCL
| "CDCL-Tableaux" | "satML-Tableaux"
| "CDCL-tableaux" | "satML-tableaux" ->
Ok Util.CDCL_Tableaux
| "tableaux" | "Tableaux"
| "tableaux-like" | "Tableaux-like" ->
Ok Util.Tableaux
| "tableaux-cdcl" | "Tableaux-CDCL"
| "tableaux-CDCL" | "Tableaux-cdcl" ->
Ok Util.Tableaux_CDCL
| sat_solver ->
Error ("Args parsing error: unkown SAT solver " ^ sat_solver)

in
Arg.(conv' (parse, Util.pp_sat_solver))
in
let default, sum_up = "CDCL-Tableaux", "satML" in
let doc = Format.sprintf
"Choose the SAT solver to use. Default value is %s (i.e. %s\
solver). Possible options are %s."
default sum_up
(Arg.doc_alts ["CDCL"; "satML"; "CDCL-Tableaux";
"satML-Tableaux"; "Tableaux-CDCL"])
in
let docv = "SAT" in
Arg.(value & opt (some ~none:default sat_solver) None &
info ["sat-solver"] ~docv ~docs:s_sat ~doc)
Arg.(conv' (parse, Util.pp_sat_solver))
in

let mk_sat_solver sat_solver interpretation =
match interpretation, sat_solver with
| INone, None -> Ok Util.CDCL_Tableaux
| INone, Some sat_solver -> Ok sat_solver
| _, (None | Some Util.Tableaux) -> Ok Tableaux
| _, Some sat_solver ->
Fmt.error
"solver '%a' does not suppot model generation"
Util.pp_sat_solver sat_solver
let default, sum_up = "CDCL-Tableaux", "satML" in
let doc = Format.sprintf
"Choose the SAT solver to use. Default value is %s (i.e. %s\
solver). Possible options are %s."
default sum_up
(Arg.doc_alts ["CDCL"; "satML"; "CDCL-Tableaux";
"satML-Tableaux"; "Tableaux-CDCL"])
in
Term.term_result' @@
Term.(const mk_sat_solver $ sat_solver_arg $ interpretation)
let docv = "SAT" in
Arg.(value & opt sat_solver Util.CDCL_Tableaux &
info ["sat-solver"] ~docv ~docs:s_sat ~doc)
in

let cdcl_tableaux_inst =
Expand Down
22 changes: 12 additions & 10 deletions src/bin/common/solving_loop.ml
Original file line number Diff line number Diff line change
Expand Up @@ -358,15 +358,7 @@ let main () =
Options.Output.create_channel name
|> Options.Output.set_diagnostic
| ":produce-models", Symbol { name = Simple "true"; _ } ->
(* TODO: The generation of models is supported only with the SAT
solver Tableaux. Remove this line after merging the OptimAE
PR. See https://github.com/OCamlPro/alt-ergo/pull/553 *)
if Stdlib.(Options.get_sat_solver () = Tableaux) then
Options.set_interpretation ILast
else
Printer.print_smtlib_err
"Model generation requires the Tableaux solver \
(try --produce-models)";
Options.set_interpretation ILast
| ":produce-models", Symbol { name = Simple "false"; _ } ->
Options.set_interpretation INone
| ":produce-unsat-cores", Symbol { name = Simple "true"; _ } ->
Expand Down Expand Up @@ -489,7 +481,17 @@ let main () =
| {contents = `Get_model; _ } ->
if Options.get_interpretation () then
match State.get partial_model_key st with
| Some partial_model -> SAT.get_model partial_model; st
| Some partial_model ->
begin
match SAT.get_model partial_model with
| Some (lazy model) ->
Models.output_concrete_model
(Options.Output.get_fmt_regular ()) model;
st
| _ ->
(* TODO: is it reachable? *)
st
end
bclement-ocp marked this conversation as resolved.
Show resolved Hide resolved
| None ->
(* TODO: add the location of the statement. *)
Printer.print_smtlib_err "No model produced.";
Expand Down
46 changes: 38 additions & 8 deletions src/lib/frontend/frontend.ml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,30 @@ module Make(SAT : Sat_solver_sig.S) : S with type sat_env = SAT.t = struct
if Options.get_unsat_core () then Ex.singleton (Ex.RootDep {name;f;loc})
else Ex.empty

let timeout_reason_to_string = function
| None -> "(?)"
| Some SAT.NoTimeout -> "NoTimeout"
| Some SAT.Assume -> "Assume"
| Some SAT.ProofSearch -> "ProofSearch"
| Some SAT.ModelGen -> "ModelGen"

let print_model env timeout =
if Options.(get_interpretation () && get_dump_models ()) then begin
let s = timeout_reason_to_string timeout in
match SAT.get_model env with
| None ->
Printer.print_fmt (Options.Output.get_fmt_diagnostic ())
"@[<v 0>It seems that no model has been computed so \
far. You may need to change your model generation strategy \
or to increase your timeouts. Returned timeout reason = %s@]" s

| Some (lazy model) ->
Printer.print_fmt
(Options.Output.get_fmt_diagnostic ())
"@[<v 0>; Returned timeout reason = %s@]" s;
Models.output_concrete_model (Options.Output.get_fmt_models ()) model
end

let process_decl print_status used_context consistent_dep_stack
((env, consistent, dep) as acc) d =
try
Expand Down Expand Up @@ -246,7 +270,7 @@ module Make(SAT : Sat_solver_sig.S) : S with type sat_env = SAT.t = struct
(* This case should mainly occur when a query has a non-unsat result,
so we want to print the status in this case. *)
print_status (Sat (d,t)) (Steps.get_steps ());
(*if get_model () then SAT.print_model ~header:true (get_fmt_mdl ()) t;*)
print_model env (Some SAT.NoTimeout);
bclement-ocp marked this conversation as resolved.
Show resolved Hide resolved
env, `Sat t, dep
| SAT.Unsat dep' ->
(* This case should mainly occur when a new assumption results in an unsat
Expand All @@ -255,18 +279,24 @@ module Make(SAT : Sat_solver_sig.S) : S with type sat_env = SAT.t = struct
let dep = Ex.union dep dep' in
if get_debug_unsat_core () then check_produced_unsat_core dep;
(* print_status (Inconsistent d) (Steps.get_steps ()); *)
env, `Unsat, dep
| SAT.I_dont_know t ->
(* In this case, it's not clear whether we want to print the status.
Instead, it'd be better to accumulate in `consistent` a 3-case adt
and not a simple bool. *)
print_status (Unknown (d, t)) (Steps.get_steps ());
(*if get_model () then SAT.print_model ~header:true (get_fmt_mdl ()) t;*)
env , `Unsat, dep
| SAT.I_dont_know {env = t; timeout} ->
(* TODO: always print Unknown for why3 ? *)
let status =
if timeout != NoTimeout then (Timeout (Some d))
else (Unknown (d, t))
in
print_status status (Steps.get_steps ());
print_model t (Some timeout);
(* TODO: Is it an appropriate behaviour? *)
(* if timeout != NoTimeout then raise Util.Timeout; *)
env, `Unknown t, dep

| Util.Timeout as e ->
(* In this case, we obviously want to print the status,
since we exit right after *)
print_status (Timeout (Some d)) (Steps.get_steps ());
print_model env None;
bclement-ocp marked this conversation as resolved.
Show resolved Hide resolved
raise e

let print_status status steps =
Expand Down
42 changes: 25 additions & 17 deletions src/lib/frontend/models.ml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ module MX = Shostak.MXH

let constraints = ref MS.empty

type t = {
propositional : Expr.Set.t;
constants : ModelMap.t;
functions : ModelMap.t;
arrays : ModelMap.t;
}

module Pp_smtlib_term = struct

let to_string_type t =
Expand Down Expand Up @@ -483,29 +490,27 @@ let rec pp_value ppk ppf = function
let pp_constant ppf (_sy, t) =
Fmt.pf ppf "%a" SmtlibCounterExample.pp_abstract_value_of_type t

let output_concrete_model fmt props ~functions ~constants ~arrays =
let output_concrete_model fmt m =
SmtlibCounterExample.reset_counter ();
if ModelMap.(is_suspicious functions || is_suspicious constants
|| is_suspicious arrays) then
if ModelMap.(is_suspicious m.functions || is_suspicious m.constants
|| is_suspicious m.arrays) then
Format.fprintf fmt "; This model is a best-effort. It includes symbols
for which model generation is known to be incomplete. @.";

Format.fprintf fmt "@[<v 2>(";
if Options.get_model_type_constraints () then
begin
Why3CounterExample.output_constraints fmt props;
Format.fprintf fmt "@ ; values"
end;
if Options.get_model_type_constraints () then begin
Why3CounterExample.output_constraints fmt m.propositional
end;

let values = Hashtbl.create 17 in
(* Add the constants *)
ModelMap.iter (fun (f, xs_ty, _) st ->
assert (Lists.is_empty xs_ty);

ModelMap.V.iter (fun (keys, (value_r, value_s)) ->
assert (Lists.is_empty keys);
Hashtbl.add values f (value (value_r, value_s)))
st) constants;
Hashtbl.add values f (value (value_r, value_s))
) st
) m.constants;

(* Add the arrays values, when applicable *)
ModelMap.iter (fun (f, xs_ty, ty) st ->
Expand All @@ -515,9 +520,8 @@ let output_concrete_model fmt props ~functions ~constants ~arrays =
in
Hashtbl.replace values f @@
ModelMap.V.fold (fun (keys, rs) acc ->
Store (acc, value (snd (List.hd keys)), value rs))
st root)
arrays;
Store (acc, value (snd (List.hd keys)), value rs)) st root
) m.arrays;

let pp_value =
pp_value (fun ppf (sy, _) ->
Expand All @@ -527,12 +531,16 @@ let output_concrete_model fmt props ~functions ~constants ~arrays =
let pp_x ppf xs = pp_value ppf (value xs) in

(* Functions *)
let records = SmtlibCounterExample.output_functions_counterexample
pp_x fmt MS.empty functions
let records =
SmtlibCounterExample.output_functions_counterexample
pp_x fmt MS.empty m.functions
in

(* Constants *)
SmtlibCounterExample.output_constants_counterexample
pp_x fmt records constants;
pp_x fmt records m.constants;

(* Arrays *)
(* SmtlibCounterExample.output_arrays_counterexample fmt m.arrays; *)

Printer.print_fmt fmt "@]@,)";
23 changes: 10 additions & 13 deletions src/lib/frontend/models.mli
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,13 @@

(** {1 Models module} *)

(** Print the given counterexample on the given formatter with the
corresponding format set with Options.get_output_format.
- functions: the functions of the model;
- constants: the variables of the model;
- arrays: (experimental) the arrays of the model.
*)
val output_concrete_model :
Format.formatter ->
Expr.Set.t ->
functions:ModelMap.t ->
constants:ModelMap.t ->
arrays:ModelMap.t ->
unit
type t = {
propositional : Expr.Set.t;
constants : ModelMap.t;
functions : ModelMap.t;
arrays : ModelMap.t;
}

val output_concrete_model : t Fmt.t
(** [output_concrete_model ppf mdl] prints the model [mdl] on
the formatter [ppf]. *)
11 changes: 5 additions & 6 deletions src/lib/reasoners/ccx.ml
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ module type S = sig
Matching_types.info Expr.Map.t * Expr.t list Expr.Map.t Symbols.Map.t ->
t -> (Expr.t -> Expr.t -> bool) -> t * Sig_rel.instances

val output_concrete_model :
Format.formatter ->
val extract_concrete_model :
prop_model:Expr.Set.t ->
t ->
unit
Models.t Lazy.t option

end

module Main : S = struct
Expand Down Expand Up @@ -738,7 +738,6 @@ module Main : S = struct
in
Uf.term_repr env.uf t

let output_concrete_model fmt ~prop_model env =
Uf.output_concrete_model fmt ~prop_model env.uf

let extract_concrete_model ~prop_model env =
Uf.extract_concrete_model ~prop_model env.uf
end
5 changes: 2 additions & 3 deletions src/lib/reasoners/ccx.mli
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,10 @@ module type S = sig
Matching_types.info Expr.Map.t * Expr.t list Expr.Map.t Symbols.Map.t ->
t -> (Expr.t -> Expr.t -> bool) -> t * Sig_rel.instances

val output_concrete_model :
Format.formatter ->
val extract_concrete_model :
prop_model:Expr.Set.t ->
t ->
unit
Models.t Lazy.t option
end

module Main : S
Loading
Loading