Isabelle's Code generation: Abstraction lemmas for containers? - code-generation

I am experimenting with the Code generator. My theory contains a datatype that encodes an invariant:
typedef small = "{x::nat. x < 10}" morphisms to_nat small
by (rule exI[where x = 0], simp)
definition "is_one x ⟷ x = small 1"
Now I want to export code for is_one. It seems that I first have to set up the data type for the code generator as follows:
code_datatype small
instantiation small :: equal
begin
definition "HOL.equal a b ⟷ to_nat a = to_nat b"
instance
apply default
unfolding equal_small_def
apply (rule to_nat_inject)
done
end
lemma [code abstype]: "small (to_nat x) = x" by (rule to_nat_inverse)
And now I am able to define a code equation for is_one that does not violate the abstraction:
definition [code del]:"small_one = small 1"
declare is_one_def[code del]
lemma [code]: "is_one x ⟷ x = small_one" unfolding is_one_def small_one_def..
lemma [code abstract]: "to_nat small_one = 1"
unfolding small_one_def by (rule small_inverse, simp)
export_code is_one in Haskell file -
Question 1: Can I avoid pulling the definition of small_one out of is_one?
Now I have a compound value of small items:
definition foo :: "small set list" where "foo = [ {small (10-i), small i} . i <- [2,3,4] ]"
In that form, I cannot export code using it (“Abstraction violation in code equation foo...”).
Question 2: How do I define an [code abstract] lemma for such a value? It seems that the code generator does not accept lemmas of the form map to_nat foo = ....

For types with invariants declared by code_abstract such as small, no code equation may contain the abstraction function small. Consequently, if you want the equality test in is_one to be expressed on type small, you have to define a constant for small 1 and prove the corresponding code equation with to_nat first. This is what you have done. However, it would be easier to use equality on the representation type nat, i.e., inline the implementation equal_class.equal; then, you do not need to instantiate equal for small either.
lemma is_one_code [code]: "is_one x ⟷ to_nat x = 1"
by(cases x)(simp add: is_one_def small_inject small_inverse)
The lifting package does most of this for you automatically:
setup_lifting type_definition_small
lift_definition is_one :: "small => bool" is "%x. x = 1" ..
export_code is_one in SML file -
Unfortunately, the code generator does not support compound return types that involve abstract types like in small set list. As described in Data Refinement in Isabelle/HOL, sect. 3.2, you have to wrap the type small set list as a type constructor small_set_list of its own and define foo with type small_set_list. The type small_set_list is then represented as nat set list with the invariant list_all (%A. ALL x:A. x < 10).

Besides Andreas' explanation and comprehensive solution, I found a work-around that avoids introducing a new type. Assume foo is not used in many places, say, only in
definition "in_foo x = (∀s ∈ set foo. x ∈ s)"
Trying to export code for in_foo gives the "Abstraction violation" error. But using program refinement, I can avoid the problematic code:
I use code_thms to find out how foo is used and find that it is used as list_all (op ∈ ?x) foo. Now I rewrite this particular use of foo without mentioning small:
lemma [code_unfold]:
"list_all (op ∈ x) foo ⟷
list_all (op ∈ (to_nat x)) [ {(10-i), i} . i <- [2, 3, 4] ]"
unfolding foo_def by (auto simp add: small_inverse small_inject)
(Had I defined foo using lift_definition, the proof after apply transfer would be even simpler.)
After this transformation, which essentially captures that all members of foo fulfill the invariant of small, the code export works as intended:
export_code in_foo in Haskell file -

Related

Extracting integer from int_Ring type in mathcomp's ssralg

