Proving the set of reachable states of semantics function is finite in Isabelle - isabelle

Consider the following property:
lemma "finite {t. (c,s) ⇒ t}"
Which refers to the following big step semantics:
inductive gbig_step :: "com × state ⇒ state ⇒ bool" (infix "⇒" 55)
where
Skip: "(SKIP, s) ⇒ s"
| Assign: "(x ::= a, s) ⇒ s(x := aval a s)"
| Seq: "⟦(c1, s1) ⇒ s2; (c2, s2) ⇒ s3⟧ ⟹ (c1;;c2, s1) ⇒ s3"
| IfBlock: "⟦(b,c) ∈ set gcs; bval b s; (c,s) ⇒ s'⟧ ⟹ (IF gcs FI, s) ⇒ s'"
| DoTrue: "⟦(b,c) ∈ set gcs; bval b s1; (c,s1) ⇒ s2;(DO gcs OD,s2) ⇒ s3⟧
⟹ (DO gcs OD, s1) ⇒ s3"
| DoFalse: "⟦(∀ (b,c) ∈ set gcs. ¬ bval b s)⟧ ⟹ (DO gcs OD, s) ⇒ s"
To me it is obvious that the property holds by induction on the big step relation. However, I can not get it out of the set, so I cannot effectively induct on it.
How could I do this?

Finiteness is nothing that you could prove directly with the induction rule of an inductive predicate. The problem is that looking at an individual run (as does the induction rule) does not say anything about the branching behaviour, which must also be finite for the statement to hold.
I see two approaches to proving finiteness:
Model the derivation tree explicitly as a datatype in Isabelle/HOL and prove that it adequately represent the derivation trees behind inductive. Then prove that the tree has finitely many leaves (by induction on the tree). If you design the datatype such that the states in the leaves are a type parameter, then the corresponding set function generated by the datatype package is what you want to prove to be finite. (Note that you cannot prove finiteness by the induction rule of the set function, because that would again be just a single run.)
Look at the internal construction of the inductive definition. It is defined as the least fixpoint of a functional. You can get access to these internals by putting the inductive definition into a context in which [[inductive_internals]] is declared. Then you can prove that the functional preserves finiteness in a single step and then lift that through the induction.
The proof argument in both approaches is similar. The explicit datatype in #1 simply reifies the fixpoint argument of #2. So you can think of #1 as a deep embedding of #2. Of course, you can also re-derive the internal construction (in a more suitable format) just from the introduction and induction theorems and then follow approach #2.
I would try to do precisely this as your semantics is small. For a large real-world semantics, it might make sense to spend the effort to automate step #2 in ML.

Related

How to prove this simple theorem in Isabelle?

I define a very simple function replace which replaces 1 with 0 while preserving other input values. I want to prove that the output of the function cannot be 1. How to achieve this?
Here's the code.
theory Question
imports Main
begin
fun replace :: "nat ⇒ nat" where
"replace (Suc 0) = 0" |
"replace x = x"
theorem no1: "replace x ≠ (Suc 0)"
sorry
end
Thanks!
There exist several approaches for proving the statement that you are trying to prove.
You can make an attempt to use sledgehammer to find the proof automatically, e.g.
theorem no1: "replace x ≠ (Suc 0)"
by sledgehammer
(*using replace.elims by blast*)
Once the proof is found, you can delete the explicit invocation of the command sledgehammer.
Perhaps, a slightly better way to state the proof found by the sledgehammer would be
theorem no1': "replace x ≠ (Suc 0)"
by (auto elim: replace.elims)
You can also try to provide a more specialized proof. For example,
theorem no1: "replace x ≠ (Suc 0)"
by (cases x rule: replace.cases) simp_all
This proof looks at the different cases the value of x can have and then uses simplifier (in conjunction with the simp rules provided by the command fun during the definition of your function) to finish the proof. You can see all theorems that are generated by the command fun by typing print_theorems immediately after the specification of replace, e.g.
fun replace :: "nat ⇒ nat" where
"replace (Suc 0) = 0" |
"replace x = x"
print_theorems
Of course, there are other ways to prove the result that you are trying to prove. One good way to improve your ability to find such proofs is by reading the documentation and tutorials on Isabelle. My own starting point for learning Isabelle was the book "Concrete Semantics" by Tobias Nipkow and Gerwin Klein.

