Skip to content

Commit 1253be1

Browse files
committed
Keep partial models in with a separate key
We keep the last consistent SAT environment with the Dolmen key `partial_model` in `Solving_loop`. This solution isn't sufficient to implement correctly `get-value`. Indeed, we need to ensure `get-model` statements located after some `get-value` statements will still print the same model. To obtain this behaviour, now we keep the last model and the last unknown reason after a `check-sat` in a separate key. Support `get-value` in Solving_loop Add support for the SMT-LIB statement `get-value` in `Solving_loop`. With the current implementation of the solver, it's not easy to keep a consistent environment of the SAT solver without reassuming all the formulas as we do in `Solving_loop`. Instead, we save the last consistent environment obtained after a `check-sat` statement in the Dolmen state and we have to keep the last model and unknown reason with different keys. Indeed, after using `get-value`, the `get-model` statement has to output the same model. Create a new type name in `Symbols` Refactoring `get-value` support This commit adds a better support of `get-value` and `get-assignment`. These features are implemented using a new wrapper functor on the top of the SAT solvers of Alt-Ergo. - Values for bool expressions are computed by the SAT solver; - Before launching the solver to compute some model terms, we check if there aren't already available in the boolean or first-order models; - Thanks to the wrapper functor, the feature works well with the legacy solver FunSAT; We also test our generated models by non-regression tests using the `get-value`. Add option `--verify-models` This option will be useful to check our models with the new `get-value` command. Adding `--verify-models` turns on the model generation (as we did with the `--produce-models` option). If our best-effort model checker doesn't find a contradiction, we don't print anything. fix documentation Add the location for get-value À la C Use first-class module instead of functor Add Expr.Core.of_bool Reinit GetValue cpt spelling Documentation of the module Graph Derive comparison function of name_space Restore the documentation of `name` Remove the joke :( Remove the joke :( New exception for wrong model We raise a new exception `Wrong_model` in `get_value` in order to clarify the API. Rebase artefacts Removing get-value statements to check models Add a new SMT option for the CLI option `--verify-models` Use `Stdcompat.Either` for OCaml 4.08 Reset decisions only for get-value statements We need to reset the decision level of SatML after calling the `unsat` function as the decision level of this solver isn't necessary zero. This hotfix is only necessary for SatML. Add a new function `reset_decisions` in the SAT API. Saved the boolean model before resetting decisions The call `SAT.reset_decisions` may erase part of the boolean models in `get_value`. For sake of efficiency, we save the boolean model before resetting decisions. Add tests Add a link to issue 1063 Address partially review
1 parent cd0520a commit 1253be1

66 files changed

Lines changed: 11240 additions & 334 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
External plugins must now be registered through the dune-site plugin
1111
mechanism in the `(alt-ergo plugins)` site to be picked up by Alt-Ergo.
1212

13+
### New features
14+
* support for the SMT-LIB statement `get-value`
15+
1316
## v2.5.2
1417

1518
### Bug fixes

src/bin/common/parse_command.ml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ let mk_limit_opt age_bound fm_cross_limit timelimit_interpretation
367367

368368
let mk_output_opt
369369
interpretation use_underscore objectives_in_interpretation unsat_core
370-
output_format model_type () () () ()
370+
output_format model_type () () () () ()
371371
=
372372
set_infer_output_format (Option.is_none output_format);
373373
let output_format = match output_format with
@@ -863,7 +863,7 @@ let parse_output_opt =
863863

864864
(* Use the --interpretation and --produce-models (which is equivalent to
865865
--interpretation last) to determine the interpretation value. *)
866-
let interpretation, dump_models, dump_models_on, frontend =
866+
let interpretation, dump_models, dump_models_on, verify_models, frontend =
867867
let interpretation =
868868
let doc = Format.sprintf
869869
"Best effort support for counter-example generation. \
@@ -888,13 +888,21 @@ let parse_output_opt =
888888
Arg.(value & opt interpretation INone &
889889
info ["interpretation"] ~docv ~docs:s_models ~doc)
890890
in
891+
891892
let produce_models =
892893
let doc =
893894
"Enable model generation (equivalent to --interpretation last)."
894895
in
895896
Arg.(value & flag & info ["produce-models"] ~doc ~docs:s_models)
896897
in
897898

899+
let verify_models =
900+
let doc =
901+
"Verify generated models."
902+
in
903+
Arg.(value & flag & info ["verify-models"] ~doc ~docs:s_models)
904+
in
905+
898906

