Add rcorres definition, lemmas, and automation#959
Add rcorres definition, lemmas, and automation#959michaelmcinerney wants to merge 5 commits intomasterfrom
rcorres definition, lemmas, and automation#959Conversation
|
The |
70cd9c9 to
c03b79f
Compare
c750327 to
3126b6a
Compare
|
Now including the Instead of targeting the other PR with Lib rules, this now targets |
lib/RCorres.thy
Outdated
| using assms | ||
| apply (clarsimp simp: rcorres_def det_wp_def) | ||
| apply (rename_tac s s' rv' t') | ||
| apply (drule_tac x=s' in meta_spec) | ||
| apply (drule_tac x=s in spec) | ||
| apply (force simp: det_wp_def rcorres_def valid_def) | ||
| done |
There was a problem hiding this comment.
For consideration:
| using assms | |
| apply (clarsimp simp: rcorres_def det_wp_def) | |
| apply (rename_tac s s' rv' t') | |
| apply (drule_tac x=s' in meta_spec) | |
| apply (drule_tac x=s in spec) | |
| apply (force simp: det_wp_def rcorres_def valid_def) | |
| done | |
| using assms | |
| by (force simp: rcorres_def det_wp_def valid_def) |
works, but is slow. The by means it doesn't matter much that it is slow, but it's still a trade-off. Personally I like to avoid instantiations when I can, but it is mostly a matter of preference, I guess.
There was a problem hiding this comment.
I like avoiding instantiations, too, and if you're happy with slow proofs in Lib, I'll go with your suggestion. I sometimes try to imagine how much an extra 2 seconds in Lib that's run all the time will affect the overall time these proofs will run in the future. But I guess that's already a lost cause.
There was a problem hiding this comment.
It won't affect the wall time of a run of Lib -- because of the by the force will run in parallel with the rest and we rarely actually use all cores. This one only affects CPU time and potential impatience :-)
There was a problem hiding this comment.
Good point. I’ll go with the by, thanks
| lemma rcorres_req: | ||
| "\<lbrakk>\<And>s s'. R s s' \<Longrightarrow> F; F \<Longrightarrow> rcorres R f f' R'\<rbrakk> \<Longrightarrow> rcorres R f f' R'" |
There was a problem hiding this comment.
What does req stand for? Not that I have a better name. The normal corres rules have something similar with corres_gen_asm. Not quite the same, though.
There was a problem hiding this comment.
I'm actually not sure what req stands for. I'm just copying the format of corres_req, which also has just a plain schematic in the guard and then produces a new assumption. I would only use this rule when the precondition is schematic and I'm doing a bit of forwards reasoning, and it works nicely for that.
There was a problem hiding this comment.
Ah, that's where it comes from. I don't think I have used that rule yet. The req is a bit obscure, I'm not sure we should proliferate it, but I also don't have a good suggestion. @Xaphiosis or @corlewis do you have any ideas on this one?
There was a problem hiding this comment.
I do also have monadic_rewrite_req in the other PR, so yeah we should definitely decide on that before we go futher.
There was a problem hiding this comment.
@Xaphiosis, @corlewis do you have any naming input on _req?
There was a problem hiding this comment.
Ah, I've seen corres_req before but also never had an idea of what the req stood for.
For an alternative name, I think the equivalent valid lemma is hoare_gen_asm_spec, so maybe this one could be rcorres_gen_asm_spec?
There was a problem hiding this comment.
I've just ran a grep, and apparently we've been using ccorres_req and ccorres_req quite a bit (>270 occurrences). Maybe we should just leave it as _req after all even though nobody knows what it was supposed to mean. It's probably more confusing if we use the valid convention here than the corres one.
| lemma rcorres_return_stateAssert: | ||
| "rcorres (\<lambda>s s'. R' s s' \<and> P s') (return ()) (stateAssert P []) (\<lambda>_ _. R')" |
There was a problem hiding this comment.
This works (and I would keep it), but do we also want an assert_l/assert_r style rule for stateAssert? It's a bit more awkward in backwards style, because of the state dependency, so you might only do something like:
lemma rcorres_stateAssert_l:
"⟦P ⟹ rcorres R (b ()) c R'⟧ ⟹ rcorres (λs s'. R s s' ∧ P s) (stateAssert P xs >>= b) c R'"
by (fastforce intro: rcorres_assume_pre)
That seems roughly equivalent to what we get in the return_stateAssert rule.
There was a problem hiding this comment.
Because the P depends on the abstract state, the P in the assumptions for this rule doesn't type check. If you just remove the P, it works, it would then be exactly like corres_stateAssert_r, so I guess that's nice and maybe what we'd like. I think that if we want to add the asserted property to the guard, then we'd need to use the forwards style rule, which has the analogue in corres world of corres_stateAssert_add_assertion. Or maybe my brain isn't sufficiently backwards(-style) and I'm wrong.
There was a problem hiding this comment.
Because the P depends on the abstract state, the P in the assumptions for this rule doesn't type check.
You are right, of course. We could do !!s. P s ==> .., but that is generally not so nice (it's sound but will usually just pollute the goal, because the s is not related to anything, and making it related to the precondition will make the precondition not schematic). So just leaving it out gives us the backwards rule, yes.
| lemma rcorres_conj_lift: | ||
| "\<lbrakk>\<And>s'. det_wp (\<lambda>s. P s s') f; rcorres Q f f' R'; rcorres R f f' Q'\<rbrakk> | ||
| \<Longrightarrow> rcorres | ||
| (\<lambda>s s'. P s s' \<and> Q s s' \<and> R s s') | ||
| f f' (\<lambda>rv rv' s s'. R' rv rv' s s' \<and> Q' rv rv' s s')" |
There was a problem hiding this comment.
The det_wp in this one is surprising, I'd have thought it would work without. Is there a similar rule that would work without it? Not saying we should get rid of this one, just trying to get an intuition. (I see it's a theme in the other rules as well)
There was a problem hiding this comment.
This was the argument about not having a conj lift rule for exs_valid, or not being able to go from (Ex x. P x) /\ (Ex x. Q x) to Ex x. P x /\ Q x. We may be able to have one state that satisfies R' and then perhaps another state that satisfies Q', but not one state that satisfies both. Forcing the function to be deterministic means that the unique state satisfies both.
There was a problem hiding this comment.
Oh, of course, that's what it is and that is also why it doesn't behave like any of the other forms of Hoare triples -- the post condition is existential in one and universal in the other state parameter, so you get effects of both. I think that would be worth a comment somewhere, either at this rule or at the definition of rcorres. Because it's really something slightly new.
There was a problem hiding this comment.
Sure thing. I could even explain there why we might not want to introduce anything about failure into rcorres itself. It might be a bit of an essay, but I could try if you'd like.
There was a problem hiding this comment.
Thanks for adding the explanation, I think that is good. I would add a summary side sentence on the universal/existential duality, will comment above.
lsf37
left a comment
There was a problem hiding this comment.
Some suggestions for tweaks and a request for more documentation (mostly examples). But I think this is looking pretty nice!
It's interesting to see where it behaves exactly like Hoare triples and where it deviates -- mostly around failure. Maybe it would correspond more closely to total correctness, but I don't think it quite does that either.
|
I've updated this, addressing all the comments so far, except the I'm expecting that we will want to still tweak things quite a lot, so please let me know if there are any further changes I should make to these changes. |
|
Actually, I'm not sure if |
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
This adds rcorres, an approach to correspondence with general relations. Many lemmas to use this concept are included, as well as a lightweight method that is very similar to wpsimp. Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
c23ef24 to
27e984f
Compare
|
Now with the |
lib/RCorres.thy
Outdated
| apply rcorres | ||
| by simp |
There was a problem hiding this comment.
We should change all of these to:
| apply rcorres | |
| by simp | |
| apply rcorres | |
| apply simp | |
| done |
apply scripts should generally end with done. We use by when it's a one-liner or when there is a good other reason like the last step taking long and therefore benefitting from concurrency.
There are a few in the original PR that I didn't comment on because they looked like they are slow steps (e.g. by force or by (fastforce ..)), but if they are not slow we should also change them to apply/done for consistency.
| result of @{term f} such that the desired conjunction holds. The @{term det_wp} | ||
| assumption does allow us to make this inference, and so we include this assumption in our rule. | ||
|
|
||
| It does not seem possible for us to state an adequate lifting rule for conjunction for |
There was a problem hiding this comment.
| It does not seem possible for us to state an adequate lifting rule for conjunction for | |
| Overall, because the precondition is universal, but the postcondition is existential, | |
| it does not seem possible for us to state an adequate lifting rule for conjunction for |
There was a problem hiding this comment.
Do you mean the precondition and the postcondition, or do you mean the concrete state is universal and the abstract state is existential?
There was a problem hiding this comment.
I mean the conditions. The definition is forall s s' in P, there exists t t' in execution such that Q t t', so the P behaves universally and the Q existentially.
Thinking more about it, I wonder if we should change it so that that Q is also universal, but that doesn't make sense if we view Q as a state relation instead of a kind of validity. So probably not.
There was a problem hiding this comment.
It doesn't make sense to me, but I can add this.
There was a problem hiding this comment.
Which bit doesn't make sense? That a condition behaves universally/existentially? I admit that's not exactly a defined expression, but I thought it carries some intuition at least. If it just confuses people we should not add it.
There was a problem hiding this comment.
I think that if we were to say the precondition is universal, but the postcondition is existential, then someone would think we're doing something along the lines of "for all preconditions P, there is a postcondition Q, such that...", which is not what we're doing, so might be more confusing than helpful. I think the comment I have there now is way too long, but I can't think of a way to make it both precise and short. I think some will be happy to just go along with the det_wp assumption being there, and for those who are sceptical or interested, the explanation is easy enough to follow.
There was a problem hiding this comment.
I had not thought of that reading. Ok, let's not add anything, your explanation is clear enough.
lib/RCorres.thy
Outdated
| From our assumptions, we know that there is there is some pair @{term "(rv_1, t_1)"} in the result | ||
| of @{term f} such that @{term "R' rv_1 rv' t_1 t'"} holds, and that there is some pair | ||
| @{term "(rv_2, t_2)"} in the result of @{term f} such that @{term "Q' rv_2 rv' t_2 t' holds"}. |
There was a problem hiding this comment.
The Isabelle convention is without the underscore (unlike LaTeX) -- mostly because the underscore is not rendered as subscript. We could use actual Isabelle subscript notation (that is fine), but it's awkward to type, so we usually don't.
I.e. I would propose just
| From our assumptions, we know that there is there is some pair @{term "(rv_1, t_1)"} in the result | |
| of @{term f} such that @{term "R' rv_1 rv' t_1 t'"} holds, and that there is some pair | |
| @{term "(rv_2, t_2)"} in the result of @{term f} such that @{term "Q' rv_2 rv' t_2 t' holds"}. | |
| From our assumptions, we know that there is there is some pair @{term "(rv1, t1)"} in the result | |
| of @{term f} such that @{term "R' rv1 rv' t1 t'"} holds, and that there is some pair | |
| @{term "(rv2, t2)"} in the result of @{term f} such that @{term "Q' rv2 rv' t2 t' holds"}. |
lib/RCorres.thy
Outdated
| of @{term f} such that @{term "R' rv_1 rv' t_1 t'"} holds, and that there is some pair | ||
| @{term "(rv_2, t_2)"} in the result of @{term f} such that @{term "Q' rv_2 rv' t_2 t' holds"}. | ||
| However, without any further assumptions on @{term f}, we cannot infer that | ||
| @{term "(rv_1, t_1) = (rv_2, t_2)"} and therefore that there is a pair @{term "(rv, t)"} in the |
There was a problem hiding this comment.
| @{term "(rv_1, t_1) = (rv_2, t_2)"} and therefore that there is a pair @{term "(rv, t)"} in the | |
| @{term "(rv1, t1) = (rv2, t2)"} and therefore that there is a pair @{term "(rv, t)"} in the |
lib/RCorres.thy
Outdated
| monadic function @{term f'}, it is occasionally useful for us to be able to state a relation | ||
| between all four values from the pair @{term "(rv', t')"} in the set of results of @{term f'} | ||
| and the pair @{term "(rv, t)"} in the set of results of @{term f}. This is a partial | ||
| generalisation of our usual @{term corres_underlying} framework, which allows for us to state a |
There was a problem hiding this comment.
Nitpick: @{term } is a good default, but if we're referring to a single constant, we should use @{const } because Isabelle will then check that the constant exists, which means you get an error if someone changes the constant and you can update the documentation.
| generalisation of our usual @{term corres_underlying} framework, which allows for us to state a | |
| generalisation of our usual @{const corres_underlying} framework, which allows for us to state a |
(For that to work, the constant has to be declared already, so it often can't be used in the explanation of the constant at the definition place, but it is available everywhere after).
lib/RCorres.thy
Outdated
| relate to the returned states. An example that is used within the proofs is list_queue_relation. | ||
|
|
||
| This shares many similarities with Hoare triples, as seen by the split rule for | ||
| @{term Nondet_Monad.bind}, as well as the lifting rules, though there are some important |
There was a problem hiding this comment.
| @{term Nondet_Monad.bind}, as well as the lifting rules, though there are some important | |
| @{const Nondet_Monad.bind}, as well as the lifting rules, though there are some important |
lib/RCorres.thy
Outdated
| @{term Nondet_Monad.bind}, as well as the lifting rules, though there are some important | ||
| differences involving determinacy that are explained below. | ||
|
|
||
| Note that rcorres is not a full generalisation of @{term corres_underlying}, in that it does not |
There was a problem hiding this comment.
| Note that rcorres is not a full generalisation of @{term corres_underlying}, in that it does not | |
| Note that rcorres is not a full generalisation of @{const corres_underlying}, in that it does not |
lib/RCorres.thy
Outdated
| play a prominent role in the proofs we wish to perform for rcorres goals. We have lifting rules | ||
| for the usual Boolean connectives, but will also wish to lift an rcorres goal to a @{term valid} | ||
| goal when the postcondition of the rcorres goal mentions only the return value and state from | ||
| one of the two monads. These lifting rules often require @{term no_fail} assumptions, and so when |
There was a problem hiding this comment.
| one of the two monads. These lifting rules often require @{term no_fail} assumptions, and so when | |
| one of the two monads. These lifting rules often require @{const no_fail} assumptions, and so when |
lib/RCorres.thy
Outdated
| goal when the postcondition of the rcorres goal mentions only the return value and state from | ||
| one of the two monads. These lifting rules often require @{term no_fail} assumptions, and so when | ||
| lifting an rcorres goal with a concrete function that is a @{term Nondet_Monad.bind} of other | ||
| functions, we will immediately be required to show @{term no_fail} for the composite function |
There was a problem hiding this comment.
| functions, we will immediately be required to show @{term no_fail} for the composite function | |
| functions, we will immediately be required to show @{const no_fail} for the composite function |
lib/RCorres.thy
Outdated
| lifting an rcorres goal with a concrete function that is a @{term Nondet_Monad.bind} of other | ||
| functions, we will immediately be required to show @{term no_fail} for the composite function | ||
| anyway. Therefore, we have chosen to keep failure separate. The section below regarding | ||
| interactions with @{term no_fail} includes a rule that allows us to show @{term no_fail} for |
There was a problem hiding this comment.
| interactions with @{term no_fail} includes a rule that allows us to show @{term no_fail} for | |
| interactions with @{const no_fail} includes a rule that allows us to show @{const no_fail} for |
lib/RCorres.thy
Outdated
| anyway. Therefore, we have chosen to keep failure separate. The section below regarding | ||
| interactions with @{term no_fail} includes a rule that allows us to show @{term no_fail} for | ||
| composite functions by transforming complex predicates with rcorres. The section below regarding | ||
| @{term corres_underlying} shows the relation between rcorres and @{term corres_underlying}.\<close> |
There was a problem hiding this comment.
| @{term corres_underlying} shows the relation between rcorres and @{term corres_underlying}.\<close> | |
| @{const corres_underlying} shows the relation between rcorres and @{const corres_underlying}.\<close> |
There was a problem hiding this comment.
(I've only done this paragraph, because I've just re-read it -- please have a look if there are more instances below. We might as well make it nice while we're here.)
lib/RCorres.thy
Outdated
| "\<lbrakk>\<And>s'. det_wp (\<lambda>s. P s s') f; rcorres Q f f' R'; rcorres R f f' Q'\<rbrakk> | ||
| \<Longrightarrow> rcorres | ||
| (\<lambda>s s'. P s s' \<and> Q s s' \<and> R s s') |
There was a problem hiding this comment.
Now that I've understood the need for the det_wp -- do we really want it to be !!s'?
I think the following would be equivalent
| "\<lbrakk>\<And>s'. det_wp (\<lambda>s. P s s') f; rcorres Q f f' R'; rcorres R f f' Q'\<rbrakk> | |
| \<Longrightarrow> rcorres | |
| (\<lambda>s s'. P s s' \<and> Q s s' \<and> R s s') | |
| "\<lbrakk>det_wp P f; rcorres Q f f' R'; rcorres R f f' Q'\<rbrakk> | |
| \<Longrightarrow> rcorres | |
| (\<lambda>s s'. P s \<and> Q s s' \<and> R s s') |
because P can always be instantiated as %s. Ex s'. P s s' if we want it to. The only time we'd want the forall form is if we want to use this rule as a forward rule where the P already exists in the rcorres goal and instantiates the one for det_wp.
There was a problem hiding this comment.
I can make this change, but then I'm not sure how I would use a lemmas statement to get the forwards version. So I think I'd have to write the forwards version out in full. If that's alright, I'll go with that.
lib/RCorres.thy
Outdated
| \<open>As with @{thm rcorres_conj_lift}, the @{term det_wp} assumption seems necessary in order | ||
| to state an adequate lifting rule for @{term All}.\<close> | ||
| lemma rcorres_allI: | ||
| "\<lbrakk>\<And>s'. det_wp (\<lambda>s. P s s') f; \<And>x. rcorres (\<lambda>s s'. R x s s') f f' (\<lambda>rv rv' s s'. R' x rv rv' s s')\<rbrakk> |
There was a problem hiding this comment.
if we decide to change the forall for det_wp above, we should also change it here and below
Signed-off-by: Michael McInerney <michael.mcinerney@proofcraft.systems>
This work should be rebased over the work Gerwin has adding the
declaringmethod, and then the automation should be updated to support thedel:feature. I will do that once that work is merged. But I thought I would make this PR now so that people could comment on it, just in case it generated a lot of thoughts/comments.