Isabelle/HOL restrict codomain

I am sorry for asking so many Isabelle questions lately. Right now I have a type problem.
I want to use a type_synonym introduced in a AFP-theory.
type_synonym my_fun = "nat ⇒ real"
I have a locale in my own theory where:
fixes n :: nat
and f :: "my_fun"
and A :: "nat set"
defines A: "A ≡ {0..n}"
However, in my use case the output of the function f is always a natural number in the set {0..n}. I want to impose this as a condition (or is there a better way to do it?). The only way I found was to:
assumes "∀v. ∃ i. f v = i ∧ i ∈ A"
since
assumes "∀v. f v ∈ A"
does not work.
If I let Isabelle show me the involved types it seems alright to me:
∀v::nat. ∃i::nat. (f::nat ⇒ real) v = real i ∧ i ∈ (A::nat set)
But of course now I cannot type something like this:
have "f ` {0..10} ⊆ A"
But I have to prove this. I understand where this problem comes from. However, I do not know how to proceed in a case like this. What is the normal way to deal with it? I would like to use my_fun as it has the same meaning as in my theory.
Thank you (again).
If you look closely at ∀v::nat. ∃i::nat. (f::nat ⇒ real) v = real i ∧ i ∈ (A::nat set), you will be able to see the mechanism that was used for making the implicit type conversion between nat and real: it is the abbreviation real (this invokes of_nat defined for semiring_1 in Nat.thy) that appears in the statement of the assumption in the context of the locale.
Of course, you can use the same mechanism explicitly. For example, you can define A::real set as A ≡ image real {0..n} instead of A::nat set as A ≡ {0..n}. Then you can use range f ⊆ A instead of assumes "∀v. ∃ i. f v = i ∧ i ∈ A”. However, I doubt that there is a universally accepted correct way to do it: it depends on what exactly you are trying to achieve. Nonetheless, for the sake of the argument, your locale could look like this:
type_synonym my_fun = "nat ⇒ real"
locale myloc_basis =
fixes n :: nat
abbreviation (in myloc_basis) A where "A ≡ image real {0..n}"
locale myloc = myloc_basis +
fixes f :: "my_fun"
assumes range: "range f ⊆ A"
lemma (in myloc) "f ` {0..10} ⊆ A"
using range by auto
I want to impose this as a condition (or is there a better way to do
it?).
The answer depends on what is known about f. If only a condition on the range of f is known, as the statement of your question seems to suggest, then, I guess, you can only state is as an assumption.
As a side note, to the best of my knowledge, defines is considered to be obsolete and it is best to avoid using it in the specifications of a locale: stackoverflow.com/questions/56497678.

Instantiating a class from a concrete object?

