Inductive predicates, transitive closure, and code generation - isabelle

I am defining subclass and subtype relations as inductive predicates for a Java-like language and would like to generate code for these relations. Defining and generating code for the subtype relation was no problem:
type_synonym class_name = string
record class_def =
cname :: class_name
super :: "class_name option"
interfaces :: "class_name list"
type_synonym program = "class_def list"
(* Look up a class by its name *)
definition lookup_class :: "program ⇒ class_name ⇒ class_def option" where
"lookup_class P C ≡ find (λcl. (class_def.cname cl) = C) P"
(* Direct subclass relation *)
inductive is_subclass1 :: "program ⇒ class_name ⇒ class_name ⇒ bool" where
"⟦
Some cl = lookup_class P C;
(class_def.super cl) = Some C'
⟧ ⟹ is_subclass1 P C C'"
(* Reflexive transitive closure of `is_subclass1` *)
definition is_subclass :: "program ⇒ class_name ⇒ class_name ⇒ bool" where
"is_subclass P C C' ≡ (is_subclass1 P)⇧*⇧* C C'"
code_pred(modes: i ⇒ i ⇒ i ⇒ bool, i ⇒ i ⇒ o ⇒ bool) is_subclass1 .
code_pred
(modes: i ⇒ i ⇒ i ⇒ bool, i ⇒ i ⇒ o ⇒ bool)
[inductify]
is_subclass .
Here, is_subclass1 P C C' is true if C' is the name of the direct superclass of C. Then is_subclass is defined to be the transitive closure of is_subclass1.
For code generation to work, it is crucial that is_subclass1 has the mode i ⇒ i ⇒ o ⇒ bool, because otherwise the transitive closure cannot be computed. In the case of is_subclass1 this is easy, as a class has at most a single direct superclass, and the name of the superclass can thus be uniquely determined from the inputs.
However, for the subtype relation I also need to consider the interfaces that a class might implement:
inductive is_subtype1 :: "program ⇒ class_name ⇒ class_name ⇒ bool" for P :: program where
― ‹Same as subclass relation, no problem›
"⟦
Ok cl = lookup_class P C;
Some C' = (class_def.super cl)
⟧ ⟹ is_subtype1 P C C'" |
"⟦
Ok cl = lookup_class P C;
― ‹HERE IS THE PROBLEM: C' cannot be uniquely derived from the inputs and can thus not be marked as an output›
C' ∈ set (class_def.interfaces cl)
⟧ ⟹ is_subtype1 P C C'"
The problem is that there are multiple possible values for C' and that it cannot be marked as an output.
Intuitively, I think this should not be a problem for the code generator, as the generated code could just iterate over all the interfaces of a class. However, I don't know if this can be expressed in Isabelle/HOL.
Thus, the question is: Is there a way to generate code for is_subtype1 with mode i ⇒ i ⇒ o ⇒ bool?

You can solve your problem by importing HOL-Library.Predicate_Compile_Alternative_Defs and then using List.member _ _ instead of _ ∈ set _.

Related

How to include statement about type of the variable in the Isabelle/HOL term

