2016-12-26 7 views
1

컴파일되지 않습니다 :최소한의 Numeric.AD 예 내가 Numeric.AD에서 다음과 같은 최소한의 예를 컴파일하려고

import Numeric.AD 
timeAndGrad f l = grad f l 
main = putStrLn "hi" 

을하고이 오류로 실행 :

test.hs:3:24: 
    Couldn't match expected type ‘f (Numeric.AD.Internal.Reverse.Reverse 
             s a) 
            -> Numeric.AD.Internal.Reverse.Reverse s a’ 
       with actual type ‘t’ 
     because type variable ‘s’ would escape its scope 
    This (rigid, skolem) type variable is bound by 
     a type expected by the context: 
     Data.Reflection.Reifies s Numeric.AD.Internal.Reverse.Tape => 
     f (Numeric.AD.Internal.Reverse.Reverse s a) 
     -> Numeric.AD.Internal.Reverse.Reverse s a 
     at test.hs:3:19-26 
    Relevant bindings include 
     l :: f a (bound at test.hs:3:15) 
     f :: t (bound at test.hs:3:13) 
     timeAndGrad :: t -> f a -> f a (bound at test.hs:3:1) 
    In the first argument of ‘grad’, namely ‘f’ 
    In the expression: grad f l 

단서로 왜 이런 일이 일어나는가?

grad :: (Traversable f, Num a) => (forall s. Reifies s Tape => f (Reverse s a) -> Reverse s a) -> f a -> f a

을하지만 실제로 내 코드에서 같은 것을 할 필요가 : 앞의 예에서보고에서 나는이 grad의 유형을 "병합"것을 수집합니다. 사실, 이것은 컴파일하지 않는 가장 최소의 예입니다. 내가 원하는 더 복잡한 일은 다음과 같습니다.

example :: SomeType 
example f x args = (do stuff with the gradient and gradient "function") 
    where gradient = grad f x 
      gradientFn = grad f 
      (other where clauses involving gradient and gradient "function") 

여기에는 컴파일하는 형식 시그니처가있는 약간 더 복잡한 버전이 있습니다.

{-# LANGUAGE RankNTypes #-} 

import Numeric.AD 
import Numeric.AD.Internal.Reverse 

-- compiles but I can't figure out how to use it in code 
grad2 :: (Show a, Num a, Floating a) => (forall s.[Reverse s a] -> Reverse s a) -> [a] -> [a] 
grad2 f l = grad f l 

-- compiles with the right type, but the resulting gradient is all 0s... 
grad2' :: (Show a, Num a, Floating a) => ([a] -> a) -> [a] -> [a] 
grad2' f l = grad f' l 
     where f' = Lift . f . extractAll 
     -- i've tried using the Reverse constructor with Reverse 0 _, Reverse 1 _, and Reverse 2 _, but those don't yield the correct gradient. Not sure how the modes work 

extractAll :: [Reverse t a] -> [a] 
extractAll xs = map extract xs 
      where extract (Lift x) = x -- non-exhaustive pattern match 

dist :: (Show a, Num a, Floating a) => [a] -> a 
dist [x, y] = sqrt(x^2 + y^2) 

-- incorrect output: [0.0, 0.0] 
main = putStrLn $ show $ grad2' dist [1,2] 

그러나, 나는 Reverse s a 처리하는 방법을 모르기 때문에 코드에서 첫 번째 버전, grad2을 사용하는 방법을 알아낼 수 없습니다. 두 번째 버전 인 grad2'은 내부 생성자 Lift을 사용하여 Reverse s a을 만들었으므로 올바른 유형을 가지고 있지만 출력 그래디언트가 모두 0이기 때문에 내부 (특히 매개 변수 s)의 작동 방식을 이해하면 안됩니다. 다른 생성자 Reverse (여기에 표시되지 않음)을 사용하면 잘못된 그래디언트가 생성됩니다.

다른 방법으로 사람들이 ad 코드를 사용한 라이브러리/코드의 예가 있습니까? 내 유스 케이스는 매우 일반적인 것 같아요.

+2

timeAndGrad에 유형 서명을 제공하면 어떻게됩니까? 랭크 1 방식에 더 많은 행운이있을 수 있습니다. – ocharles

+0

형식 서명과 다른 접근 방식을 추가하기 위해 제 질문을 편집했습니다 (또한 작동하지 않음). – kye

답변

2

where f' = Lift . f . extractAll으로 모든 파생어를 버리고 상수 값만 유지하는 자동 차별화 기본 유형에 백도어를 만듭니다. grad에이 값을 사용하면 결과가 0입니다. 당신은 정말 자동 차별화를 사용하는 더 복잡한 아무것도 알 필요가 없습니다

dist :: Floating a => [a] -> a 
dist [x, y] = sqrt $ x^2 + y^2 
-- preferrable is of course `dist = sqrt . sum . map (^2)` 

main = print $ grad dist [1,2] 
-- output: [0.4472135954999579,0.8944271909999159] 

:

분별있는 방법은 그대로 단지 grad을 사용하는 것입니다. Num 또는 Floating - 다형 함수 만 구별하면 모든 것이 그대로 작동합니다. 인수로 전달 된 함수를 차별화해야하는 경우 해당 인수를 2 차 다형성으로 만들 필요가 있습니다 (대안은 ad 함수의 순위 1 버전으로 전환하는 것이지만 그다지 우아하지 않고 정말로 당신에게 많은 것을 얻지는 않습니다).

{-# LANGUAGE Rank2Types, UnicodeSyntax #-} 

mainWith :: (∀n . Floating n => [n] -> n) -> IO() 
mainWith f = print $ grad f [1,2] 

main = mainWith dist 
+0

예, 인수로 전달 된 함수를 구별해야합니다. "그 논점을 다형성 2 등급으로 만드는 것"이 ​​의미하는 것을 조금 더 설명 할 수 있습니까? 나는 또한 grad의 rank-1 버전으로 전환을 시도했는데, 함수에 유형이 있음을 지정해야합니다 ([Forward a] -> Forward a). – kye

+0

여기에서 사용하는 형식은 (Num a => [a] -> a) 형식의 함수를 허용하지 않지만 코드에서이를 전달할 수 있습니다. 왜 형식이 이런 식으로 행동하는지 잘 모르겠습니다. – kye