Как правильно сопоставлять типы при использовании Numeric.AD в Haskell?
Я пытаюсь реализовать алгоритм поиска корня Ньютона-Рафсона с помощью рекламного пакета, но не могу правильно сопоставить типы функций. Я знаю, что есть правильный ответ на подобный вопрос, на который ответил сам создатель рекламы, но пакет сильно изменился с версии 1.0.6 (текущая версия 4.3.4).
Этот первый минимальный пример компилируется и работает, когда я перебираю его:
import Numeric.AD
import Numeric.AD.Internal.Forward
g :: Fractional a => a -> a
g x = - x + 2
g' :: Fractional a => a -> a
g' x = diff g x
newtonG :: Fractional a => a -> a
newtonG x = x - (g x) / (g' x)
Но если я попытаюсь абстрагировать функцию, вот так:
import Numeric.AD
import Numeric.AD.Internal.Forward
g :: Fractional a => a -> a
g x = - x + 2
newton :: Fractional a => (a -> a) -> a -> a
newton f x = x - (f x) / (diff f x)
GHC возвращает следующую ошибку:
fsolve.hs:8:32: error:
* Couldn't match type `a' with `AD s (Forward a)'
`a' is a rigid type variable bound by
the type signature for:
newton :: forall a. Fractional a => (a -> a) -> a -> a
at fsolve.hs:7:11
Expected type: AD s (Forward a) -> AD s (Forward a)
Actual type: a -> a
* In the first argument of `diff', namely `f'
In the second argument of `(/)', namely `(diff f x)'
In the second argument of `(-)', namely `(f x) / (diff f x)'
* Relevant bindings include
x :: a (bound at fsolve.hs:8:10)
f :: a -> a (bound at fsolve.hs:8:8)
newton :: (a -> a) -> a -> a (bound at fsolve.hs:8:1)
Если я использую Numeric.AD.Rank1.Forward
вместо Numeric.AD
компилятор говорит, что не может совпадать a
с Forward a
, вместо a
с AD s (Forward a)
, Я также попробовал несколько способов создания двойного числа из x
передать это f
например, snd . unbundle . f $ bundle x 1
, но это работает только если я создаю новый g' x
используя его, как в первом случае. Используя это в newton
тоже не работает.
В Numeric.AD
, diff :: Num a => (forall s. AD s (Forward a) -> AD s (Forward a)) -> a -> a
, И в Numeric.AD.Rank1.Forward
, его diff :: Num a => (Forward a -> Forward a) -> a -> a
, Так почему же они принимают функцию типа a -> a
в первом случае, но не во втором? Помимо использования полиморфных функций, я должен проявить особую осторожность при создании функций для использования с Numeric.AD
? Наконец, как мне изменить мой код, чтобы он работал? Я знаю, что в пакете уже есть функция поиска корней, но я пока не хочу ее использовать (поскольку я все еще изучаю Haskell), и, глядя на документацию, пытаюсь разобраться в этом, я чувствую, что бегаю по кругу.
1 ответ
Обратите внимание, что ваша функция:
newton :: Fractional a => (a -> a) -> a -> a
newton f x = x - (f x) / (diff f x)
использует аргумент функции f
в двух местах. Во-первых, подвыражение f x
использования f
с типом:
f :: Fractional a => a -> a
Во-вторых, из-за использования diff
подвыражение diff f x
использования f
с типом:
f :: forall s a. Fractional a => AD s (Forward a) -> AD s (Forward a)
Полученное сообщение об ошибке - система типов, которая отмечает, что это разные типы, которые нельзя объединить.
Решением является явное количественное определение аргумента функции newton
работать для всех типов, удовлетворяющих соответствующим ограничениям класса числовых типов. Это требует RankNTypes
расширение языка:
{-# LANGUAGE RankNTypes #-}
newton
:: Fractional a
=> (forall b. Fractional b => b -> b)
-> a
-> a
newton f x = x - (f x) / (diff f x)