{-# LANGUAGE BangPatterns #-}
module FSRS.Algorithm
(
retrievability
, retrievabilityDerivative
, nextIntervalDays
, initialDifficulty
, nextDifficulty
, initialStability
, stabilityAfterReview
, transitionCoefficient
, nextStability
, nextMemoryState
, replayReviews
, stabilityMin
, stabilityMax
, difficultyMin
, difficultyMax
, minimumIntervalDays
, maximumIntervalDays
) where
import FSRS.Parameters
( CurveWeights (..)
, Parameters
, StabilityWeights (..)
, curveWeights
, difficultyDelta
, initialDifficultyBase
, initialDifficultyRate
, initialStabilityWeight
, longTermWeights
, shortTermWeights
, transitionAmplitude
, transitionRate
)
import FSRS.Types
( Days
, Difficulty
, MemoryState (..)
, Rating (..)
, Retrievability
, Stability
, ratingToInt
)
stabilityMin :: Stability
stabilityMin :: Double
stabilityMin = Double
1.0e-4
stabilityMax :: Stability
stabilityMax :: Double
stabilityMax = Double
36500.0
difficultyMin :: Difficulty
difficultyMin :: Double
difficultyMin = Double
1.0
difficultyMax :: Difficulty
difficultyMax :: Double
difficultyMax = Double
10.0
minimumIntervalDays :: Days
minimumIntervalDays :: Double
minimumIntervalDays = Double
1.0 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
86400.0
maximumIntervalDays :: Days
maximumIntervalDays :: Double
maximumIntervalDays = Double
36500.0
clampTo :: Double -> Double -> Double -> Double
clampTo :: Double -> Double -> Double -> Double
clampTo Double
lo Double
hi Double
x = Double -> Double -> Double
forall a. Ord a => a -> a -> a
min Double
hi (Double -> Double -> Double
forall a. Ord a => a -> a -> a
max Double
lo Double
x)
retrievability :: Parameters -> Days -> Stability -> Retrievability
retrievability :: Parameters -> Double -> Double -> Double
retrievability Parameters
params Double
t Double
s = (Double
weight1 Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
r1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
weight2 Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
r2) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ (Double
weight1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
weight2)
where
cw :: CurveWeights
cw = Parameters -> CurveWeights
curveWeights Parameters
params
tOverS :: Double
tOverS = Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
s
powerLaw :: Double -> Double -> Double
powerLaw Double
base Double
decay = (Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
factor Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
tOverS) Double -> Double -> Double
forall a. Floating a => a -> a -> a
** Double
decay
where
factor :: Double
factor = Double
base Double -> Double -> Double
forall a. Floating a => a -> a -> a
** (Double
1 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
decay) Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
1
r1 :: Double
r1 = Double -> Double -> Double
powerLaw (CurveWeights -> Double
cwBase1 CurveWeights
cw) (CurveWeights -> Double
cwDecay1 CurveWeights
cw)
r2 :: Double
r2 = Double -> Double -> Double
powerLaw (CurveWeights -> Double
cwBase2 CurveWeights
cw) (CurveWeights -> Double
cwDecay2 CurveWeights
cw)
weight1 :: Double
weight1 = CurveWeights -> Double
cwWeight1 CurveWeights
cw Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
s Double -> Double -> Double
forall a. Floating a => a -> a -> a
** Double -> Double
forall a. Num a => a -> a
negate (CurveWeights -> Double
cwStabilityPower1 CurveWeights
cw)
weight2 :: Double
weight2 = CurveWeights -> Double
cwWeight2 CurveWeights
cw Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
s Double -> Double -> Double
forall a. Floating a => a -> a -> a
** CurveWeights -> Double
cwStabilityPower2 CurveWeights
cw
retrievabilityDerivative :: Parameters -> Days -> Stability -> Double
retrievabilityDerivative :: Parameters -> Double -> Double -> Double
retrievabilityDerivative Parameters
params Double
t Double
s =
(Double
weight1 Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
d1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
weight2 Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
d2) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ (Double
weight1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
weight2)
where
cw :: CurveWeights
cw = Parameters -> CurveWeights
curveWeights Parameters
params
tOverS :: Double
tOverS = Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
s
slope :: Double -> Double -> Double
slope Double
base Double
decay = Double
decay Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
inner Double -> Double -> Double
forall a. Floating a => a -> a -> a
** (Double
decay Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
1) Double -> Double -> Double
forall a. Num a => a -> a -> a
* (Double
factor Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
s)
where
factor :: Double
factor = Double
base Double -> Double -> Double
forall a. Floating a => a -> a -> a
** (Double
1 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
decay) Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
1
inner :: Double
inner = Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
factor Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
tOverS
d1 :: Double
d1 = Double -> Double -> Double
slope (CurveWeights -> Double
cwBase1 CurveWeights
cw) (CurveWeights -> Double
cwDecay1 CurveWeights
cw)
d2 :: Double
d2 = Double -> Double -> Double
slope (CurveWeights -> Double
cwBase2 CurveWeights
cw) (CurveWeights -> Double
cwDecay2 CurveWeights
cw)
weight1 :: Double
weight1 = CurveWeights -> Double
cwWeight1 CurveWeights
cw Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
s Double -> Double -> Double
forall a. Floating a => a -> a -> a
** Double -> Double
forall a. Num a => a -> a
negate (CurveWeights -> Double
cwStabilityPower1 CurveWeights
cw)
weight2 :: Double
weight2 = CurveWeights -> Double
cwWeight2 CurveWeights
cw Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
s Double -> Double -> Double
forall a. Floating a => a -> a -> a
** CurveWeights -> Double
cwStabilityPower2 CurveWeights
cw
nextIntervalDays :: Parameters -> Retrievability -> Stability -> Days
nextIntervalDays :: Parameters -> Double -> Double -> Double
nextIntervalDays Parameters
params Double
target Double
s
| Bool -> Bool
not (Double
target Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
> Double
0) = Double
maximumIntervalDays
| Double
target Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
>= Double
1 = Double
minimumIntervalDays
| Parameters -> Double -> Double -> Double
retrievability Parameters
params Double
lo Double
s Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
<= Double
target = Double
lo
| Parameters -> Double -> Double -> Double
retrievability Parameters
params Double
hi Double
s Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
>= Double
target = Double
hi
| Bool
otherwise = Double -> Double
forall a. Floating a => a -> a
exp (Int -> Double -> Double -> Double -> Double
search (Int
0 :: Int) (Double -> Double
forall a. Floating a => a -> a
log Double
lo) (Double -> Double
forall a. Floating a => a -> a
log Double
hi) Double
u0)
where
lo :: Double
lo = Double
minimumIntervalDays
hi :: Double
hi = Double
maximumIntervalDays
u0 :: Double
u0 = Double -> Double -> Double -> Double
clampTo (Double -> Double
forall a. Floating a => a -> a
log Double
lo) (Double -> Double
forall a. Floating a => a -> a
log Double
hi) (Double -> Double
forall a. Floating a => a -> a
log Double
s)
maxIterations :: Int
maxIterations = Int
200 :: Int
tolerance :: Double
tolerance = Double
1.0e-15
search :: Int -> Double -> Double -> Double -> Double
search !Int
n !Double
a !Double
b !Double
u
| Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
maxIterations = Double
u
| Double
fu Double -> Double -> Bool
forall a. Eq a => a -> a -> Bool
== Double
0 = Double
u
| Bool
converged = Double
next
| Bool
otherwise = Int -> Double -> Double -> Double -> Double
search (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Double
a' Double
b' Double
next
where
t :: Double
t = Double -> Double
forall a. Floating a => a -> a
exp Double
u
fu :: Double
fu = Parameters -> Double -> Double -> Double
retrievability Parameters
params Double
t Double
s Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
target
(Double
a', Double
b') = if Double
fu Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
> Double
0 then (Double
u, Double
b) else (Double
a, Double
u)
slope :: Double
slope = Parameters -> Double -> Double -> Double
retrievabilityDerivative Parameters
params Double
t Double
s Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
t
newton :: Double
newton = Double
u Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
fu Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
slope
next :: Double
next
| Double -> Bool
forall a. RealFloat a => a -> Bool
isNaN Double
newton Bool -> Bool -> Bool
|| Double -> Bool
forall a. RealFloat a => a -> Bool
isInfinite Double
newton Bool -> Bool -> Bool
|| Double
newton Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
<= Double
a' Bool -> Bool -> Bool
|| Double
newton Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
>= Double
b' =
Double
0.5 Double -> Double -> Double
forall a. Num a => a -> a -> a
* (Double
a' Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
b')
| Bool
otherwise = Double
newton
converged :: Bool
converged = Double -> Double
forall a. Num a => a -> a
abs (Double
next Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
u) Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
<= Double
tolerance Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double -> Double -> Double
forall a. Ord a => a -> a -> a
max Double
1 (Double -> Double
forall a. Num a => a -> a
abs Double
u)
rawInitialDifficulty :: Parameters -> Rating -> Double
rawInitialDifficulty :: Parameters -> Rating -> Double
rawInitialDifficulty Parameters
params Rating
rating =
Parameters -> Double
initialDifficultyBase Parameters
params
Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double -> Double
forall a. Floating a => a -> a
exp (Parameters -> Double
initialDifficultyRate Parameters
params Double -> Double -> Double
forall a. Num a => a -> a -> a
* Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Rating -> Int
ratingToInt Rating
rating Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1))
Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
1
initialDifficulty :: Parameters -> Rating -> Difficulty
initialDifficulty :: Parameters -> Rating -> Double
initialDifficulty Parameters
params =
Double -> Double -> Double -> Double
clampTo Double
difficultyMin Double
difficultyMax (Double -> Double) -> (Rating -> Double) -> Rating -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parameters -> Rating -> Double
rawInitialDifficulty Parameters
params
nextDifficulty :: Parameters -> Difficulty -> Rating -> Difficulty
nextDifficulty :: Parameters -> Double -> Rating -> Double
nextDifficulty Parameters
params Double
d Rating
rating =
Double -> Double -> Double -> Double
clampTo Double
difficultyMin Double
difficultyMax (Double -> Double
meanReversion Double
damped)
where
delta :: Double
delta = Double -> Double
forall a. Num a => a -> a
negate (Parameters -> Double
difficultyDelta Parameters
params) Double -> Double -> Double
forall a. Num a => a -> a -> a
* Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Rating -> Int
ratingToInt Rating
rating Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
3)
damped :: Double
damped = Double
d Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
delta Double -> Double -> Double
forall a. Num a => a -> a -> a
* (Double
10 Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
d) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
9
meanReversion :: Double -> Double
meanReversion Double
current = Double
0.01 Double -> Double -> Double
forall a. Num a => a -> a -> a
* Parameters -> Rating -> Double
rawInitialDifficulty Parameters
params Rating
Easy Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
0.99 Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
current
initialStability :: Parameters -> Rating -> Stability
initialStability :: Parameters -> Rating -> Double
initialStability = Parameters -> Rating -> Double
initialStabilityWeight
stabilityAfterReview
:: StabilityWeights
-> MemoryState
-> Retrievability
-> Rating
-> Stability
stabilityAfterReview :: StabilityWeights -> MemoryState -> Double -> Rating -> Double
stabilityAfterReview StabilityWeights
w (MemoryState Double
s Double
d) Double
r Rating
rating
| Rating
rating Rating -> Rating -> Bool
forall a. Eq a => a -> a -> Bool
== Rating
Again = Double
postLapse
| Bool
otherwise = Double -> Double -> Double
forall a. Ord a => a -> a -> a
max Double
postLapse (Double
s Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
increase)
where
postLapse :: Double
postLapse = Double -> Double -> Double
forall a. Ord a => a -> a -> a
min Double
s Double
failureStability
failureStability :: Double
failureStability =
StabilityWeights -> Double
swFailureFactor StabilityWeights
w
Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
d Double -> Double -> Double
forall a. Floating a => a -> a -> a
** Double -> Double
forall a. Num a => a -> a
negate (StabilityWeights -> Double
swFailureDifficultyExponent StabilityWeights
w)
Double -> Double -> Double
forall a. Num a => a -> a -> a
* ((Double
s Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
1) Double -> Double -> Double
forall a. Floating a => a -> a -> a
** StabilityWeights -> Double
swFailureStabilityExponent StabilityWeights
w Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
1)
Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double -> Double
forall a. Floating a => a -> a
exp ((Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
r) Double -> Double -> Double
forall a. Num a => a -> a -> a
* StabilityWeights -> Double
swFailureRetrievabilityFactor StabilityWeights
w)
hardPenalty :: Double
hardPenalty = if Rating
rating Rating -> Rating -> Bool
forall a. Eq a => a -> a -> Bool
== Rating
Hard then StabilityWeights -> Double
swHardPenalty StabilityWeights
w else Double
1
easyBonus :: Double
easyBonus = if Rating
rating Rating -> Rating -> Bool
forall a. Eq a => a -> a -> Bool
== Rating
Easy then StabilityWeights -> Double
swEasyBonus StabilityWeights
w else Double
1
increase :: Double
increase =
Double
1
Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double -> Double
forall a. Floating a => a -> a
exp (StabilityWeights -> Double
swIncreaseBase StabilityWeights
w Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
1.5)
Double -> Double -> Double
forall a. Num a => a -> a -> a
* (Double
11 Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
d)
Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
s Double -> Double -> Double
forall a. Floating a => a -> a -> a
** Double -> Double
forall a. Num a => a -> a
negate (StabilityWeights -> Double
swIncreaseStabilityExponent StabilityWeights
w)
Double -> Double -> Double
forall a. Num a => a -> a -> a
* (Double -> Double
forall a. Floating a => a -> a
exp ((Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
r) Double -> Double -> Double
forall a. Num a => a -> a -> a
* StabilityWeights -> Double
swIncreaseRetrievabilityFactor StabilityWeights
w) Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
1)
Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
hardPenalty
Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
easyBonus
transitionCoefficient :: Parameters -> Days -> Double
transitionCoefficient :: Parameters -> Double -> Double
transitionCoefficient Parameters
params Double
deltaT =
Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
- Parameters -> Double
transitionAmplitude Parameters
params Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double -> Double
forall a. Floating a => a -> a
exp (Double -> Double
forall a. Num a => a -> a
negate (Parameters -> Double
transitionRate Parameters
params) Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
deltaT)
nextStability
:: Parameters
-> MemoryState
-> Days
-> Rating
-> Stability
nextStability :: Parameters -> MemoryState -> Double -> Rating -> Double
nextStability Parameters
params MemoryState
state Double
deltaT Rating
rating =
Double
coefficient Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
longTerm Double -> Double -> Double
forall a. Num a => a -> a -> a
+ (Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
coefficient) Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
shortTerm
where
r :: Double
r = Parameters -> Double -> Double -> Double
retrievability Parameters
params Double
deltaT (MemoryState -> Double
memoryStability MemoryState
state)
longTerm :: Double
longTerm = StabilityWeights -> MemoryState -> Double -> Rating -> Double
stabilityAfterReview (Parameters -> StabilityWeights
longTermWeights Parameters
params) MemoryState
state Double
r Rating
rating
shortTerm :: Double
shortTerm = StabilityWeights -> MemoryState -> Double -> Rating -> Double
stabilityAfterReview (Parameters -> StabilityWeights
shortTermWeights Parameters
params) MemoryState
state Double
r Rating
rating
coefficient :: Double
coefficient = Parameters -> Double -> Double
transitionCoefficient Parameters
params Double
deltaT
nextMemoryState
:: Parameters
-> Maybe MemoryState
-> Days
-> Rating
-> MemoryState
nextMemoryState :: Parameters -> Maybe MemoryState -> Double -> Rating -> MemoryState
nextMemoryState Parameters
params Maybe MemoryState
before Double
deltaT Rating
rating =
MemoryState
{ memoryStability :: Double
memoryStability = Double -> Double -> Double -> Double
clampTo Double
stabilityMin Double
stabilityMax Double
s
, memoryDifficulty :: Double
memoryDifficulty = Double -> Double -> Double -> Double
clampTo Double
difficultyMin Double
difficultyMax Double
d
}
where
(Double
s, Double
d) = case Maybe MemoryState
before of
Maybe MemoryState
Nothing ->
( Parameters -> Rating -> Double
initialStability Parameters
params Rating
rating
, Parameters -> Rating -> Double
initialDifficulty Parameters
params Rating
rating
)
Just MemoryState
state ->
( Parameters -> MemoryState -> Double -> Rating -> Double
nextStability Parameters
params MemoryState
state Double
deltaT Rating
rating
, Parameters -> Double -> Rating -> Double
nextDifficulty Parameters
params (MemoryState -> Double
memoryDifficulty MemoryState
state) Rating
rating
)
replayReviews :: Parameters -> [(Days, Rating)] -> Maybe MemoryState
replayReviews :: Parameters -> [(Double, Rating)] -> Maybe MemoryState
replayReviews Parameters
params = Maybe MemoryState -> [(Double, Rating)] -> Maybe MemoryState
go Maybe MemoryState
forall a. Maybe a
Nothing
where
go :: Maybe MemoryState -> [(Double, Rating)] -> Maybe MemoryState
go Maybe MemoryState
before [] = Maybe MemoryState
before
go Maybe MemoryState
before ((Double
deltaT, Rating
rating) : [(Double, Rating)]
rest) =
let next :: MemoryState
next = Parameters -> Maybe MemoryState -> Double -> Rating -> MemoryState
nextMemoryState Parameters
params Maybe MemoryState
before Double
deltaT Rating
rating
in MemoryState
next MemoryState -> Maybe MemoryState -> Maybe MemoryState
forall a b. a -> b -> b
`seq` Maybe MemoryState -> [(Double, Rating)] -> Maybe MemoryState
go (MemoryState -> Maybe MemoryState
forall a. a -> Maybe a
Just MemoryState
next) [(Double, Rating)]
rest