I have following simple Isabelle/HOL theory:
theory Max_Of_Two_Integers_Real
imports Main
"HOL-Library.Multiset"
"HOL-Library.Code_Target_Numeral"
"HOL-Library.Code_Target_Nat"
"HOL-Library.Code_Abstract_Nat"
begin
definition two_integer_max_case_def :: "nat ⇒ nat ⇒ nat" where
"two_integer_max_case_def a b = (case a > b of True ⇒ a | False ⇒ b)"
lemma spec_final:
fixes a :: nat and b :: nat
assumes "a > b" (* and "b < a" *)
shows "two_integer_max_case_def a b = a"
using assms by (simp add: two_integer_max_case_def_def)
lemma spec_1:
fixes a :: nat and b :: nat
shows "a > b ⟹ two_integer_max_case_def a b = a"
by (simp add: two_integer_max_case_def_def)
lemma spec_2:
shows " (a ∈ nat set) ∧ (b ∈ nat set) ∧ (a > b) ⟹ two_integer_max_case_def a b = a"
by (simp add: two_integer_max_case_def_def)
end
Three lemmas try to express and prove that same statement, but progressively I am trying to move information from assumes and fixes towards the term. First 2 lemmas are correct, but the third (last) lemma is failing syntactically with the error message:
Type unification failed: Clash of types "_ ⇒ _" and "int"
Type error in application: incompatible operand type
Operator: nat :: int ⇒ nat
Operand: set :: ??'a list ⇒ ??'a set
My aim in this lemma is to move type information from the fixes towards the term/statement? How I make statements about the type of variable in the term (of inner syntax)?
Maybe I should use, if I am trying to avoid fixes clause (in which the variables may be declared), the full ForAll expression like:
lemma spec_final_3:
shows "∀ a :: nat . ∀ b :: nat . ( (a > b) ⟹ two_integer_max_case_def a b = a)"
by (simp add: two_integer_max_case_def_def)
But it is failing syntactically as well with the error message:
Inner syntax error: unexpected end of input⌂
Failed to parse prop
So - is it possible to include type statements in the term directly (without fixes clause) and is there any difference between fixes clause and type statement in the term? Maybe such differences start to appear during (semi)automatic proofs, e.g., when simplification tactics are applied or some other tactics?
nat set is interpreted as a function (that does not type correctly). The set of natural numbers can be expressed as UNIV :: nat set. Then, spec_2 reads
lemma spec_2:
shows "a ∈ (UNIV :: nat set) ∧ b ∈ (UNIV :: nat set) ∧ a > b ⟹
two_integer_max_case_def a b = a"
by (simp add: two_integer_max_case_def_def)
However, more natural way would be to include the type information in spec_1 without fixes clause:
lemma spec_1':
shows "(a :: nat) > (b :: nat) ⟹ two_integer_max_case_def a b = a"
by (simp add: two_integer_max_case_def_def)
∀ belongs to HOL, so the HOL implication should be used in spec_final_3:
lemma spec_final_3:
shows "∀ a :: nat. ∀ b :: nat. a > b ⟶ two_integer_max_case_def a b = a"
by (simp add: two_integer_max_case_def_def)
spec_1 can also be rewritten using an explicit meta-logic qualification (and implication) to look similar to spec_final_3:
lemma spec_1'':
shows "⋀ a :: nat. ⋀ b :: nat. a > b ⟹ two_integer_max_case_def a b = a"
by (simp add: two_integer_max_case_def_def)

How do I convert a predicate to a function in Isabelle?

In Isabelle HOL, I have a predicate on two numbers like this:
definition f :: "nat ⇒ nat ⇒ bool"
where
...
I can prove that this predicate is morally a function:
lemma f_function:
fixes x :: nat
shows "∃! y . f x y""
...
Intuitively, this should be enough for me to construct a function f' :: nat ⇒ nat that is provably equivalent to f', i.e.:
lemma f'_correct:
"f x y = (f' x = y)"
But how do I do that?
definition f' :: "nat ⇒ nat"
where
"f' x ≡ ?"
What do I put in for the question mark?
The typical approach is to use the definite description operator THE:
definition f' :: "nat ⇒ nat" where "f' x = (THE y. f x y)"
If you have already proven that this y is unique, you can then use e.g. the theorem theI' to show that f x (f' x) holds and theI_unique to show that if f x y holds, then y = f' x.
For more information about THE, SOME, etc. see the following:
Isabelle/HOL: What does the THE construct denote?
Proving intuitive statements about THE in Isabelle

Definition of right cancellation in Isabelle/HOL