I'm attempting to formalize a series of proofs about topology from a book [1] in Isabelle.
I want to encode the idea that a topological space (X,T) consists of a set X of "points" (elements of some arbitrary type 'a), and a set of subsets of X, called T, such that:
A1. if an element p is in X, then there exists at least one set N in T that also contains p.
A2. if sets U and V are in T, and if p∈(U∩V), then there must exist at a set N in T where N⊆(U∩V) and x∈N. (If two sets intersect, then there must be a neighborhood that covers the intersection.).
Currently I have the following definition:
class topspace =
fixes X :: "'a set"
fixes T :: "('a set) set"
assumes A1: "p∈X ≡ ∃N∈T. p∈N"
assumes A2: "U∈T ∧ V∈T ∧ x∈(U∩V) ⟹ ∃N∈T. x∈N ∧ N⊆(U∩V)"
begin
(* ... *)
end
So far, so good. I'm able to add various definitions and prove various lemmas and theorems about hypothetical topspace instances.
But how do I actually create one? Unless I'm misinterpreting things, the examples I've seen so far for the instance and instantiate keywords all seem to be been about declaring that one particular abstract class (or type or locale) is an instance of another.
How do I tell Isabelle that a particular pair of sets (e.g. X={1::int, 2, 3}, T={X,{}}) form a topspace?
Likewise, how can I use my definition to prove that X={1::int, 2, 3}, T={} does not fit the requirements?
Finally, once I show that a particular concrete object X meets the definition of a topspace, how do I tell Isabelle to now make use of all the definitions and theorems I've proven about topspace when proving things about X?
BTW, I'm using class because I don't know any better. If it's not the right tool for the job, I'm happy to do something else.
[1]: A Bridge to Advanced Mathematics by Dennis Sentilles
I've made some progress here: a class is a special type of locale, but it isn't necessary for this sort of usage, and using the locale keyword directly simplifies the situation a bit. Every locale has an associated theorem that you can use to instantiate it:
locale topspace =
fixes X :: "'a set"
fixes T :: "('a set) set"
assumes A1 [simp]: "x∈X ≡ ∃N∈T. x∈N"
assumes A2 [simp]: "U∈T ∧ V∈T ∧ x∈(U∩V) ⟹ ∃N∈T. x∈N ∧ N⊆(U∩V)"
theorem
assumes "X⇩A={1,2,3::int}" and "T⇩A={{}, {1,2,3::int}}"
shows "topspace X⇩A T⇩A"
proof
show "⋀U V x. U∈T⇩A ∧ V∈T⇩A ∧ x∈U∩V ⟹ ∃N∈T⇩A. x∈N ∧ N⊆U∩V"
and "⋀x. x∈X⇩A ≡ ∃N∈T⇩A. x∈N" using assms by auto
qed
If we want to use definition for declarations, the proof goal becomes a bit more complex, and we need to use the unfolding keyword. (The locales.pdf that comes with isabelle covers this, but I'm not sure I'm not yet able to explain it in my own words). Anyway, this works:
experiment
begin
definition X⇩B where "X⇩B={1,2,3::int}"
definition T⇩B where "T⇩B={{}, {1,2,3::int}}"
lemma istop0: "topspace X⇩B T⇩B" proof
show "⋀U V x. U∈T⇩B ∧ V∈T⇩B ∧ x∈U∩V ⟹ ∃N∈T⇩B. x∈N ∧ N⊆U∩V"
and "⋀x. x∈X⇩B ≡ ∃N∈T⇩B. x∈N" unfolding X⇩B_def T⇩B_def by auto
qed
end
I believe it's also possible, and possibly preferable, to do all this work inside of a sub-locale, but I haven't quite worked out the syntax for this.
Although locales are implemented in the calculus itself and hence their predicates can be used in any regular proposition, this is usually not recommended. Instead, you should instantiate locales using e.g. interpretation, as in the following example.
locale topspace =
fixes X :: "'a set"
fixes T :: "('a set) set"
assumes A1 [simp]: "x∈X ⟷ (∃N∈T. x∈N)"
assumes A2 [simp]: "U∈T ∧ V∈T ∧ x∈(U∩V) ⟹ ∃N∈T. x∈N ∧ N⊆(U∩V)"
context
fixes X⇩A T⇩A
assumes X⇩A_eq: "X⇩A = {1, 2, 3 :: int}"
and T⇩A_eq: "T⇩A = {{}, {1, 2, 3 :: int}}"
begin
interpretation example: topspace X⇩A T⇩A
by standard (auto simp add: X⇩A_eq T⇩A_eq)
lemmas facts = example.A1 example.A2
end
thm facts
Whether this pattern really fits for your needs depends on your application; if you just want to have a predicate, it is better to define it directly without using locale at all.
Note: there is really need to the Pure equality »≡«; prefer HOL equality »=«, or its syntactic variant »⟷«.

Reasoning about the entirety of a codatatype in Isabelle/HOL

I'd like to write down some definitions (and prove some lemmas!) about paths in a graph. Let's say that the graph is given implicitly by a relation of type 'a => 'a => bool. To talk about a possibly infinite path in the graph, I thought a sensible thing was to use a lazy list codatatype like 'a llist as given in "Defining (Co)datatypes and Primitively (Co)recursive Functions in Isabelle/HOL" (datatypes.pdf in the Isabelle distribution).
This works well enough, but I'd then I'd like to define a predicate that takes such a list and a graph relation and evaluates to true iff the list defines a valid path in the graph: any pair of adjacent entries in the list must be an edge.
If I was using 'a list as a type to represent the paths, this would be easy: I'd just define the predicate using primrec. However, the co-inductive definitions I can find all seem to generate or consume the data one element at a time, rather than being able to make a statement about the whole thing. Obviously, I realise that the resulting predicate won't be computable (because it is making a statement about infinite streams), so it might have a \forall in there somewhere, but that's fine - I want to use it for developing a theory, not generating code.
How can I define such a predicate? (And how could I prove the obvious associated introduction and elimination lemmas that make it useful?)
Thanks!
I suppose the most idiomatic way to do this is to use a coinductive predicate. Intuitively, this is like a normal inductive predicate except that you also allow ‘infinite derivation trees’:
type_synonym 'a graph = "'a ⇒ 'a ⇒ bool"
codatatype 'a llist = LNil | LCons 'a "'a llist"
coinductive is_path :: "'a graph ⇒ 'a llist ⇒ bool" for g :: "'a graph" where
is_path_LNil:
"is_path g LNil"
| is_path_singleton:
"is_path g (LCons x LNil)"
| is_path_LCons:
"g x y ⟹ is_path g (LCons y path) ⟹ is_path g (LCons x (LCons y path))"
This gives you introduction rules is_path.intros and an elimination rule is_path.cases.
When you want to show that an inductive predicate holds, you just use its introduction rules; when you want to show that an inductive predicate implies something else, you use induction with its induction rule.
With coinductive predicates, it is typically the other way round: When you want to show that a coinductive predicate implies something else, you just use its elimination rules. When you want to show that a coinductive predicate holds, you have to use coinduction.

Why is my definition of a function that chooses an element from a finite set inconsistent?

I would like to reason about functions that choose one element from a finite set.
I tried to define a predicate that tells me whether some given function is such a “chooser” function:
definition chooser :: "('a set ⇒ 'a) ⇒ bool"
where "chooser f ⟷ (∀ A . finite A ⟶ f A ∈ A)"
Actually those finite sets from which I'd like to choose elements are of a concrete type, but putting a concrete type in 'a's place causes the same trouble.
I have also tried to omit finite A, but the sets I'm dealing with are finite, and I don't even want to think about the axiom of choice here.
Now this definition seems to be inconsistent:
lemma assumes "chooser f" shows "False" using assms chooser_def by force
How can I define chooser in a reasonable way? I would like to use it as follows:
assume "finite A"
moreover assume "chooser f"
moreover assume "choice = f A"
ultimately have "choice ∈ A" by ???
Most of the time it merely matters that a member of the set is chosen, not how it is chosen.
Background: I'd like to formalise tie-breakers in auctions (section 4 of this paper). Suppose there are two highest bids for the item being auctioned, we need to arbitrarily choose the one bidder who should win the auction.
Here is, BTW, a really minimal example (which is a bit harder to understand):
lemma "(∀ A . finite A ⟶ f A ∈ A) ⟹ False" by force
I merely provide the details based on Brian's comment that a choice function is defined only for a collection of non-empty sets.
From the Wikipedia entry on Choice_function:
A choice function (selector, selection) is a mathematical function f that is defined on some collection X of nonempty sets and assigns to each set S in that collection some element f(S) of S.
By now, you probably already have what you need from Brian's coment, but I do this anyway. The definition of chooser only needs the requirement that the set isn't empty:
definition chooser :: "('a set => 'a) => bool" where
"chooser f <-> (!A. A ~= {} --> f A ∈ A)"
theorem "(finite A & A ~= {} & chooser f) ==> (f A ∈ A)"
by(metis chooser_def)
theorem "(A ~= {} & chooser f) ==> (f A ∈ A)"
by(metis chooser_def)
You said that you don't want to use the Axiom of Choice, but a standard choice function demonstrates a good template to follow, not that you need it.
definition choice :: "'a set => 'a" where
"choice T = (SOME x. x ∈ T)"
theorem "T ~= {} ==> choice T ∈ T"
by(unfold choice_def, metis ex_in_conv someI
--GC

Resources