Vector Idris:关于向量串联的证明
假设我有以下idris源代码:Vector Idris:关于向量串联的证明,vector,tensor,proof,idris,Vector,Tensor,Proof,Idris,假设我有以下idris源代码: module Source import Data.Vect --in order to avoid compiler confusion between Prelude.List.(++), Prelude.String.(++) and Data.Vect.(++) infixl 0 +++ (+++) : Vect n a -> Vect m a -> Vect (n+m) a v +++ w = v ++ w --NB: further d
module Source
import Data.Vect
--in order to avoid compiler confusion between Prelude.List.(++), Prelude.String.(++) and Data.Vect.(++)
infixl 0 +++
(+++) : Vect n a -> Vect m a -> Vect (n+m) a
v +++ w = v ++ w
--NB: further down in the question I'll assume this definition isn't needed because the compiler
-- will have enough context to disambiguate between these and figure out that Data.Vect.(++)
-- is the "correct" one to use.
lemma : reverse (n :: ns) +++ (n :: ns) = reverse ns +++ (n :: n :: ns)
lemma {ns = []} = Refl
lemma {ns = n' :: ns} = ?lemma_rhs
如图所示,引理
的基本情况是平凡的Refl
。但我似乎找不到一种方法来证明归纳的情况:repl“只是”吐出以下内容
*source> :t lemma_rhs
phTy : Type
n1 : phTy
len : Nat
ns : Vect len phTy
n : phTy
-----------------------------------------
lemma_rhs : Data.Vect.reverse, go phTy
(S (S len))
(n :: n1 :: ns)
[n1, n]
ns ++
n :: n1 :: ns =
Data.Vect.reverse, go phTy (S len) (n1 :: ns) [n1] ns ++
n :: n :: n1 :: ns
我知道phTy
代表“幻影类型”,我正在考虑的向量的隐式类型。我还了解到,go
是库函数定义的where
子句中定义的函数名reverse
问题
我怎样才能继续证明?我的归纳策略正确吗?有更好的吗
上下文
在我的一个玩具项目中,我尝试定义任意张量;具体而言,定义“完全指数收缩”似乎需要这样做。我会详细说明一下:
我定义张量的方式大致相当于
data Tensor : (rank : Nat) -> (shape : Vector rank Nat) -> Type where
Scalar : a -> Tensor Z [] a
Vector : Vect n (Tensor rank shape a) -> Tensor (S rank) (n :: shape) a
对源代码的其余部分进行了润色(因为它不相关,而且到目前为止相当长且无趣),我能够定义以下函数
contractIndex : Num a =>
Tensor (r1 + (2 + r2)) (s1 ++ (n :: n :: s2)) a ->
Tensor (r1 + r2) (s1 ++ s2) a
tensorProduct : Num a =>
Tensor r1 s1 a ->
Tensor r2 s2 a ->
Tensor (r1 + r2) (s1 ++ s2) a
contractProduct : Num a =>
Tensor (S r1) s1 a ->
Tensor (S r2) ((last s1) :: s2) a ->
Tensor (r1 + r2) ((take r1 s1) ++ s2) a
nreverse_id : (k : Nat) -> nreverse k = k
contractAllIndices : Num a =>
Tensor (nreverse k + k) (reverse ns ++ ns) a ->
Tensor Z [] a
contractAllProduct : Num a =>
Tensor (nreverse k) (reverse ns) a ->
Tensor k ns a ->
Tensor Z []
我正在做另一个
fullIndexContraction : Num a =>
Tensor r (reverse ns) a ->
Tensor r ns a ->
Tensor 0 [] a
fullIndexContraction {r = Z} {ns = []} t s = t * s
fullIndexContraction {r = S r} {ns = n :: ns} t s = ?rhs
这应该“尽可能地迭代contractProduct
(即,r
次)”;同样,可以将其定义为由尽可能多的收缩指数组成的tensorProduct
(同样,该金额应为r
)
我把所有这些都包括在内,因为在不证明上面的引理的情况下解决这个问题可能更容易:如果是这样的话,我也会完全满意。我只是觉得上面的“较短”版本可能更容易处理,因为我很确定我能自己找出缺失的部分
我使用的idris版本是1.3.2-git:PRE
(从命令行调用repl时会这样说)
编辑:xash的答案几乎涵盖了所有内容,我能够编写以下函数
contractIndex : Num a =>
Tensor (r1 + (2 + r2)) (s1 ++ (n :: n :: s2)) a ->
Tensor (r1 + r2) (s1 ++ s2) a
tensorProduct : Num a =>
Tensor r1 s1 a ->
Tensor r2 s2 a ->
Tensor (r1 + r2) (s1 ++ s2) a
contractProduct : Num a =>
Tensor (S r1) s1 a ->
Tensor (S r2) ((last s1) :: s2) a ->
Tensor (r1 + r2) ((take r1 s1) ++ s2) a
nreverse_id : (k : Nat) -> nreverse k = k
contractAllIndices : Num a =>
Tensor (nreverse k + k) (reverse ns ++ ns) a ->
Tensor Z [] a
contractAllProduct : Num a =>
Tensor (nreverse k) (reverse ns) a ->
Tensor k ns a ->
Tensor Z []
我还写了一个“花式”版本的reverse
,我们称之为fancy\u reverse
,它会自动重写结果中的nreverse k=k
。所以我试着写一个签名中没有nreverse
的函数,比如
fancy_reverse : Vect n a -> Vect n a
fancy_reverse {n} xs =
rewrite sym $ nreverse_id n in
reverse xs
contract : Num a =>
{auto eql : fancy_reverse ns1 = ns2} ->
Tensor k ns1 a ->
Tensor k ns2 a ->
Tensor Z [] a
contract {eql} {k} {ns1} {ns2} t s =
flip contractAllProduct s $
rewrite sym $ nreverse_id k in
?rhs
现在,rhs
的推断类型是Tensor(nreverse k)(反向ns2)
,我在范围内有一个k=nreverse k
的重写规则,但是我似乎不知道如何重写隐式的eql
证明来进行类型检查:我做错了什么吗?前奏曲Data.Vect.reverse
很难解释,因为在类型检查器中不会解析go
helper函数。通常的方法是将自己定义为一个更简单的反向
,而不需要在类型级别上重写
正如你们所看到的,这个定义很直接,这个等价引理不需要进一步的工作就可以解决。因此,您可能只需在fullIndexContraction
中的reverse ns
上进行匹配,如本例所示:
data Foo : Vect n Nat -> Type where
MkFoo : (x : Vect n Nat) -> Foo x
foo : Foo a -> Foo (reverse a) -> Nat
foo (MkFoo []) (MkFoo []) = Z
foo (MkFoo $ x::xs) (MkFoo $ reverse xs ++ [x]) =
x + foo (MkFoo xs) (MkFoo $ reverse xs)
对于您的评论:首先,有时必须使用len=nreverse len
,但是如果您在类型级别(通过通常的n+1=1+n
骗局)上有rewrite
,您也会遇到同样的问题(即使没有更复杂的证明,但这只是一个猜测)
vectapendassociative
实际上已经足够了:
lemma2 : Main.reverse (n :: ns1) ++ ns2 = Main.reverse ns1 ++ (n :: ns2)
lemma2 {n} {ns1} {ns2} = sym $ vectAppendAssociative (reverse ns1) [n] ns2
我试图在我的项目中实现您的答案,但有些事情让我感到困扰。第一个是,我必须证明(并一遍又一遍地重写)nreverse k=k
;这并不困难,但可能会使代码更不可读。或者,我可以通过在其定义中重写这样的证明来改变反转的类型:这可能会在这方面节省我的时间。第二,也是更重要的,是它不像我期望的那样容易证明(反转n::ns1)++ns2=(反转ns1)++(n::ns2)
:我可能遗漏了一些东西,我很高兴听到你对这件事的看法。(我尝试使用++
是关联的这一事实,但显然这对编译器来说还不够)更新了我的答案,因为vectasociate部分在这里不适合提交。非常感谢您的解释!我仍然有一个小问题,它将不适合在评论。为了完整起见,我将对问题进行编辑,对其进行详细说明。eql
问题正是“rewrite
在类型级别上”。有时使用显式的replace
至少有助于了解发生了什么,但通常您希望使用简单的函数,如reverse
,或者更简单但更详细的使用数据类型证明IsReversed ns1 ns2
。顺便说一句,如果您只是对键入(nrevert k)(revert xs)
感到厌烦,请记住您可以使用辅助类型,例如:翻转:(k:Nat)->Vect k Nat->Type->Type;翻转的kxs a=张量(nreverse k)(反向xs)a