I am trying to define in a Isabelle theory the property of right cancellation for function composition but
there are some errors that I can't to fix.
The definition I would like specify in Isabelle is the following:
f : A → B has the property of right cancellation iff
∀ C : (∀ g, h : B → C ) : g ◦ f = h ◦ f =⇒ g = h
Is it possible? Or more precisely, is it possible to quantify over a type?
Thanks in advance
It's not possible to explicitly quantify over types. If you prove a lemma with a type variable, it is implicitly proved for all instantiations of the type.
In some cases you can use a workaround and use the type V from the AFP entry ZFC_in_HOL. This type V basically is a type with so many elements that there is an injective function from (almost?) every HOL type into V. So in some cases it can be used as a kind of dynamic type to escape the type system.
theory ...
imports "ZFC_in_HOL.ZFC_Typeclasses"
begin
definition right_cancellation :: "('a ⇒ 'b) ⇒ bool" where
"right_cancellation f ≡ (∀g h :: 'b ⇒ V. g ∘ f = h ∘ f ⟶ g = h)"
In this case it is also possible to show that the definition is independent of the type used, so you might just use bool:
definition right_cancellation :: "'c itself ⇒ ('a ⇒ 'b) ⇒ bool" where
"right_cancellation t f ≡ (∀ g h :: 'b ⇒ 'c. g ∘ f = h ∘ f ⟶ g = h)"
lemma
fixes f:: "'x ⇒ 'y"
assumes as: "a1 ≠ (a2::'a)"
and r: "right_cancellation (A::'a itself) f"
shows "right_cancellation (B::'b itself) f"

Functor-like construction in Isabelle/Isar

Here's a small theorem in mathematics:
Suppose u is not an element of A, and v is not an element of B, and f is an injective function from A to B. Let A' = A union {u} and B' = B union {v}, and define g: A' -> B' by g(x) = f(x) if x is in A, and g(u) = v. Then g is injective as well.
If I were writing OCaml-like code, I'd represent A and B as types, and f as an A->B function, something like
module type Q =
sig
type 'a
type 'b
val f: 'a -> 'b
end
and then define a functor
module Extend (M : Q) : Q =
struct
type a = OrdinaryA of M.a | ExoticA
type b = OrdinaryB of M.b | ExoticB
let f x = match x with
OrdinaryA t -> OrdinaryB ( M.f t)
| Exotic A -> ExoticB
end;;
and my theorem would be that if Q.f is injective, then so is (Extend Q).f, where I'm hoping I've gotten the syntax more or less correct.
I'd like to do the same thing in Isabelle/Isar. Normally, that'd mean writing something like
definition injective :: "('a ⇒ 'b) ⇒ bool"
where "injective f ⟷ ( ∀ P Q. (f(P) = f(Q)) ⟷ (P = Q))"
proposition: "injective f ⟹ injective (Q(f))"
and Q is ... something. I don't know how to make, in Isabelle a single operation analogous to the functor Q in OCaml that creates two new datatypes and a function between them. The proof of injectivity seems as if it'd be fairly straightforward --- merely a four-case split. But I'd like help defining the new function that I've called Q f, given the function f.
Here's a solution. I tried to make a "definition" for the function Q, but could not do so; instead, creating a constant Q (built in strong analogy to map) let me state and prove the theorem:
theory Extensions
imports Main
begin
text ‹We show that if we have f: 'a → 'b that's injective, and we extend
both the domain and codomain types by a new element, and extend f in the
obvious way, then the resulting function is still injective.›
definition injective :: "('a ⇒ 'b) ⇒ bool"
where "injective f ⟷ ( ∀ P Q. (f(P) = f(Q)) ⟷ (P = Q))"
datatype 'a extension = Ordinary 'a | Exotic
fun Q :: "('a ⇒ 'b) ⇒ (('a extension) ⇒ ('b extension))" where
"Q f (Ordinary u) = Ordinary (f u)" |
"Q f (Exotic) = Exotic"
lemma "⟦injective f⟧ ⟹ injective (Q f)"
by (smt Q.elims extension.distinct(1) extension.inject injective_def)
end

How to define an abstract collection data type?