899907
let frontend =
900908
let doc =
@@ -947,6 +955,7 @@ let parse_output_opt =
947955
),
948956
dump_models,
949957
dump_models_on,
958+
verify_models,
950959
frontend
951960
in
952961

@@ -1113,6 +1122,10 @@ let parse_output_opt =
11131122
Term.(const Output.set_dump_models $ dump_models_on)
11141123
in
11151124

1125+
let set_verify_models =
1126+
Term.(const set_verify_models $ verify_models)
1127+
in
1128+
11161129
let set_frontend =
11171130
Term.(const set_frontend $ frontend)
11181131
in
@@ -1121,7 +1134,7 @@ let parse_output_opt =
11211134
interpretation $ use_underscore $
11221135
objectives_in_interpretation $ unsat_core $
11231136
output_format $ model_type $
1124-
set_dump_models $ set_dump_models_on $
1137+
set_dump_models $ set_dump_models_on $ set_verify_models $
11251138
set_sat_options $ set_frontend
11261139
))
11271140

src/bin/common/solving_loop.ml

Lines changed: 97 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,11 @@ let is_solver_ctx_empty = function
4242
{ ctx = []; local = []; global = [] } -> true
4343
| _ -> false
4444

45-
type 'a sat_module = (module Sat_solver_sig.S with type t = 'a)
46-
47-
type any_sat_module = (module Sat_solver_sig.S)
48-
4945
(* Internal state while iterating over input statements *)
5046
type 'a state = {
5147
env : 'a;
5248
solver_ctx: solver_ctx;
53-
sat_solver : any_sat_module;
49+
sat_solver : Sat_solver_util.any_sat_module;
5450
}
5551

