definitions with fin types (lean) - math

I try to formalize a few statements about geometry with the help of the great mathlib. Unfortunately I'm running into some issues expressing my definitions.
import analysis.convex.basic
import analysis.convex.segment
import algebra.module.basic
import data.set.basic
import data.list.basic
import data.fin.tuple.basic
open set
section polygon
variables R M : Type*
variables [ordered_semiring R] [add_comm_monoid M] [module R M]
definition PolygonChain {n : ℕ} (V: list (M, M)) (P : set M):
∀ pair ∈ V, (segment pair.0 pair.1) ⊆ P
end polygon
What I want to say is, given a list of vertex tuples from some vector space (extreme points of a polygon chain) and a set of points (that is supposed to be the whole polygon chain), then there must be a segment between the start and end vertex from every tuple, in the set of all points from the polygon chain.
I tried different ways to build the definition and noticed, that I seem to misunderstand fin related types, so a more elaborate explanation, would be appreciated. More specific an alternative definition was
definition PolygonChain {n : ℕ} (V: vector M n) (P : set M):
∀ m ∈ (fin n), (segment (vector.nth m) (vector.nth m+1)) ⊆ P
I know that that the forall quantor is only applicable to types (so it doesn't really iterate over the range 0 < n), and saying that sth. is an element of sth. else, isn't a valid notation for that. Moreover how would I in general express statements about ranges and specific entries in vectors and lists?
Edit:
Thanks to Eric's answer I found the solution to the second definition
definition AltPolygonChain {n : ℕ} (v: vector M (n+1)) (P : set M): Prop :=
∀ m : fin (n), (segment R (vector.nth v m) (vector.nth v (m+1))) ⊆ P

This at least fixes the syntax errors in your code:
import analysis.convex.basic
import analysis.convex.segment
import algebra.module.basic
import data.set.basic
import data.list.basic
import data.fin.tuple.basic
open set
section polygon
variables R M : Type*
variables [ordered_semiring R] [add_comm_monoid M] [module R M]
definition PolygonChain {n : ℕ} (V: list (M × M)) (P : set M) : Prop :=
∀ pair : M × M, pair ∈ V → (segment R pair.1 pair.2) ⊆ P
end polygon
Some remarks:
list (M, M) should be spelt list (M × M). (a, b) is a term of type A × B.
The elements of a pair are .1 and .2, not .0 and .1.
Projection notation (.1) only works if Lean already knows the type of pair.
Segment needs an R argument.
If you're defining a property, you want def name : Prop := statement. If the statement appears before the :=, then you're proving the statement.

Related

Nested recursion and `Program Fixpoint` or `Function`

I’d like to define the following function using Program Fixpoint or Function in Coq:
Require Import Coq.Lists.List.
Import ListNotations.
Require Import Coq.Program.Wf.
Require Import Recdef.
Inductive Tree := Node : nat -> list Tree -> Tree.
Fixpoint height (t : Tree) : nat :=
match t with
| Node x ts => S (fold_right Nat.max 0 (map height ts))
end.
Program Fixpoint mapTree (f : nat -> nat) (t : Tree) {measure (height t)} : Tree :=
match t with
Node x ts => Node (f x) (map (fun t => mapTree f t) ts)
end.
Next Obligation.
Unfortunately, at this point I have a proof obligation height t < height (Node x ts) without knowing that t is a member of ts.
Similarly with Function instead of Program Fixpoint, only that Function detects the problem and aborts the definition:
Error:
the term fun t : Tree => mapTree f t can not contain a recursive call to mapTree
I would expect to get a proof obligation of In t ts → height t < height (Node x ts).
Is there a way of getting that that does not involve restructuring the function definition? (I know work-arounds that require inlining the definition of map here, for example – I’d like to avoid these.)
Isabelle
To justify that expectation, let me show what happens when I do the same in Isabelle, using the function command, which is (AFAIK) related to Coq’s Function command:
theory Tree imports Main begin
datatype Tree = Node nat "Tree list"
fun height where
"height (Node _ ts) = Suc (foldr max (map height ts) 0)"
function mapTree where
"mapTree f (Node x ts) = Node (f x) (map (λ t. mapTree f t) ts)"
by pat_completeness auto
termination
proof (relation "measure (λ(f,t). height t)")
show "wf (measure (λ(f, t). height t))" by auto
next
fix f :: "nat ⇒ nat" and x :: nat and ts :: "Tree list" and t
assume "t ∈ set ts"
thus "((f, t), (f, Node x ts)) ∈ measure (λ(f, t). height t)"
by (induction ts) auto
qed
In the termination proof, I get the assumption t ∈ set ts.
Note that Isabelle does not require a manual termination proof here, and the following definition works just fine:
fun mapTree where
"mapTree f (Node x ts) = Node (f x) (map (λ t. mapTree f t) ts)"
This works because the map function has a “congruence lemma” of the form
xs = ys ⟹ (⋀x. x ∈ set ys ⟹ f x = g x) ⟹ map f xs = map g ys
that the function command uses to find out that the termination proof only needs to consider t ∈ set ts..
If such a lemma is not available, e.g. because I define
definition "map' = map"
and use that in mapTree, I get the same unprovable proof obligation as in Coq. I can make it work again by declaring a congruence lemma for map', e.g. using
declare map_cong[folded map'_def,fundef_cong]
In this case, you actually do not need well-founded recursion in its full generality:
Require Import Coq.Lists.List.
Set Implicit Arguments.
Inductive tree := Node : nat -> list tree -> tree.
Fixpoint map_tree (f : nat -> nat) (t : tree) : tree :=
match t with
| Node x ts => Node (f x) (map (fun t => map_tree f t) ts)
end.
Coq is able to figure out by itself that recursive calls to map_tree are performed on strict subterms. However, proving anything about this function is difficult, as the induction principle generated for tree is not useful:
tree_ind :
forall P : tree -> Prop,
(forall (n : nat) (l : list tree), P (Node n l)) ->
forall t : tree, P t
This is essentially the same problem you described earlier. Luckily, we can fix the issue by proving our own induction principle with a proof term.
Require Import Coq.Lists.List.
Import ListNotations.
Unset Elimination Schemes.
Inductive tree := Node : nat -> list tree -> tree.
Set Elimination Schemes.
Fixpoint tree_ind
(P : tree -> Prop)
(IH : forall (n : nat) (ts : list tree),
fold_right (fun t => and (P t)) True ts ->
P (Node n ts))
(t : tree) : P t :=
match t with
| Node n ts =>
let fix loop ts :=
match ts return fold_right (fun t' => and (P t')) True ts with
| [] => I
| t' :: ts' => conj (tree_ind P IH t') (loop ts')
end in
IH n ts (loop ts)
end.
Fixpoint map_tree (f : nat -> nat) (t : tree) : tree :=
match t with
| Node x ts => Node (f x) (map (fun t => map_tree f t) ts)
end.
The Unset Elimination Schemes command prevents Coq from generating its default (and not useful) induction principle for tree. The occurrence of fold_right on the induction hypothesis simply expresses that the predicate P holds of every tree t' appearing in ts.
Here is a statement that you can prove using this induction principle:
Lemma map_tree_comp f g t :
map_tree f (map_tree g t) = map_tree (fun n => f (g n)) t.
Proof.
induction t as [n ts IH]; simpl; f_equal.
induction ts as [|t' ts' IHts]; try easy.
simpl in *.
destruct IH as [IHt' IHts'].
specialize (IHts IHts').
now rewrite IHt', <- IHts.
Qed.
You can now do this with Equations and get the right elimination principle automatically, using either structural nested recursion or well-founded recursion
In general, it might be advisable to avoid this problem. But if one really wants to obtain the proof obligation that Isabelle gives you, here is a way:
In Isabelle, we can give an external lemma that stats that map applies its arguments only to members of the given list. In Coq, we cannot do this in an external lemma, but we can do it in the type. So instead of the normal type of map
forall A B, (A -> B) -> list A -> list B
we want the type to say “f is only ever applied to elements of the list:
forall A B (xs : list A), (forall x : A, In x xs -> B) -> list B
(It requires reordering the argument so that the type of f can mention xs).
Writing this function is not trivial, and I found it easier to use a proof script:
Definition map {A B} (xs : list A) (f : forall (x:A), In x xs -> B) : list B.
Proof.
induction xs.
* exact [].
* refine (f a _ :: IHxs _).
- left. reflexivity.
- intros. eapply f. right. eassumption.
Defined.
But you can also write it “by hand”:
Fixpoint map {A B} (xs : list A) : forall (f : forall (x:A), In x xs -> B), list B :=
match xs with
| [] => fun _ => []
| x :: xs => fun f => f x (or_introl eq_refl) :: map xs (fun y h => f y (or_intror h))
end.
In either case, the result is nice: I can use this function in mapTree, i.e.
Program Fixpoint mapTree (f : nat -> nat) (t : Tree) {measure (height t)} : Tree :=
match t with
Node x ts => Node (f x) (map ts (fun t _ => mapTree f t))
end.
Next Obligation.
and I don’t have to do anything with the new argument to f, but it shows up in the the termination proof obligation, as In t ts → height t < height (Node x ts) as desired. So I can prove that and define mapTree:
simpl.
apply Lt.le_lt_n_Sm.
induction ts; inversion_clear H.
- subst. apply PeanoNat.Nat.le_max_l.
- rewrite IHts by assumption.
apply PeanoNat.Nat.le_max_r.
Qed.
It only works with Program Fixpoint, not with Function, unfortunately.

Defining Primtive Recursion for multiplication in Isabelle

I am new to Isabelle and I am trying to define primitive recursive functions. I have tried out addition but I am having trouble with multiplication.
datatype nati = Zero | Suc nati
primrec add :: "nati ⇒ nati ⇒ nati" where
"add Zero n = n" |
"add (Suc m) n = Suc(add m n)"
primrec mult :: "nati ⇒ nati ⇒ nati" where
"mult Suc(Zero) n = n" |
"mult (Suc m) n = add((mult m n) m)"
I get the following error for the above code
Type unification failed: Clash of types "_ ⇒ _" and "nati"
Type error in application: operator not of function type
Operator: mult m n :: nati
Operand: m :: nati
Any ideas?
The problem is your mult function: It should look like this:
primrec mult :: "nati ⇒ nati ⇒ nati" where
"mult Zero n = Zero" |
"mult (Suc m) n = add (mult m n) m"
Function application in functional programming/Lambda calculus is the operation that binds strongest and it associates to the left: something like f x y means ‘f applied to x, and the result applied to y’ – or, equivalently due to Currying: the function f applied to the parameters x and y.
Therefore, something like mult Suc(Zero) n would be read as mult Suc Zero n, i.e. the function mult would have to be a function taking three parameters, namely Suc, Zero, and n. That gives you a type error. Similarly, add ((mult m n) m) does not work, since that is identical to add (mult m n m), which would mean that add is a function taking one parameter and mult is one taking three.
Lastly, if you fix all that, you will get another error saying you have a non-primitive pattern on the left-hand side of your mult function. You cannot pattern-match on something like Suc Zero since it is not a primitive pattern. You can do that if you use fun instead of primrec, but it is not what you want to do here: You want to instead handle the cases Zero and Suc (see my solution). In your definition, mult Zero n would even be undefined.

Isabelle recursive function

I have the following recursive function that creates a list of 0s (i.e. [0,...,0]) in VDM. How can this be translated to Isabelle using fun-where?
VDM:
NewList: nat1 * seq of nat -> seq of nat
NewList(n, l) ==
if len l = n then l
else NewList(n, l ^ [0])
-- pre/post-conditions excluded here
My attempts are horribly wrong due to my lack of understanding of Isabelle (but below at least proves that I tried...).
Isabelle:
fun
NewList:: "N ⇒ (VDMNat VDMSeq) ⇒ (VDMNat VDMSeq)"
where
"NewList n [] = NewList n [0]"
| "NewList n [x] = (if len [x] = n then [x] else NewList n (x#[0]))"
| "NewList n (x # xs) = (if len (x # xs) = n then (x # xs) else NewList n ((x # xs) # [(0::VDMNat)]))"
*The data types VDMNat and VDMSeq are defined in some library. Please ignore the VDMNat and VDMSeq for now - any sort of implementation using Isabelle's data types are welcome (at least it would provide a good reference for my implementation). Please refer to the VDM code for the data types intended.
Could you also please explain what x, xs, and (x # xs) are referring to? I've seen this in several recursive function examples (though none helps me).
Thank you for your help!
First of all, x and xs are variables. When definiting recursive functions on lists, these are often used to denote the first element of the list (x) and the remaining list (xs). The expression x # xs means ‘x prepended to the list xs’, and that is the reason why (x # xs) # [0] in your question does not work: x # xs is a list and [0] is also a list. You would have to do x # xs # [0}, where # is the function to concatenate two lists.
Now, to your function: My interpretation of your function definition is that you have a natural number n and a list l and want to pad the list l with zeros at the back up to length n.
However, when the list l is of length > n to begin with, your function does not terminate. You would have to think about what to do in that case.
Here are my suggestions for what you could do:
Possibility 1
Change the = n to a ≥ n. Then you can prove termination of the function by looking at
function new_list :: "nat ⇒ nat list ⇒ nat list" where
"new_list n l = (if length l ≥ n then l else new_list n (l # [0]))"
by pat_completeness auto
termination by (relation "measure (λ(n, l). n - length l)") auto
However, proving theorems about this will probably get ugly. I would therefore urge you to do something like the following two possibilities. Ideally, use functions from Isabelle's standard library, because there is usually good automation setup for them. Alternatively, define your own small building blocks (like take and replicate) for your datatypes and prove reusable facts on them and combine them to do what you want. A ‘monolithic’ function definition like yours is difficult to work with when doing proofs.
Possibility 2
Use the builtin function replicate, which takes a natural number n and an element and returns a list of n times that element:
definition new_list :: "nat ⇒ nat list ⇒ nat list" where
"new_list n l = l # replicate (n - length l) 0"
You can also do the same thing with fun, but definition is the more low-level tool. Note that definition does not add the function definition theorem new_list_def as a simplifier rule; you can do this by writing declare new_list_def [simp].
Possibility 3
You can combine possibility 2 with the builtin function take to ensure that you always get a list of length exactly n, even when the input list is longer (it is then possibly truncated):
definition new_list :: "nat ⇒ nat list ⇒ nat list" where
"new_list n l = take n l # replicate (n - length l) 0"
Summary
In the first two cases, you can prove the theorems
length l ≤ n ⟹ length (new_list n l) = n
take (length l) (new_list n l) = l
(in the first case by induction using new_list.induct; in the second case just by unfolding the definition and simplifying)
In the third case, you can prove
length (new_list n l) = n
take (length l) (new_list n l) = take n l
Obviously, if length l ≤ n, the first two and the last one coincide completely.
The easy solution is: replicate n (0::nat) using the function replicate of Isabelle/HOL's library.
If you want to implement the function yourself via fun then do what you should always do in functional programming ;) try to split your problem into smaller problems that can be solved recursively:
fun newlist :: "nat => nat list"
where
"newlist 0 = []" -- "the only list of length 0*)
| "newlist (Suc n) = ..." -- "use result for 'n' to obtain result for 'n+1'"

Isabelle type unification/inference error

I'm just getting started in Isabelle and I'm getting a type unification error while working through Exercise 3.3 in of Concrete Semantics:
Define a substitution function
subst :: vname ⇒ aexp ⇒ aexp ⇒ aexp
such that subst x a e is the result of replacing every occurrence of variable x by a in e. For example:
subst ''x'' (N 3) (Plus (V ''x'') (V ''y'')) = Plus (N 3) (V ''y'')
Here's what I've got so far:
theory Scratchpad
imports Main
begin
type_synonym vname = string
type_synonym val = int
type_synonym state = "vname ⇒ val"
datatype aexp = N int | V vname | Plus aexp aexp
fun subst :: "vname ⇒ aexp ⇒ aexp ⇒ aexp" where
"subst x (N a) (N e) = (N e)" |
"subst x (N a) (V e) = (if x=e then (N a) else (V e))" |
"subst x (N a) (Plus e1 e2) = Plus(subst(x (N a) e1) subst(x (N a) e2))"
end
When the third case in the function definition is commented out, running the test cases
value "subst ''x'' (N 3) (N 5)"
value "subst ''x'' (N 3) (V ''x'')"
produces (N 5) and (N 3) respectively, so I know the first two lines are working correctly. Adding the last line results in the error
Type unification failed: Clash of types "_ ⇒ _" and "_ list"
Type error in application: operator not of function type
Operator: x :: char list
Operand: N a :: aexp
I don't think this is a syntax issue, although I'm not yet completely sure what purposes different types of quotation marks serve (e.g. double quotes vs. two single quotes). From this answer, I believe that Isabelle is assigning x to be a function type on the right side of the line, which is not what I want.
What do the error messages actually mean (specifically and generally), and how do I fix this?
To answer your question about quotes: Two single quotes are used in Isabelle/HOL (more precisely its inner syntax) to denote string literals. That is, by ''abc'' we denote the string containing the three characters a, b, and c (which would again use some special syntax if you had to enter them literally). Double quotes on the other hand, are mostly used to separate Isar statements (outer syntax) from terms inside the logic. So while ''...'' is part of the term language, "..." is not.
Now for the error message. It tells you that you are trying to use the list x (type _ list) as a function (type _ => _). Why does Isabelle think you want to use x as a function? Well, because juxtaposition (i.e., writing terms next to each other, separated by white space) denotes function application. Thus x (N a) is interpreted as applying a function x to an argument (N a) (just as f y is the application of f to the argument y). In order to give your definition the correct meaning you have to use parenthesis at the right positions. I guess what you intended in your third clause was:
Plus (subst x (N a) e1) (subst x (N a) e2)
where we have two occurrences of the function subst applied to three arguments. (So it was a syntax issue after all ;).)
Another comment. Your implementation of subst could be more general. As is, the second argument of subst is always fixed to be some number a (because of your usage of the constructor N). However, everything should work just as well if you would allow arbitrary expressions of type aexp.

How to obtain a FMapInterface.In from a FMapInterface.MapsTo and vice-versa?

From the manual FMapInterface.In is defined as:
Definition In (k:key)(m: t elt) : Prop := exists e:elt, MapsTo k e m.
So, I was expecting that unfolding a term In k m would yield exists e, MapsTo k e m.
However, in Coq 8.4pl4, given this:
______________________________________(1/1)
In (elt:=t) k m
performing an unfold yields
______________________________________(1/1)
Raw.In0 t (this m)
by issuing a Print M (where M is the module in question) I get
Module M
: Sig
.
.
.
End
:= (FMapAVL.Make ID)
My solution is to use lemmas find_mapsto_iff and in_find_iff (from FMapFacts), but that seems overly complicated. Why doesn't unfolding the definition work?
The instance of module FMapInterface (obtained from FMapAVL.Make) changes the definition of In, so this basic property is lost in the instance.
Instead, the result must be proved at the level of FMapInterface. The solution is to create an auxiliary module with these two properties.
Require Coq.FSets.FMapFacts.
Require Coq.FSets.FMapInterface.
Module MapUtil (Import M:FMapInterface.WS).
Module F := FMapFacts.Facts M.
Import F.
Lemma mapsto_to_in:
forall elt k e m,
MapsTo (elt:=elt) k e m ->
In k m.
Proof.
intros.
unfold In.
exists e.
assumption.
Qed.
Lemma in_to_mapsto : forall (elt:Type) m x,
In x m -> exists (e:elt), MapsTo x e m.
Proof.
intros.
unfold In in H.
assumption.
Qed.
End MapUtil.
Using the module above follows the same pattern of using Coq.FSets.FMapFacts. For example:
Require Import Coq.Structures.OrderedTypeEx. (* Imports: Nat_as_OT *)
Require Import Coq.FSets.FMapAVL. (* Imports: FMapAVL.Make *)
Module Map_Nat := FMapAVL.Make Nat_as_OT. (* Defines a map with nats as keys. *)
Module Map_Nat_Util := MapUtil Map_Nat. (* Defines an instance of module MapUtil. *)

Resources