I have the following setup:
data Shape : ( n : Nat ) -> Type where
Nil : Shape Z
(::) : ( k : Nat ) -> { auto prf : IsSucc k } -> ( xs : Shape n ) -> Shape ( S n )
record Tensor ( n : Nat ) ( a : Type ) where
constructor MkTensor
shape : ( Shape n )
arr : Vect ( count shape ) a
count : Shape n -> Nat
count Nil = 1
count ( x :: xs ) = x * count xs
I also have a proof for the theorem:
countIsNonZero : ( shape : Shape n ) -> IsSucc ( count shape )
countIsNonZero Nil = ItIsSucc
countIsNonZero ( (::) x { prf } xs ) = helper x {p1 = prf} ( count xs ) {p2 = countIsNonZero xs}
where
helper2 : ( k : Nat ) -> { auto p : IsSucc k } -> ( x : Nat ) -> IsSucc ( x + k )
helper2 k {p} Z = p
helper2 k {p} ( S l ) = ItIsSucc
helper : ( a : Nat ) -> { auto p1 : IsSucc a } -> ( b : Nat ) -> { auto p2 : IsSucc b } -> IsSucc ( a * b )
helper ( S a ) b {p1} {p2} = rewrite plusCommutative b ( a * b ) in helper2 b {p = p2} ( a * b )
Now, I want to use it to create an indexing function, which informally has the following signature:
index : Tensor n a -> Fin t1 -> ... -> Fin tn -> a
where t1...tn are values in shape.
I wrote a simple helper for that purpose, namely:
getShapeFunctionType : ( Shape n ) -> ( Nat -> Type ) -> Type -> Type
getShapeFunctionType Nil f r = r
getShapeFunctionType ( x :: xs ) f r = ( ( f x ) -> getShapeFunctionType xs f r )
index : { a : Type } -> ( t : Tensor n a ) -> getShapeFunctionType ( shape t ) ( \i : Nat => Fin i ) a
index { a } tn = getIndexFunction ( shape tn )
where
getIndexFunction : ( sh : Shape n ) -> getShapeFunctionType sh ( \i : Nat => Fin i ) a
getIndexFunction Nil = Data.Vect.index ( fromInteger 0 {prf=?h1} ) ( arr tn )
getIndexFunction ( x :: xs ) = ?h2
The first problem I met is that I do not know how to convert the theorem I have to the hole h1, i.e. how to tell Idris that since count shape is never equal to 0, we are always able to get zeroth element of Vect ( count shape ).
Related
I was trying to prove that replicate1 works correctly by showing that all elements of replicate1 n x are x:
all1 : (p : a -> Bool) -> List a -> Bool
all1 p [] = True
all1 p (x :: xs) = p x && all1 p xs
replicate1 : (n: Nat) -> a -> List a
replicate1 Z x = [x]
replicate1 (S k) x = x :: replicate1 k x
all_replicate_is_x : Eq a => {x: a} -> all1 (== x) (replicate1 n x) = True
all_replicate_is_x {n = Z} = ?hole
all_replicate_is_x {n = (S k)} = ?all_replicate_is_x_rhs_2
The base case hole is
Test.hole [P]
`-- a : Type
constraint : Eq a
x : a
-----------------------------------------
Test.hole : x == x && Delay True = True
How to prove this?
I defined n-dimensional vectors in Idris as follows:
import Data.Vect
NDVect : (Num t) => (rank : Nat) -> (shape : Vect rank Nat) -> (t : Type) -> Type
NDVect Z [] t = t
NDVect (S n) (x::xs) t = Vect x (NDVect n xs t)
Then I defined the following function which maps a function f to every entry in the tensor.
iterateT : (f : t -> t') -> (v : NDVect r s t) -> NDVect r s t'
iterateT {r = Z} {s = []} f v = f v
iterateT {r = S n} {s = x::xs} f v = map (iterateT f) v
But when I try to call iteratorT in the following function:
scale : Num t => (c : t) -> (v : NDVect rank shape t) -> NDVect rank shape t
scale c v = iterateT (*c) v
I get the following error message saying there is a type mismatched, which seems pretty fine to me
When checking right hand side of scale with expected type
NDVect rank shape t
When checking argument v to function Main.iterateT:
Type mismatch between
NDVect rank shape t (Type of v)
and
NDVect r s t (Expected type)
Specifically:
Type mismatch between
NDVect rank shape t
and
NDVect r s t
Specifically:
Type mismatch between
NDVect rank shape t
and
NDVect r s t
I have also been wondering how to express n-dimensional vectors (i.e. tensors) in Idris. I had a play with the type definition in the question, but encountered various issues, so I expressed the NDVect function as a data type:
data NDVect : (rank : Nat) -> (shape : Vect rank Nat) -> Type -> Type where
NDVZ : (value : t) -> NDVect Z [] t
NDV : (values : Vect n (NDVect r s t)) -> NDVect (S r) (n::s) t
And implemented map as follows:
nmap : (t -> u) -> (NDVect r s t) -> NDVect r s u
nmap f (NDVZ value) = NDVZ (f value)
nmap f (NDV values) = NDV (map (nmap f) values)
The following now works:
*Main> NDVZ 5
NDVZ 5 : NDVect 0 [] Integer
*Main> nmap (+4) (NDVZ 5)
NDVZ 9 : NDVect 0 [] Integer
*Main> NDV [NDVZ 1, NDVZ 2, NDVZ 3]
NDV [NDVZ 1, NDVZ 2, NDVZ 3] : NDVect 1 [3] Integer
*Main> nmap (+4) (NDV [NDVZ 1, NDVZ 2, NDVZ 3])
NDV [NDVZ 5, NDVZ 6, NDVZ 7] : NDVect 1 [3] Integer
Unfortunately, having all the type constructors makes things a bit ugly. I'd love to know if there's a cleaner way to solve this.
Edit:
Here's a slightly shorter type signature that doesn't explicitly encode the tensor rank in the type:
data NDVect : (shape : List Nat) -> Type -> Type where
NDVZ : (value : t) -> NDVect [] t
NDV : (values : Vect n (NDVect s t)) -> NDVect (n::s) t
nmap : (t -> u) -> (NDVect s t) -> NDVect s u
nmap f (NDVZ value) = NDVZ (f value)
nmap f (NDV values) = NDV (map (nmap f) values)
Why does I confuse auto? Deleting it makes everything work.
module Main
import Data.Vect
%default total
data LessThan : String -> String -> Type where
D : (x < x' = True) -> LessThan x x'
I : LessThan x x' -> LessThan x' x'' -> LessThan x x'' -- Deleting this line fixes the error
data OrderedStringVect : Vect n String -> Type where
Nil : OrderedStringVect []
OAddF : (x : String) -> OrderedStringVect [] -> OrderedStringVect [x] -- Adding the first element does not require a LT from
OAddO : (x : String) -> OrderedStringVect (x'::xs) -> {auto p : LessThan x x'} -> OrderedStringVect (x :: x' :: xs) -- Adding another element does
data InsertProof : String -> Vect n String -> Type where
IPF : InsertProof n [] -- There is no first element
IPH : LessThan n n' -> InsertProof n (n' :: ns) -- Less the the first element
IPL : LessThan n' n -> InsertProof n ns -> InsertProof n (n' :: ns) -- Greater than the first element
-- Eq is not allowed
insertT : (n : String) -> (ns : Vect m String) -> {auto p : InsertProof n ns} -> Vect (S m) String
insertT {p = IPF} n [] = n :: []
insertT {p = IPH _} n (n'::ns) = n :: n' :: ns
insertT {p = IPL _ _} n (n'::ns) = n' :: insertT n ns
(::) : (n : String) -> OrderedStringVect ns -> {auto p : InsertProof n ns} -> OrderedStringVect (insertT {p=p} n ns)
(::) {p = IPF} n Nil = OAddF n Nil
(::) {p = IPH _} n (OAddF n' []) = OAddO n (OAddF n' [])
(::) {p = IPH _} n (OAddO n' ns) = OAddO n (OAddO n' ns)
(::) {p = IPL _ IPF} n (OAddF n' Nil) = OAddO n' (OAddF n Nil)
(::) {p = IPL _ (IPH _)} n (OAddO n' ns) = OAddO n' (OAddO n ns)
(::) {p = IPL _ (IPL _ _)} n (OAddO n' ns) = OAddO n' (n :: ns)
test : with Main ["foo", "bar", "biz"] = ["bar", "biz", "foo"] -- Prove order does not matter
test = Refl
Error:
Type checking ./main7.idr
main7.idr:32:58-60:When checking right hand side of Main.:: with expected type
OrderedStringVect (insertT n (n' :: x' :: xs))
When checking argument p to Main.:::
Can't find a value of type
InsertProof n (x' :: xs)
main7.idr:34:6:When checking type of Main.test:
Can't disambiguate name: Prelude.List.::, Main.::, Prelude.Stream.::, Data.Vect.::
Edit: I have decided to do something completely different so I no longer need the answer, but would still like to know.
Following my other question, I tried to implement the actual exercise in Type-Driven Development with Idris for same_cons to prove that, given two equal lists, prepending the same element to each list results in two equal lists.
Example:
prove that 1 :: [1,2,3] == 1 :: [1,2,3]
So I came up with the following code that compiles:
sameS : {xs : List a} -> {ys : List a} -> (x: a) -> xs = ys -> x :: xs = x :: ys
sameS {xs} {ys} x prf = cong prf
same_cons : {xs : List a} -> {ys : List a} -> xs = ys -> x :: xs = x :: ys
same_cons prf = sameS _ prf
I can call it via:
> same_cons {x=5} {xs = [1,2,3]} {ys = [1,2,3]} Refl
Refl : [5, 1, 2, 3] = [5, 1, 2, 3]
Regarding the cong function, my understanding is that it takes a proof, i.e. a = b, but I don't understand its second argument: f a.
> :t cong
cong : (a = b) -> f a = f b
Please explain.
If you have two values u : c and v : c, and a function f : c -> d, then if you know that u = v, it has to follow that f u = f v, following simply from referential transparency.
cong is the proof of the above statement.
In this particular use case, you are setting (via unification) c and d to List a, u to xs, v to ys, and f to (:) x, since you want to prove that xs = ys -> (:) x xs = (:) x ys.
I'm implementing in Idris the algorithm and proofs of first-order unification by structural recursion (current status of the development available here).
Idris in giving me the following error message
`-- UnifyProofs.idr line 130 col 60:
When checking right hand side of maxEquiv with expected type
(Max p n f -> Max q n f, Max q n f -> Max p n f)
When checking argument b to constructor Builtins.MkPair:
No such variable k
when it tries to type check the following function
maxEquiv : p .=. q -> Max p .=. Max q
maxEquiv pr n f = ( \ a => ( fst (pr n f) (fst a)
, \ n1 => \ g => \pr1 => (snd a) n1 g
(snd (pr n1 g) pr1))
, \ a' => (snd (pr n f) (fst a')
, \ n2 => \ g' => \ pr2 => (snd a') n2 g'
(fst (pr n2 g') pr2)))
where Max and .=. are defined as
Property : (m : Nat) -> Type
Property m = (n : Nat) -> (Fin m -> Term n) -> Type
infix 5 .=.
(.=.) : Property m -> Property m -> Type
(.=.) {m = m} P Q = (n : Nat) -> (f : Fin m -> Term n) ->
Pair (P n f -> Q n f)
(Q n f -> P n f)
Max : (p : Property m) -> Property m
Max {m = m} p = \n => \f => (p n f , (k : Nat) -> (f' : Fin m -> Term k) ->
p k f' -> f' .< f)
I've tried to pass all function arguments explicitly in order to avoid problems with implicit argument inference. But the error persists. Could someone provide me some tip on how can I solve this?
Here is the solution to my question:
Max : (p : Property m) -> Property m
Max {m = m} p = \n => \f => (p n f , (k : Nat) -> (f' : Fin m -> Term k) -> p k f' -> f' .< f)
applySnd : Max {m = m} p n f -> (k : Nat) -> (f' : Fin m -> Term k) -> p k f' -> f' .< f
applySnd = snd
maxEquiv : p .=. q -> Max p .=. Max q
maxEquiv pr n f = ( \ a => ( fst (pr n f) (fst a)
, \ n1 => \ g => \pr1 => (applySnd a) n1 g (snd (pr n1 g) pr1))
, \ a' => (snd (pr n f) (fst a
, \ n2 => \ g' => \ pr2 => (applySnd a') n2 g' (fst (pr n2 g') pr2)))
I just have used a function applySnd to make the same thing as snd. I do not know why this is necessary... Probably a Idris bug...