A bit of set up for the question: The notation `_i is defined to be the i-th component of a sequence, but is also meant to be the i-th coefficient of a polynomial. The following code outputs Negz 2 : int_ZmodType:
From mathcomp Require Import all_ssreflect.
From mathcomp Require Import all_algebra.
Open Scope ring_scope.
Definition my_seq := [:: Posz 4; Negz 2].
Eval compute in my_seq`_1.
The type of my_seq is seq int. The type int has constructors Posz and Negz.
The header of
https://github.com/math-comp/math-comp/blob/master/mathcomp/algebra/poly.v
informs us that Poly s is a polynomial with coefficients from the sequence s. It also says that p`_i is the i-th coefficient of a polynomial p. I expected the following code to output Negz 2:
Definition my_polynomial := Poly my_seq.
Eval compute in my_polynomial`_1.
The resulting term is not Negz 2, though it does have type int_Ring. There is a sequence constructor polyseq for polynomials. Indeed, the type of polyseq my_polynomial is seq int_Ring. However, doing Eval compute in (polyseq my_polynomial)`_1. gives the same mess.
In transitioning from the concrete type int to int_Ring, has the value of the integer been lost? Or, is there a way to recover the value of an int from an int_Ring? The way int_Ring is packaged, it doesn't look like it's possible, because the constructors don't reference elements. However, the same can be said of int_ZmodType. For reference, those types are defined in
https://github.com/math-comp/math-comp/blob/master/mathcomp/algebra/ssralg.v
This is not completely answering the question, but I managed to prove that the coefficient is indeed Negz 2. I give the proof here for the record. Note that I am not familiar at all with ssreflect, so there may be better and more natural ways to do this.
From mathcomp Require Import all_ssreflect.
From mathcomp Require Import all_algebra.
Open Scope ring_scope.
Definition my_seq := [:: Posz 4; Negz 2].
Eval compute in my_seq`_1.
Definition my_polynomial := Poly my_seq.
Example test : my_polynomial `_1 = Negz 2.
Proof.
cbn.
rewrite 2!polyseq_cons. cbn.
rewrite 2!size_polyC. cbn.
rewrite polyseqC. cbn. reflexivity.
Qed.
EDIT: As explained in the comments below, there exist simpler proofs of this fact.
Here's a version of your code that works:
From mathcomp Require Import all_ssreflect.
From mathcomp Require Import all_algebra.
Open Scope ring_scope.
Definition my_seq := [:: Posz 4; Negz 2].
Definition my_poly := #Polynomial _ my_seq erefl.
Compute my_poly`_1.
Instead of using the simpler Poly wrapper function defined in the library, this version calls directly the constructor of polynomial. If you look at the definition of this type, you'll see that a polynomial is simply a record containing the sequence of coefficients of the polynomial, plus a proof of a boolean equality asserting that the last element of this sequence (the leading coefficient) is not zero. (The second argument in the above expression is a proof that true = true, which is understood by Coq as the same thing as a proof of (last 1 polyseq != 0) = true, by the rules of computation.) You can check manually that there is nothing preventing the expression we're computing from reducing, so Coq is able to figure out the answer.
To see what is wrong with your original attempt, we have to unfold it a little bit. I have included the relevant definitions here in order, expanding some notations:
Poly s := foldr cons_poly (polyC 0)
polyC c := insubd poly_nil [:: c]
(* from eqtype.v *)
insubd {T : Type} {P : pred T} {sT : subType T P} u0 (x : T) : sT :=
odflt u0 (insub x)
insub {T : Type} {P : pred T} {sT : subType T P} (x : T) : option sT
:= if #idP (P x) is ReflectT Px then #Some sT (Sub x Px) else None
And here we find the culprit: Poly is defined in terms of insub, which in turn is defined by case analysis on idP, which is an opaque lemma! And Coq's reduction gets stuck when an opaque term gets in the way. (In case you are curious, what is going on here is that insub is testing, using idP, whether the leading coefficient of the polynomial is indeed different from zero, and, if so, using that fact to build the polynomial.)
The problem is that many definitions in ssreflect were not made to compute fully inside the logic. This is due to two reasons. One is performance: by allowing everything to fully reduce, we can make type checking much slower. The other is that ssreflect is tailored for convenience of reasoning, so many definitions are not the most efficient. The CoqEAL library was developed to connect definitions with better computational behavior to ones that are easier to reason about, like in ssreflect; unfortunately, I don't know if the project is still being maintained.

Defining isomorphism classes in Coq

How to define isomorphism classes in Coq?
Suppose I have a record ToyRec:
Record ToyRec {Labels : Set} := {
X:Set;
r:X->Labels
}.
And a definition of isomorphisms between two objects of type ToyRec, stating that two
objects T1 and T2 are isomorphic if there exists a bijection f:T1.(X)->T2.(X) which preserves the label of mapped elements.
Definition Isomorphic{Labels:Set} (T1 T2 : #ToyRec Labels) : Prop :=
exists f:T1.(X)->T2.(X), (forall x1 x2:T1.(X), f x1 <> f x2) /\
(forall x2:T2.(X), exists x1:T1.(X), f x1 = f x2) /\
(forall x1:T1.(X) T1.(r) x1 = T2.(r) (f x1)).
Now I would like to define a function that takes an object T1 and returns a set
containing all objects that are isomorphic to T1.
g(T1) = {T2 | Isomorphic T1 T2}
How one does such a thing in coq? I know that I might be reasoning too set theoretically
here, but what would be the right type theoretic notion of isomorphism class? Or even more basically, how one would define a set (or type) of all elemenets satisfying a given property?
It really depends on what you want to do with it. In Coq, there is a comprehension type {x : T | P x} which is the type of all elements x in type T that satisfy property P. However, it is a type, meaning that it is used to classify other terms, and not a data-structure you can compute with in the traditional sense. Thus, you can use it, for instance, to write a function on T that only works on elements that satisfy P (in which case the type of the function would be {x : T | P x} -> Y, where Y is its result type), but you can't use it to, say, write a function that computes how many elements of T satisfy P.
If you want to compute with this set, things become a bit more complicated. Let's suppose P is a decidable property so that things become a bit easier. If T is a finite type, then you can a set data-structure that has a comprehension operator (have a look at the Ssreflect library, for instance). However, this breaks when T is infinite, which is the case of your ToyRec type. As Vinz said, there's no generic way of constructively building this set as a data-structure.
Perhaps it would be easier to have a complete answer if you explained what you want to do with this type exactly.

Declaring a well colored digraph in coq

I would like to declare a structure in coq which represents a digraph which is well colored. I declared a Register which is accepted by coq if I don't have a condition. However I tried many ways of writing the condition wellColored in coq without exit. Each time I get a new error message:
The condition wellColored is the following:
for every pair of vertices $v1$, $v2$ and every edge $e$, if the source of $e$ is $v1$,
the target of $e$ is $v2$ and the color of $v1$ is $a$ then there is a color $b$ such that $b$ is different from $a$ and the color of $v2$ is $b$.
My attempt is written below. What are the mistakes in the condition wellColored and what is the correct definition?
Record dirgraph:={
V:Set;
E:Set;
s:E->V; (* source function *)
t:E->V; (* target function *)
l:V->nat;
wellColored: forall (v1:V) (v2:V) (e:E) (a:nat),
In V v1 /\ In V v2 /\ In E e /\ s e v1 /\ t e v2 /\ l v1 a-> (exists b, b<>a /\ l v2 b)
}.
For the moment I'm not interested in using packages with formalization of graphs. My main interest is to understand how to define structures and prove things about them. So I would like to define the graph precisely as it is above except with the correct condition.
The problem with your definition is that some conditions that you impose on wellColored do not need to be expressed, or simply cannot be expressed, in Coq's formalism:
I assume that by In V v1 you mean that v1 should be a member of V. Coq (as well as most type theories) is very different from usual set theory in this respect, because it doesn't make sense to assert that an object has some type as a proposition -- types in Coq (including things of type Set, like V in your definition) are not like sets in set theory. Instead, each object in the theory has its own type once and for all, and that type cannot change. When you write forall (v1 : V), ..., it is already assumed that v1 is a member of V.
If you want to say that some object of type T has some special property that not all objects of that type have (e.g., some number n is prime), you would express that with a predicate on that type (i.e., something of type T -> Prop), and not as a "subset" of T, as you would do in set theory.
You defined s, t and l as functions of one argument, but used them as two-argument functions, as in s e v1. I suppose you wanted to say something like v1 = s e, i.e. v1 is the source of edge e. However, there's no need to say that: s e is an expression of type V like any other, and can be used directly, with no need to declare additional variables (see below). Likewise, you don't need to say that there exists some color b that is different from a: you can just refer to the color of that node directly.
Here's a version of your type that avoids those problems:
Record dirgraph:={
V:Set;
E:Set;
s:E->V; (* source function *)
t:E->V; (* target function *)
color: V->nat;
wellColored:
forall e : E, color (s e) <> color (t e)
}.

Working with Isabelle's code generator: Data refinement and higher order functions

This is a follow-up on Isabelle's Code generation: Abstraction lemmas for containers?:
I want to generate code for the_question in the following theory:
theory Scratch imports Main begin
typedef small = "{x::nat. x < 10}" morphisms to_nat small
by (rule exI[where x = 0], simp)
code_datatype small
lemma [code abstype]: "small (to_nat x) = x" by (rule to_nat_inverse)
definition a_pred :: "small ⇒ bool"
where "a_pred = undefined"
definition "smaller j = [small i . i <- [0 ..< to_nat j]]"
definition "the_question j = (∀i ∈ set (smaller j). a_pred j)"
The problem is that the equation for smaller is not suitable for code generation, as it mentions the abstraction function small.
Now according to Andreas’ answer to my last question and the paper on data refinement, the next step is to introduce a type for sets of small numbers, and create a definition for smaller in that type:
typedef small_list = "{l. ∀x∈ set l. (x::nat) < 10}" by (rule exI[where x = "[]"], auto)
code_datatype Abs_small_list
lemma [code abstype]: "Abs_small_list (Rep_small_list x) = x" by (rule Rep_small_list_inverse)
definition "smaller' j = Abs_small_list [ i . i <- [0 ..< to_nat j]]"
lemma smaller'_code[code abstract]: "Rep_small_list (smaller' j) = [ i . i <- [0 ..< to_nat j]]"
unfolding smaller'_def
by (rule Abs_small_list_inverse, cases j, auto elim: less_trans simp add: small_inverse)
Now smaller' is executable. From what I understand I need to redefine operations on small list as operations on small_list:
definition "small_list_all P l = list_all P (map small (Rep_small_list l))"
lemma[code]: "the_question j = small_list_all a_pred (smaller' j)"
unfolding small_list_all_def the_question_def smaller'_code smaller_def Ball_set by simp
I can define a good looking code equation for the_question. But the definition of small_list_all is not suitable for code generation, as it mentions the abstraction morphismsmall. How do I make small_list_all executable?
(Note that I cannot unfold the code equation of a_pred, as the problem actually occurs in the code equation of the actually recursive a_pred. Also, I’d like to avoid hacks that involve re-checking the invariant at runtime.)
I don't have a good solution to the general problem, but here's an idea that will let you generate code for the_question in this particular case.
First, define a function predecessor :: "small ⇒ small with an abstract code equation (possibly using lift_definition from λn::nat. n - 1).
Now you can prove a new code equation for smaller whose rhs uses if-then-else, predecessor and normal list operations:
lemma smaller_code [code]:
"smaller j = (if to_nat j = 0 then []
else let k = predecessor j in smaller k # [k])"
(More efficient implementations are of course possible if you're willing to define an auxiliary function.)
Code generation should now work for smaller, since this code equation doesn't use function small.
The short answer is no, it does not work.
The long answer is that there are often workarounds possible. One is shown by Brian in his answer. The general idea seems to be
Separate the function that has the abstract type in covariant positions besides the final return value (i.e. higher order functions or functions returning containers of abstract values) into multiple helper functions so that abstract values are only constructed as a single return value of one of the helper function.
In Brian’s example, this function is predecessor. Or, as another simple example, assume a function
definition smallPrime :: "nat ⇒ small option"
where "smallPrime n = (if n ∈ {2,3,5,7} then Some (small n) else None)"
This definition is not a valid code equation, due to the occurrence of small. But this derives one:
definition smallPrimeHelper :: "nat ⇒ small"
where "smallPrimeHelper n = (if n ∈ {2,3,5,7} then small n else small 0)"
lemma [code abstract]: "to_nat (smallPrimeHelper n) = (if n ∈ {2,3,5,7} then n else 0)"
by (auto simp add: smallPrimeHelper_def intro: small_inverse)
lemma [code_unfold]: "smallPrime n = (if n ∈ {2,3,5,7} then Some (smallPrimeHelper n) else None)"
unfolding smallPrime_def smallPrimeHelper_def by simp
If one wants to avoid the redundant calculation of the predicate (which might be more complex than just ∈ {2,3,5,7}, one can make the return type of the helper smarter by introducing an abstract view, i.e. a type that contains both the result of the computation, and the information needed to construct the abstract type from it:
typedef smallPrime_view = "{(x::nat, b::bool). x < 10 ∧ b = (x ∈ {2,3,5,7})}"
by (rule exI[where x = "(2, True)"], auto)
setup_lifting type_definition_small
setup_lifting type_definition_smallPrime_view
For the view we have a function building it and accessors that take the result apart, with some lemmas about them:
lift_definition smallPrimeHelper' :: "nat ⇒ smallPrime_view"
is "λ n. if n ∈ {2,3,5,7} then (n, True) else (0, False)" by simp
lift_definition smallPrimeView_pred :: "smallPrime_view ⇒ bool"
is "λ spv :: (nat × bool) . snd spv" by auto
lift_definition smallPrimeView_small :: "smallPrime_view ⇒ small"
is "λ spv :: (nat × bool) . fst spv" by auto
lemma [simp]: "smallPrimeView_pred (smallPrimeHelper' n) ⟷ (n ∈ {2,3,5,7})"
by transfer simp
lemma [simp]: "n ∈ {2,3,5,7} ⟹ to_nat (smallPrimeView_small (smallPrimeHelper' n)) = n"
by transfer auto
lemma [simp]: "n ∈ {2,3,5,7} ⟹ smallPrimeView_small (smallPrimeHelper' n) = small n"
by (auto intro: iffD1[OF to_nat_inject] simp add: small_inverse)
With that we can derive a code equation that does the check only once:
lemma [code]: "smallPrime n =
(let spv = smallPrimeHelper' n in
(if smallPrimeView_pred spv
then Some (smallPrimeView_small spv)
else None))"
by (auto simp add: smallPrime_def Let_def)

How to prove code correctness lemmas with the "undefined" constant

Given I have a very simple definition for code generation. It is only defined for certain cases and throws a runtime exception otherwise.
definition "blubb a = (if P a then True else undefined)"
Now I want to show blubb correct. The case in which the exception is thrown should be ignored (from my point of view, not the mathematical point of view). However, I end up with a subgoal that assumes that some arbitrary value X is undefined. The following lemma is more or less equivalent to the subgoal. I want to show False as I want to ignore the case in which the exception is thrown (i.e., undefined is returned).
lemma "X = undefined ⟹ False"
This is not provable.
try
Nitpick found a counterexample for card 'a = 1:
Free variable:
X = a1
What is the best way to show correctness of functions that might throw exceptions or deal with undefined?
This relates to this question.
undefined is a constant in Isabelle which you don't know anything about. In particular, you can not in general prove that X ≠ undefined.
If you want to write functions that are only valid for certain inputs, you might consider using the 'a option type, as follows:
definition "blubb a ≡ (if P a then Some True else None)"
and then in your proofs assume that blubb a is defined as follows:
lemma "∃x. blubb a = Some x ⟹ Q (blubb a)"
...
or simply:
lemma "a ∈ dom blubb ⟹ Q (blubb a)"
...
The value of blubb a can then be extracted using the (blubb a).