-
Notifications
You must be signed in to change notification settings - Fork 82
Invariants from other isolates are only assumed before actions, not after #92
Comments
Well, Ivy doesn't do that because you could make a circular argument to prove false like this: isolate safety1 = { isolate safety2 = { Notice here that each isolate uses the invariant of the other. To avoid such circularities, Ivy uses an approach called "circular assume/guarantee". This means that the proof of assertion A at time t can assume assertion B at all times less than t, but not at t, which would allow us to use A to prove B and B to prove A. Put another way, all of the assertions are proved by mutual induction over time. You might say "why not disallow the above case?". In a concurrent system, you may have modules that interact over time, such that each has to assume the specification of the other. Think of a landlord and a tenant: the tenant promises to pay the rent as long as the landlord keeps the heat on, and the landlord promises to keep the heat on as long as the tenant pays the rent. This contract isn't circular because each party gets to assume the other upholds its guarantee in the strict past. Thus, neither can be the first to violate the contract. The normal use case of one isolate exporting an invariant to another is when isolate A calls isolate B. In this case A gets to assume the invariant of B on return from B (but not on return from A!). |
Thanks, that makes sense. I saw the example about circular assume/guarantee in the docs but didn't connect the dots. I wonder if there's an alternate way to accomplish what I was trying to do. I have identified some invariant P, which is inductive, but doesn't yet imply the safety property I care about. But as I added more clauses to the invariant, checking slowed down. So my hope was to first isolate P, check it independently, then export it to another isolate where I add in yet more. With the option of temporarily turning on "trusted" to avoid re-checking the first isolate as I was developing. (But perhaps the slow down is a signal that I need to go back to the drawing board on the structure of my proof.) |
Makes sense. In other words, you want to do an incremental inductive proof. There is a not-so-pretty way to do it. You can wrap isolate A inside isolate B and just have each action of A call the corresponding action of B. The invariant of B can then be used by A in the way that you want. Soon, I hope, there will be a better way to do this using isolate-level tactics. |
I've also experienced such slow downs, and I think it would be good to have support for more fine-grained control over which invariants go into the verification conditions. Even if the proof is not incremental (i.e., there is a circular dependence between many invariants), it may still be useful to allow the user to select which invariants to assume in the prestate when proving a given invariant (with the default being to assume all invariants). Such control can also be useful to eliminate some quantifier alternation cycles. |
Have a look at the branch |
It would be very useful to have such tactics in my opinion! |
Thanks for the suggestion. I tried that but then I get an error about the external call having a visible effect on
then
|
Maybe I misunderstood what you were trying to do before. I thought you were trying to do incremental induction. Here's a version that does that:
Notice how action However, in this proof, the code of It would be possible to add support for Hoare-style (i.e., procedure-modular) specifications in Ivy (for example, as implemented in Dafny). If you have a good use case for this I would consider it. |
Ah, what you did is exactly what I wanted, I just did not understand correctly how to do the wrapping that you had proposed. Thank you for writing out the example in detail, it was very helpful. |
The
invariant
in isolatesafety2
in the following example fails:More generally, I expected if isolate
A
has invariantQ
and isolateB
has invariantT
, then when we usewith A
onB
, the check forB
would try to prove that for each action from state s to s',Q(s) /\ T(s) /\ Q(s') -> T(s')
, but instead it seems to checkQ(s) /\ T(s) -> T(s')
. (#19 seemed related but was about initialization.)If I understand correctly, the way the invariants from
A
are incorporated is by essentially adding them asassume
statements at the beginning of each action -- perhaps they could also soundly be added asassume
statements at the end?The text was updated successfully, but these errors were encountered: