haskell-fsrs
Safe HaskellSafe-Inferred
LanguageHaskell2010

FSRS.Algorithm

Description

The FSRS-7 memory model.

This module is a direct transcription of the reference implementation in srs-benchmark (models/fsrs_v7.py and models/fsrs_v7_interval_penalty.py). Every function here is pure and total.

What changed in FSRS-7 relative to FSRS-6:

  • The forgetting curve is a stability-weighted mixture of two power laws instead of one, which is why it has eight weights and no closed-form inverse (see nextIntervalDays).
  • The stability update is computed twice — once with a long-term weight block and once with a short-term one — and the two are blended by transitionCoefficient, a smooth function of the elapsed time. FSRS-6 switched between two separate formulas on a same-day/not-same-day flag.
  • Intervals are genuinely continuous. Ten minutes is 10 / 1440 days and the model is meant to be evaluated at such values, not at whole days.
Synopsis

Forgetting curve

retrievability :: Parameters -> Days -> Stability -> Retrievability Source #

The probability of recalling a card t days after the last review, given its stability.

FSRS-7 mixes two power laws whose relative weight depends on the stability itself, so — unlike every earlier version — the retrievability at t == s is not a fixed 0.9 but drifts with s.

t must be non-negative and s strictly positive.

retrievabilityDerivative :: Parameters -> Days -> Stability -> Double Source #

d/dt of retrievability. Never positive for in-bounds parameters.

nextIntervalDays :: Parameters -> Retrievability -> Stability -> Days Source #

The interval, in days, after which a card of the given stability will have decayed to exactly the desired retention.

The FSRS-7 forgetting curve has no closed-form inverse, so this is a root-find: Newton's method in log t (which is what makes the problem well-conditioned — see the upstream fsrs_v7_interval_penalty module), safeguarded by a bracketing bisection so that it cannot diverge.

The result is always within [minimumIntervalDays, maximumIntervalDays]; a desired retention that is unreachable within that window saturates at the nearer end.

Difficulty

initialDifficulty :: Parameters -> Rating -> Difficulty Source #

The difficulty a card is born with, given the rating of its first review.

nextDifficulty :: Parameters -> Difficulty -> Rating -> Difficulty Source #

Difficulty after a review: a rating-driven step, damped so that it slows down as difficulty approaches its maximum, then reverted 1% of the way towards the difficulty an Easy first review would have produced.

Stability

initialStability :: Parameters -> Rating -> Stability Source #

The stability a card is born with, given the rating of its first review.

stabilityAfterReview Source #

Arguments

:: StabilityWeights 
-> MemoryState 
-> Retrievability

The retrievability at review time, from retrievability.

-> Rating 
-> Stability 

One half of the stability update.

Called twice per review — once with longTermWeights and once with shortTermWeights — and the two results are blended by nextStability.

On a lapse the new stability is the post-lapse stability, which can never exceed the old one. On a success it is the old stability scaled by a factor that grows with how overdue the card was, shrinks as the card gets easier and more stable, and is scaled again by the hard penalty or the easy bonus.

transitionCoefficient :: Parameters -> Days -> Double Source #

How much of a long-term review this is: 0 for a review that happens at the same instant as the previous one, tending to 1 as the gap grows.

This is the piece that replaces FSRS-6's hard same-day/not-same-day split. The elapsed time is expected to be non-negative.

nextStability Source #

Arguments

:: Parameters 
-> MemoryState 
-> Days

Days since the previous review.

-> Rating 
-> Stability 

Stability after a review, blending the long- and short-term updates.

State transition

nextMemoryState Source #

Arguments

:: Parameters 
-> Maybe MemoryState

The state before the review, or Nothing for a brand-new card.

-> Days

Days since the previous review.

-> Rating 
-> MemoryState 

Advance a card's memory state by one review.

Pass Nothing for a card that has never been reviewed; the elapsed time is then ignored and the state is read straight off the initial-stability and initial-difficulty weights.

replayReviews :: Parameters -> [(Days, Rating)] -> Maybe MemoryState Source #

Fold a whole review history into a memory state.

Each element is (days since the previous review, rating); the elapsed time of the first review is ignored. Nothing for an empty history.

Bounds

stabilityMin :: Stability Source #

The smallest stability the model will report, 1e-4 days (about nine seconds). This is the s_min the upstream configuration uses for FSRS-7, which is always run with second-precision intervals.

stabilityMax :: Stability Source #

The largest stability the model will report: 36500 days, a century.

minimumIntervalDays :: Days Source #

One second, expressed in days — the shortest interval nextIntervalDays will return.

maximumIntervalDays :: Days Source #

A century in days — the longest interval nextIntervalDays will return.