-
Notifications
You must be signed in to change notification settings - Fork 33
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
[WIP] Removing exceptions from the Sat solver API #795
Conversation
src/lib/reasoners/satml_frontend.ml
Outdated
(* Leaving this exception in case we actually raise Sat one day. | ||
Remember to update `safe_call` by catching this exception if you do. *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
safe_call
already catches this exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I removed the Sat exception, wrote this comment, put it back and forgot about it.
src/lib/reasoners/satml_frontend.ml
Outdated
| None -> raise Exit | ||
| None -> assert false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is within a try .. with Exit -> None
, can you explain this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A mistake, I'll just revert the commit
src/lib/reasoners/sat_solver_sig.mli
Outdated
(** [unsat env f size] checks the unsatisfiability of [f] in | ||
[env]. Raises I_dont_know when the proof tree's height reaches | ||
[size]. Raises Sat if [f] is satisfiable in [env] *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs to be updated
src/lib/frontend/frontend.ml
Outdated
match | ||
SAT.assume env | ||
{E.ff=f; | ||
origin_name = ""; | ||
gdist = -1; | ||
hdist = -1; | ||
trigger_depth = max_int; | ||
nb_reductions = 0; | ||
age=0; | ||
lem=None; | ||
mf=false; | ||
gf=false; | ||
from_terms = []; | ||
theory_elim = true; | ||
} Ex.empty | ||
with | ||
| Done e -> e | ||
| Sat t -> raise (SAT t) | ||
| Unsat e -> raise (UNSAT e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should use process_sat_res
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a bit unsure about this change. While the goal to have a cleaner API interface is good, this means that we currently:
- Raise an exception in the SAT solver,
- Convert the exception to
res
in the SAT solver, - Immediately convert the
res
back into an exception in the frontend
This feels like artificial complexity to me, especially considering that this is all internal to the library (the interface for library users is process_decl
, which already captures and deals with the underlying exceptions).
src/lib/reasoners/sat_solver_sig.mli
Outdated
type res = | ||
| Done of t | ||
| Sat of t | ||
| Unsat of Explanation.t | ||
| I_dont_know of t |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest using
type outcome = Sat of t | Unsat of Explanation.t | I_dont_know of t
type 'a res = ('a, outcome) Result.t
and use t res
as return type for assume
/ assume_th_elt
/ pred_def
, and outcome
for unsat
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your version adds an extra pointer in the Error
case. Besides, the Error
constructor of Result.t
would be badly named. It is not an error, it is a standard response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the extra pointer here matters, I was trying to optimise for readability —sure Error
is badly named, but there is a fundamental difference here between Done
and Sat / Unsat / I_dont_know
(as exemplified by the fact that unsat
can only return Sat / Unsat / I_dont_know
, never Done
).
But actually, as mentioned in another comment, that Sat of t | Unsat of Explanation.t | I_dont_know of t
should be enough, bcause it makes sense for assume
/ assume_th_elt
/ pred_def
to return I_dont_know
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we could indeed replace Done
by I_dont_know
as they seem to play the same role (and I kind of did not like the Done
name)
b3bb728
to
7e16334
Compare
I think it would make more sense to change the interface of the This would get rid of process_decl entirely (although we could re-implement process_decl on top of that new API to avoid changing the current solving loop), and bring the public-facing API more in-line with what is expected of other solvers, which was the goal I understood at yesterday's meeting. Edited to add: Actually, it would make sense to do this in the SAT solvers themselves rather than in the Frontend. The main point is that |
IMO, the way the frontend handles the response of the sat solver should be independent from the sat solver functionning. The next step of this work would be indeed to remove the process_decl function and replacing it with specific functions, possibly without using exceptions at all. |
I agree; however, we also said yesterday that it made sense for the SAT solver to use exceptions. I think the main question is: do we want to keep the
I originally replied under the assumption we were going for the first option, but I think the second option may actually be closer to what @Halbaroth had in mind (and thinking about it more, it is probably the best option long-term, because it avoid one additional layer of wrapping). |
I'm not sure to see the difference between the two versions. The
|
I had the second option in mind. I agree the PR should remove the exceptions in the frontend but |
The preprocessing performed by the frontend is very light and could be instead provided as a (shared) helper module for both solvers.
More precise types are not always desirable. As a user, if I have 5 formulas to add to my context, most of the time I don't really care if the context becomes inconsistent after I added 2 of them or 4 of them, I just care about the conjunction of all of them. If we expose the API you suggest, I have to do something like: let extract = function
| Unknown t -> t
| Unsat expl -> raise (Unsat expl)
in
let t = extract @@ assume t fmla1 in
let t = extract @@ assume t fmla2 in
let t = extract @@ assume t fmla3 in
let t = extract @@ assume t fmla4 in
let t = extract @@ assume t fmla5 in
match unsat t with
| Sat _ | Unknown _ -> (* Do something appropriate *) ()
| Unsat expl -> raise (Unsat expl) But if we hide from the user the fact that we can actually detect inconsistencies early (this will cause an "early" Unsat exception, before the user ever calls the (* `assume` returns [ `I_dont_know of env | `Unsat of Explanation.t ] as an abstract type `t` *)
let t = assume t fmla1 in
let t = assume t fmla2 in
let t = assume t fmla3 in (* Maybe we detect an inconsistency between fmla1 and fmla3 here *)
let t = assume t fmla4 in
let t = assume t fmla5 in
unsat t (* Returns [ `Sat of env | `I_dont_know of env | `Unsat of Explanation.t ] *) which is much more pleasant. And we can expose a function: val is_inconsistent : t -> option Explanation.t to be able to check whether an inconsistency was detected. |
I understand your point. Note that you can already create a syntactic sugar for your example : let extract_and x f = match x with
| Unknown t -> f t
| Unsat expl -> raise (Unsat expl)
let (let*) = extract_and
let* t = assume t fmla1 in
let* t = assume t fmla2 in
let* t = assume t fmla3 in
let* t = assume t fmla4 in
let* t = assume t fmla5 in
unsat t |
True, but then I am also either forced to use exceptions (as I did in my example and you do here, but is what we were trying to avoid), or to live in a monad. |
Exceptions have now disapeared from both modules; the only remaining exceptions are used for debugging. I almost "purified" the whole API, but there still are few options dependencies left. |
I had in mind that these changes would be mostly in the SAT solver modules rather than the frontend, sorry if I was not clear. But I suppose doing this in the frontend could work as well — at first glance this looks mostly OK, although I have not checked the new logic in detail. I'll let @Halbaroth make a proper review since he mentioned in #789 (comment) that this part of the frontend changes a lot in OptimAE and will know better if this risk causing issues. |
I will review it tomorrow :) |
As this PR will not be compatible with the optimae changes, I created another branch from scratch, based on optimae, that reworks the frontend to make it more user-friendly. Should I close this PR and open another one, or recycle this one? |
This PR removes the Sat, Unsat and I_dont_know exceptions raised by the library. It now returns an ADT which is either Done, Sat, Unsat or I_dont_know.
Alt-ergo has the exact same behavior than before, but it is not a bit cleaner.
This PR also removes a
raise Exit
lost in the middle of the library.