5652
let empty_solver_ctx = {
@@ -101,6 +97,24 @@ let cmd_on_modes st modes cmd =
10197
Errors.forbidden_command curr_mode cmd
10298
end
10399

100+
let verify_model ~get_value () =
101+
match get_value [Expr.vrai] with
102+
| Some [e] when Expr.equal e Expr.vrai -> ()
103+
104+
| Some [_]
105+
| exception Sat_solver_util.Wrong_model _
106+
| exception Sat_solver_util.No_model ->
107+
recoverable_error "The model is wrong"
108+
109+
| None ->
110+
(* The model generation is not enabled. *)
111+
()
112+
113+
| Some _ ->
114+
(* The length of the output list is the same as the length of the
115+
input list. *)
116+
assert false
117+
104118
(* Dolmen util *)
105119

106120
(** Adds the named terms of the statement [stmt] to the map accumulator [acc] *)
@@ -119,7 +133,7 @@ let add_if_named
119133
acc
120134

121135
(* We currently use the full state of the solver as model. *)
122-
type model = Model : 'a sat_module * 'a -> model
136+
type model = Model : 'a Sat_solver_util.sat_module * 'a -> model
123137

124138
type solve_res =
125139
| Sat of model
@@ -181,6 +195,12 @@ let main () =
181195
Fmt.pf (Options.Output.get_fmt_models ()) "%a@."
182196
FE.print_model partial_model
183197
end;
198+
if Options.get_verify_models () then begin
199+
let get_value =
200+
Sat_solver_util.get_value (module SAT) partial_model
201+
in
202+
verify_model ~get_value ()
203+
end;
184204
Sat mdl
185205
end
186206
| `Unknown ->
@@ -194,6 +214,12 @@ let main () =
194214
Fmt.pf (Options.Output.get_fmt_models ()) "%a@."
195215
FE.print_model partial_model
196216
end;
217+
if Options.get_verify_models () then begin
218+
let get_value =
219+
Sat_solver_util.get_value (module SAT) partial_model
220+
in
221+
verify_model ~get_value ()
222+
end;
197223
Unknown (Some mdl)
198224
end
199225
| `Unsat -> Unsat
@@ -342,7 +368,7 @@ let main () =
342368
in
343369

344370
let partial_model_key: model option State.key =
345-
State.create_key ~pipe:"" "sat_state"
371+
State.create_key ~pipe:"" "partial_model"
346372
in
347373

348374
let named_terms: DStd.Expr.term Util.MS.t State.key =
@@ -685,6 +711,12 @@ let main () =
685711
| None -> print_wrn_opt ~name st_loc "integer" value; st
686712
| Some i -> set_steps_bound i st
687713
end
714+
| ":verify-models", Symbol { name = Simple "true"; _ } ->
715+
Options.set_verify_models true;
716+
st
717+
| ":verify-models", Symbol { name = Simple "false"; _ } ->
718+
Options.set_verify_models false;
719+
st
688720
| _ ->
689721
unsupported_opt name; st
690722
in
@@ -800,45 +832,45 @@ let main () =
800832
unsupported_opt name
801833
in
802834

803-
(* Fetches the term value in the current model. *)
804-
let evaluate_term get_value name term =
805-
(* There are two ways to evaluate a term:
806-
- if its name is registered in the environment, get its value;
807-
- if not, check if the formula is in the environment.
808-
*)
809-
let simple_form =
810-
Expr.mk_term
811-
(Sy.name name)
812-
[]
813-
(D_cnf.dty_to_ty term.DStd.Expr.term_ty)
814-
in
815-
match get_value simple_form with
816-
| Some v -> Fmt.to_to_string Expr.print v
817-
| None -> "unknown"
835+
let dl_to_ael dloc_file (compact_loc: DStd.Loc.t) =
836+
DStd.Loc.(lexing_positions (loc dloc_file compact_loc))
818837
in
819838

820-
let print_terms_assignments =
821-
Fmt.list
822-
~sep:Fmt.cut
823-
(fun fmt (name, v) -> Fmt.pf fmt "(%s %s)" name v)
839+
let handle_get_value loc ~get_value l =
840+
let l =
841+
List.map (D_cnf.mk_expr ~loc ~toplevel:false
842+
~decl_kind:Daxiom) l
843+
in
844+
match get_value l with
845+
| Some values ->
846+
Printer.print_std
847+
"(@[<v 0>%a@])@,"
848+
Fmt.(iter ~sep:cut Lists.iter_pair
849+
((pair ~sep:sp Expr.pp_smtlib Expr.pp_smtlib) |> parens))
850+
(l, values)
851+
| None ->
852+
recoverable_error "No model produced, cannot execute get-value."
853+
| exception Sat_solver_util.Wrong_model _ ->
854+
recoverable_error "The model is wrong, cannot execute get-value."
855+
| exception Sat_solver_util.No_model ->
856+
recoverable_error "No model produced but it should, cannot execute get-value."
824857
in
825858

826-
let handle_get_assignment ~get_value st =
827-
let assignments =
828-
Util.MS.fold
829-
(fun name term acc ->
830-
if DStd.Expr.Ty.equal term.DStd.Expr.term_ty DStd.Expr.Ty.bool then
831-
(name, evaluate_term get_value name term) :: acc
832-
else
833-
acc
834-
)
835-
(State.get named_terms st)
836-
[]
859+
let handle_get_assignment ~get_assignment st =
860+
let names, l =
861+
Util.MS.fold (fun name term (names, acc) ->
862+
assert (DStd.Expr.Ty.equal term.DStd.Expr.term_ty DStd.Expr.Ty.bool);
863+
name :: names,
864+
Expr.mk_term (Sy.name name) []
865+
(D_cnf.dty_to_ty term.DStd.Expr.term_ty) :: acc
866+
) (State.get named_terms st) ([], [])
837867
in
868+
let values = get_assignment l in
838869
Printer.print_std
839870
"(@[<v 0>%a@])@,"
840-
print_terms_assignments
841-
assignments
871+
Fmt.(iter ~sep:cut Lists.iter_pair
872+
((pair ~sep:sp string Sat_solver_util.pp_lbool) |> parens))
873+
(names, values)
842874
in
843875

844876
let handle_stmt :
@@ -931,11 +963,12 @@ let main () =
931963
| {contents = `Get_model; _ } ->
932964
cmd_on_modes st [Sat] "get-model";
933965
if Options.get_interpretation () then
934-
let () = match State.get partial_model_key st with
935-
| Some (Model ((module SAT), env)) ->
966+
let () =
967+
match State.get partial_model_key st with
968+
| Some Model ((module SAT), partial_model) ->
936969
let module FE = Frontend.Make (SAT) in
937970
Fmt.pf (Options.Output.get_fmt_regular ()) "%a@."
938-
FE.print_model env
971+
FE.print_model partial_model
939972
| None ->
940973
(* TODO: add the location of the statement. *)
941974
recoverable_error "No model produced."
@@ -979,9 +1012,10 @@ let main () =
9791012
match State.get partial_model_key st with
9801013
| Some Model ((module SAT), partial_model) ->
9811014
if DO.ProduceAssignment.get st then
982-
handle_get_assignment
983-
~get_value:(SAT.get_value partial_model)
984-
st
1015+
let get_assignment =
1016+
Sat_solver_util.get_assignment (module SAT) partial_model
1017+
in
1018+
handle_get_assignment ~get_assignment st
9851019
else
9861020
recoverable_error
9871021
"Produce assignments disabled; \
@@ -994,6 +1028,24 @@ let main () =
9941028
st
9951029
end
9961030

1031+
| {contents = `Get_value l; loc; _} ->
1032+
begin
1033+
cmd_on_modes st [Sat] "get-value";
1034+
match State.get partial_model_key st with
1035+
| Some Model ((module SAT), partial_model) ->
1036+
let file = (State.get State.logic_file st).loc in
1037+
let loc = dl_to_ael file loc in
1038+
let get_value =
1039+
Sat_solver_util.get_value (module SAT) partial_model
1040+
in
1041+
handle_get_value loc ~get_value l;
1042+
st
1043+
| None ->
1044+
(* TODO: add the location of the statement. *)
1045+
recoverable_error "No model produced, cannot execute get-value.";
1046+
st
1047+
end
1048+
9971049
| {contents = `Other (custom, args); loc; _} ->
9981050
handle_custom_statement loc custom args st
9991051

src/lib/dune

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
Fun_sat Fun_sat_frontend Inequalities Bitv_rel Th_util Adt Adt_rel
5353
Instances IntervalCalculus Intervals Ite_rel Matching Matching_types
5454
Polynome Records Records_rel Satml_frontend_hybrid Satml_frontend Satml
55-
Sat_solver Sat_solver_sig Sig Sig_rel Theory Uf Use Rel_utils Bitlist
55+
Sat_solver Sat_solver_sig Sig Sat_solver_util Sig_rel Theory Uf Use
56+
Rel_utils Bitlist
5657
; structures
5758
Commands Errors Explanation Fpa_rounding
5859
Parsed Profiling Satml_types Symbols

src/lib/frontend/d_cnf.ml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ module SE = E.Set
3232

3333
module C = Commands
3434
module Sy = Symbols
35-
module SM = Sy.Map
3635

3736
module DE = DStd.Expr
3837
module DT = DE.Ty
@@ -627,13 +626,13 @@ let mk_ty_decl (ty_c: DE.ty_cst) =
627626
in the cache as well as the symbol associated to the term. *)
628627
let mk_term_decl ({ id_ty; path; tags; _ } as tcst: DE.term_cst) =
629628
let name = get_basename path in
630-
let sy =
629+
let name =
631630
begin match DStd.Tag.get tags DE.Tags.ac with
632-
| Some () -> Sy.name ~kind:Sy.Ac name
633-
| _ -> Sy.name name
631+
| Some () -> Sy.Name.mk ~kind:Sy.Ac name
632+
| _ -> Sy.Name.mk name
634633
end
635634
in
636-
Cache.store_sy tcst sy;
635+
Cache.store_sy tcst (Sy.Name name);
637636
(* Adding polymorphic types to the cache. *)
638637
Cache.store_ty_vars id_ty;
639638
let arg_tys, ret_ty =
@@ -642,7 +641,7 @@ let mk_term_decl ({ id_ty; path; tags; _ } as tcst: DE.term_cst) =
642641
List.map dty_to_ty arg_tys, dty_to_ty ret_ty
643642
| _ -> [], dty_to_ty id_ty
644643
in
645-
(Hstring.make name, arg_tys, ret_ty)
644+
(name, arg_tys, ret_ty)
646645

647646
(** Handles the definitions of a list of mutually recursive types.
648647
- If one of the types is an ADT, the ADTs that have only one case are

src/lib/frontend/d_cnf.mli

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ val make_form :
4848
decl_kind:Expr.decl_kind ->
4949
Expr.t
5050

51+
val mk_expr :
52+
?loc:Loc.t ->
53+
?name_base:string ->
54+
?toplevel:bool ->
55+
decl_kind:Expr.decl_kind ->
56+
Dolmen.Std.Expr.term ->
57+
Expr.t
58+
5159
val make :
5260
D_loop.DStd.Loc.file ->
5361
Commands.sat_tdecl list ->

0 commit comments

Comments
 (0)