Skip to content

Commit 5f625a1

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 59d9b7e commit 5f625a1

66 files changed

Lines changed: 11202 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
@@ -369,7 +369,7 @@ let mk_limit_opt age_bound fm_cross_limit timelimit_interpretation
369369

370370
let mk_output_opt
371371
interpretation use_underscore objectives_in_interpretation unsat_core
372-
output_format model_type () () () ()
372+
output_format model_type () () () () ()
373373
=
374374
set_infer_output_format (Option.is_none output_format);
375375
let output_format = match output_format with
@@ -865,7 +865,7 @@ let parse_output_opt =
865865

866866
(* Use the --interpretation and --produce-models (which is equivalent to
867867
--interpretation last) to determine the interpretation value. *)
868-
let interpretation, dump_models, dump_models_on, frontend =
868+
let interpretation, dump_models, dump_models_on, verify_models, frontend =
869869
let interpretation =
870870
let doc = Format.sprintf
871871
"Best effort support for counter-example generation. \
@@ -890,13 +890,21 @@ let parse_output_opt =
890890
Arg.(value & opt interpretation INone &
891891
info ["interpretation"] ~docv ~docs:s_models ~doc)
892892
in
893+
893894
let produce_models =
894895
let doc =
895896
"Enable model generation (equivalent to --interpretation last)."
896897
in
897898
Arg.(value & flag & info ["produce-models"] ~doc ~docs:s_models)
898899
in
899900

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

901909
let frontend =
902910
let doc =
@@ -949,6 +957,7 @@ let parse_output_opt =
949957
),
950958
dump_models,
951959
dump_models_on,
960+
verify_models,
952961
frontend
953962
in
954963

@@ -1115,6 +1124,10 @@ let parse_output_opt =
11151124
Term.(const Output.set_dump_models $ dump_models_on)
11161125
in
11171126

1127+
let set_verify_models =
1128+
Term.(const set_verify_models $ verify_models)
1129+
in
1130+
11181131
let set_frontend =
11191132
Term.(const set_frontend $ frontend)
11201133
in
@@ -1123,7 +1136,7 @@ let parse_output_opt =
11231136
interpretation $ use_underscore $
11241137
objectives_in_interpretation $ unsat_core $
11251138
output_format $ model_type $
1126-
set_dump_models $ set_dump_models_on $
1139+
set_dump_models $ set_dump_models_on $ set_verify_models $
11271140
set_sat_options $ set_frontend
11281141
))
11291142

src/bin/common/solving_loop.ml

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

48-
type 'a sat_module = (module Sat_solver_sig.S with type t = 'a)
49-
50-
type any_sat_module = (module Sat_solver_sig.S)
51-
5248
(* Internal state while iterating over input statements *)
5349
type 'a state = {
5450
env : 'a;
5551
solver_ctx: solver_ctx;
56-
sat_solver : any_sat_module;
52+
sat_solver : Sat_solver_util.any_sat_module;
5753
}
5854

