2016-12-26 10 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 «s:

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

, но я на самом деле нужно сделать что-то подобное в моем коде. На самом деле это самый минимальный пример, который не будет компилироваться. Чем сложнее вещь, которую я хочу сделать что-то вроде этого:

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] 

Однако, я не могу понять, как использовать первый вариант, grad2, в коде, потому что я не знаю, как бороться с Reverse s a. Вторая версия, grad2', имеет правильный тип, потому что я использую внутренний конструктор Lift для создания Reverse s a, но я не должен понимать, как работает внутренняя часть (в частности параметр s), потому что выходной градиент - это все 0s. Использование другого конструктора Reverse (не показано здесь) также создает неправильный градиент.

В качестве альтернативы существуют примеры библиотек/кода, в которых люди использовали код ad? Я думаю, что мой прецедент очень распространен.

+2

Что произойдет, если вы предоставите знак типа timeAndGrad? У вас может быть больше удачи с подходом ранга-1. – ocharles

+0

Я отредактировал мой вопрос, чтобы добавить подпись типа и другой подход (который также не работает). – kye

ответ

2

С where f' = Lift . f . extractAll вы по существу создаете заднюю дверь в базовый тип автоматической дифференциации, который отбрасывает все производные и сохраняет только постоянные значения. Если вы затем используете это для grad, то неудивительно, что вы получаете нулевой результат!

Разумный способ это просто использовать grad как это:

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] 

Вам не нужно знать ничего более сложного, чтобы использовать автоматическую дифференциацию. Пока вы только различаете Num или Floating -полиморфные функции, все будет работать как есть. Если вам нужно дифференцировать функцию, переданную в качестве аргумента, вам нужно сделать этот аргумент rank-2 polymorphic (альтернативой было бы перейти на версию ранга 1 функций ad, но я полагаю, что это менее элегантно и на самом деле вам не очень много).

{-# LANGUAGE Rank2Types, UnicodeSyntax #-} 

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

main = mainWith dist 
+0

Да, мне нужно дифференцировать функцию, переданную в качестве аргумента. Можете ли вы объяснить немного больше того, что вы подразумеваете под «сделать этот аргумент rank-2 полиморфным?» Я также попытался переключиться на версию ранга ранга 1, и мне нужно указать, что функция имеет тип ([Forward a] -> Forward a). – kye

+0

Тип не позволяет использовать функцию с типом (Num a => [a] -> a), но я могу передать это в коде. Я не уверен, почему типы ведут себя таким образом. – kye

 Смежные вопросы

  • Нет связанных вопросов^_^