There are 4 kinds of collections in my theory. For each collection type I defined count and for_all operations:
theory MyCollections
imports Main
"~~/src/HOL/Library/Dlist"
"~~/src/HOL/Library/Multiset"
begin
typedef 'a mybag = "UNIV :: 'a multiset set" .. (* not unique, not ordered *)
typedef 'a myseq = "UNIV :: 'a list set" .. (* not unique, ordered *)
typedef 'a myset = "UNIV :: 'a set set" .. (* unique, not ordered *)
typedef 'a myord = "UNIV :: 'a dlist set" .. (* unique, ordered *)
setup_lifting type_definition_mybag
setup_lifting type_definition_myseq
setup_lifting type_definition_myset
setup_lifting type_definition_myord
lift_definition mybag_count :: "'a mybag ⇒ 'a ⇒ nat" is "Multiset.count" .
lift_definition myseq_count :: "'a myseq ⇒ 'a ⇒ nat" is "count_list" .
lift_definition myset_count :: "'a myset ⇒ 'a ⇒ nat" is "(λxs x. if x ∈ xs then 1 else 0)" .
lift_definition myord_count :: "'a myord ⇒ 'a ⇒ nat" is "(λxs x. if Dlist.member xs x then 1 else 0)" .
lift_definition mybag_for_all :: "'a mybag ⇒ ('a ⇒ bool) ⇒ bool" is "Multiset.Ball" .
lift_definition myseq_for_all :: "'a myseq ⇒ ('a ⇒ bool) ⇒ bool" is "(λxs f. list_all f xs)" .
lift_definition myset_for_all :: "'a myset ⇒ ('a ⇒ bool) ⇒ bool" is "Ball" .
lift_definition myord_for_all :: "'a myord ⇒ ('a ⇒ bool) ⇒ bool" is "(λxs f. list_all f (list_of_dlist xs))" .
I need to define polymorphic operations (includes and includes_all) for these collection types:
lift_definition mybag_includes :: "'a mybag ⇒ 'a ⇒ bool" is
"(λxs x. mybag_count xs x > 0)" .
lift_definition myseq_includes :: "'a myseq ⇒ 'a ⇒ bool" is
"(λxs x. myseq_count xs x > 0)" .
lift_definition myset_includes :: "'a myset ⇒ 'a ⇒ bool" is
"(λxs x. myset_count xs x > 0)" .
lift_definition myord_includes :: "'a myord ⇒ 'a ⇒ bool" is
"(λxs x. myord_count xs x > 0)" .
lift_definition mybag_mybag_includes_all :: "'a mybag ⇒ 'a mybag ⇒ bool" is
"(λxs ys. mybag_for_all ys (mybag_includes xs))" .
lift_definition mybag_myseq_includes_all :: "'a mybag ⇒ 'a myseq ⇒ bool" is
"(λxs ys. myseq_for_all ys (mybag_includes xs))" .
(* ... and 14 more similar operations for other type combinations *)
Some test cases:
value "mybag_myseq_includes_all (Abs_mybag {#1::nat,2,4,5,3,4#}) (Abs_myseq [1::nat,2])"
value "mybag_myseq_includes_all (Abs_mybag {#1::nat,2,4,5,3,4#}) (Abs_myseq [1::nat,7])"
The problem is that these operations are structurally identical and I don't want to duplicate them. I try to define an abstract collection type:
typedecl 'a mycol
consts
mycol_count :: "'a mycol ⇒ 'a ⇒ nat"
mycol_for_all :: "'a mycol ⇒ ('a ⇒ bool) ⇒ bool"
definition mycol_includes :: "'a mycol ⇒ 'a ⇒ bool" where
"mycol_includes xs x ≡ mycol_count xs x > 0"
definition mycol_includes_all :: "'a mycol ⇒ 'a mycol ⇒ bool" where
"mycol_includes_all xs ys ≡ mycol_for_all xs (mycol_includes ys)"
But I have no idea how to derive concrete collection types from the abstract one:
typedef 'a mybag = "{xs :: 'a mycol. ???}" ..
typedef 'a myseq = "{xs :: 'a mycol. ???}" ..
typedef 'a myset = "{xs :: 'a mycol. ???}" ..
typedef 'a myord = "{xs :: 'a mycol. ???}" ..
Once you have axiomatized the abstract collections type, you cannot refine it inside the logic any more. So the proposed approach does not work. But if you leave the container type abstract (as a type variable), then this is possible. I recommend to do that using locales:
locale container =
fixes count :: "'container => 'a => nat"
and for_all :: "'container => ('a => bool) => bool"
begin
definition "includes" where "includes C x <--> count C x > 0"
definition includes_all where "includes_all C C' <--> for_all C (includes C')"
end
Then, you can define your different collection types as usual and obtain the common operations by locale interpretation. For example,
interpretation mybag: container mybag_count mybag_forall .
generates the abbreviations mybag.includes and mybag.includes_all. Additionally, all theorems that are proven in the locale container are also specialized to mybag and prefixed with mybag.

Resources