5955
let empty_solver_ctx = {
@@ -104,6 +100,24 @@ let cmd_on_modes st modes cmd =
104100
Errors.forbidden_command curr_mode cmd
105101
end
106102

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

109123
(** Adds the named terms of the statement [stmt] to the map accumulator [acc] *)
@@ -122,7 +136,7 @@ let add_if_named
122136
acc
123137

124138
(* We currently use the full state of the solver as model. *)
125-
type model = Model : 'a sat_module * 'a -> model
139+
type model = Model : 'a Sat_solver_util.sat_module * 'a -> model
126140

127141
type solve_res =
128142
| Sat of model
@@ -184,6 +198,12 @@ let main () =
184198
Fmt.pf (Options.Output.get_fmt_models ()) "%a@."
185199
FE.print_model partial_model
186200
end;
201+
if Options.get_verify_models () then begin
202+
let get_value =
203+
Sat_solver_util.get_value (module SAT) partial_model
204+
in
205+
verify_model ~get_value ()
206+
end;
187207
Sat mdl
188208
end
189209
| `Unknown ->
@@ -197,6 +217,12 @@ let main () =
197217
Fmt.pf (Options.Output.get_fmt_models ()) "%a@."
198218
FE.print_model partial_model
199219
end;
220+
if Options.get_verify_models () then begin
221+
let get_value =
222+
Sat_solver_util.get_value (module SAT) partial_model
223+
in
224+
verify_model ~get_value ()
225+
end;
200226
Unknown (Some mdl)
201227
end
202228
| `Unsat -> Unsat
@@ -345,7 +371,7 @@ let main () =
345371
in
346372

347373
let partial_model_key: model option State.key =
348-
State.create_key ~pipe:"" "sat_state"
374+
State.create_key ~pipe:"" "partial_model"
349375
in
350376

351377
let named_terms: DStd.Expr.term Util.MS.t State.key =
@@ -688,6 +714,12 @@ let main () =
688714
| None -> print_wrn_opt ~name st_loc "integer" value; st
689715
| Some i -> set_steps_bound i st
690716
end
717+
| ":verify-models", Symbol { name = Simple "true"; _ } ->
718+
Options.set_verify_models true;
719+
st
720+
| ":verify-models", Symbol { name = Simple "false"; _ } ->
721+
Options.set_verify_models false;
722+
st
691723
| _ ->
692724
unsupported_opt name; st
693725
in
@@ -803,45 +835,45 @@ let main () =
803835
unsupported_opt name
804836
in
805837

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

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

829-
let handle_get_assignment ~get_value st =
830-
let assignments =
831-
Util.MS.fold
832-
(fun name term acc ->
833-
if DStd.Expr.Ty.equal term.DStd.Expr.term_ty DStd.Expr.Ty.bool then
834-
(name, evaluate_term get_value name term) :: acc
835-
else
836-
acc
837-
)
838-
(State.get named_terms st)
839-
[]
862+
let handle_get_assignment ~get_assignment st =
863+
let names, l =
864+
Util.MS.fold (fun name term (names, acc) ->
865+
assert (DStd.Expr.Ty.equal term.DStd.Expr.term_ty DStd.Expr.Ty.bool);
866+
name :: names,
867+
Expr.mk_term (Sy.name name) []
868+
(D_cnf.dty_to_ty term.DStd.Expr.term_ty) :: acc
869+
) (State.get named_terms st) ([], [])
840870
in
871+
let values = get_assignment l in
841872
Printer.print_std
842873
"(@[<v 0>%a@])@,"
843-
print_terms_assignments
844-
assignments
874+
Fmt.(iter ~sep:cut Lists.iter_pair
875+
((pair ~sep:sp string Sat_solver_util.pp_lbool) |> parens))
876+
(names, values)
845877
in
846878

847879
let handle_stmt :
@@ -934,11 +966,12 @@ let main () =
934966
| {contents = `Get_model; _ } ->
935967
cmd_on_modes st [Sat] "get-model";
936968
if Options.get_interpretation () then
937-
let () = match State.get partial_model_key st with
938-
| Some (Model ((module SAT), env)) ->
969+
let () =
970+
match State.get partial_model_key st with
971+
| Some Model ((module SAT), partial_model) ->
939972
let module FE = Frontend.Make (SAT) in
940973
Fmt.pf (Options.Output.get_fmt_regular ()) "%a@."
941-
FE.print_model env
974+
FE.print_model partial_model
942975
| None ->
943976
(* TODO: add the location of the statement. *)
944977
recoverable_error "No model produced."
@@ -982,9 +1015,10 @@ let main () =
9821015
match State.get partial_model_key st with
9831016
| Some Model ((module SAT), partial_model) ->
9841017
if DO.ProduceAssignment.get st then
985-
handle_get_assignment
986-
~get_value:(SAT.get_value partial_model)
987-
st
1018+
let get_assignment =
1019+
Sat_solver_util.get_assignment (module SAT) partial_model
1020+
in
1021+
handle_get_assignment ~get_assignment st
9881022
else
9891023
recoverable_error
9901024
"Produce assignments disabled; \
@@ -997,6 +1031,24 @@ let main () =
9971031
st
9981032
end
9991033

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

src/lib/dune

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
(pps
3131
ppx_blob
3232
ppx_deriving.ord
33+
ppx_deriving.eq
3334
ppx_deriving.show
3435
ppx_deriving.enum
3536
ppx_deriving.fold
@@ -51,7 +52,8 @@
5152
Fun_sat Fun_sat_frontend Inequalities Bitv_rel Th_util Adt Adt_rel
5253
Instances IntervalCalculus Intervals Ite_rel Matching Matching_types
5354
Polynome Records Records_rel Satml_frontend_hybrid Satml_frontend Satml
54-
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
5557
; structures
5658
Commands Errors Explanation Fpa_rounding
5759
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
@@ -35,7 +35,6 @@ module SE = E.Set
3535

3636
module C = Commands
3737
module Sy = Symbols
38-
module SM = Sy.Map
3938

4039
module DE = DStd.Expr
4140
module DT = DE.Ty
@@ -641,13 +640,13 @@ let mk_ty_decl (ty_c: DE.ty_cst) =
641640
in the cache as well as the symbol associated to the term. *)
642641
let mk_term_decl ({ id_ty; path; tags; _ } as tcst: DE.term_cst) =
643642
let name = get_basename path in
644-
let sy =
643+
let name =
645644
begin match DStd.Tag.get tags DE.Tags.ac with
646-
| Some () -> Sy.name ~kind:Sy.Ac name
647-
| _ -> Sy.name name
645+
| Some () -> Sy.Name.mk ~kind:Sy.Ac name
646+
| _ -> Sy.Name.mk name
648647
end
649648
in
650-
Cache.store_sy tcst sy;
649+
Cache.store_sy tcst (Sy.Name name);
651650
(* Adding polymorphic types to the cache. *)
652651
Cache.store_ty_vars id_ty;
653652
let arg_tys, ret_ty =
@@ -656,7 +655,7 @@ let mk_term_decl ({ id_ty; path; tags; _ } as tcst: DE.term_cst) =
656655
List.map dty_to_ty arg_tys, dty_to_ty ret_ty
657656
| _ -> [], dty_to_ty id_ty
658657
in
659-
(Hstring.make name, arg_tys, ret_ty)
658+
(name, arg_tys, ret_ty)
660659

661660
(** Handles the definitions of a list of mutually recursive types.
662661
- 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
@@ -51,6 +51,14 @@ val make_form :
5151
decl_kind:Expr.decl_kind ->
5252
Expr.t
5353

54+
val mk_expr :
55+
?loc:Loc.t ->
56+
?name_base:string ->
57+
?toplevel:bool ->
58+
decl_kind:Expr.decl_kind ->
59+
Dolmen.Std.Expr.term ->
60+
Expr.t
61+
5462
val make :
5563
D_loop.DStd.Loc.file ->
5664
Commands.sat_tdecl list ->

0 commit comments

Comments
 (0)