Skip to content

Commit 8567f2d

Browse files
prithaxdevclaude
andcommitted
feat: implement SM-2 spaced repetition algorithm
Implements sm2_schedule() following Wozniak (1990): - quality < 3: reset interval to 1, repetitions to 0 - quality >= 3: interval grows (1 → 6 → interval × ease_factor) - ease_factor adjusts per quality, floored at 1.3 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e9431c0 commit 8567f2d

1 file changed

Lines changed: 57 additions & 0 deletions

File tree

apps/api/app/algorithms/sm2.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""SM-2 Spaced Repetition Algorithm.
2+
3+
Reference: Wozniak, P.A. (1990). SuperMemo 2 algorithm.
4+
https://www.supermemo.com/en/archives1990-2015/english/ol/sm2
5+
"""
6+
7+
8+
def sm2_schedule(
9+
quality: int,
10+
repetitions: int,
11+
ease_factor: float,
12+
interval: int,
13+
) -> tuple[int, int, float]:
14+
"""Calculate the next review schedule using the SM-2 algorithm.
15+
16+
Args:
17+
quality: Rating of recall quality from 0 to 5.
18+
0-2: complete failure / incorrect response
19+
3: correct response with serious difficulty
20+
4: correct response after hesitation
21+
5: perfect response
22+
repetitions: Number of times this item has been successfully recalled.
23+
ease_factor: Difficulty multiplier, minimum 1.3. Default starts at 2.5.
24+
interval: Current interval in days before the next review.
25+
26+
Returns:
27+
A tuple of (new_interval, new_repetitions, new_ease_factor).
28+
"""
29+
if not (0 <= quality <= 5):
30+
raise ValueError(f"quality must be between 0 and 5, got {quality}")
31+
if repetitions < 0:
32+
raise ValueError(f"repetitions must be >= 0, got {repetitions}")
33+
if ease_factor < 1.3:
34+
raise ValueError(f"ease_factor must be >= 1.3, got {ease_factor}")
35+
if interval < 1:
36+
raise ValueError(f"interval must be >= 1, got {interval}")
37+
38+
if quality < 3:
39+
# Forgot — reset progress
40+
new_repetitions = 0
41+
new_interval = 1
42+
else:
43+
# Remembered — advance schedule
44+
new_repetitions = repetitions + 1
45+
46+
if repetitions == 0:
47+
new_interval = 1
48+
elif repetitions == 1:
49+
new_interval = 6
50+
else:
51+
new_interval = round(interval * ease_factor)
52+
53+
# Adjust ease factor based on quality (applies regardless of pass/fail)
54+
new_ease_factor = ease_factor + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02))
55+
new_ease_factor = max(1.3, round(new_ease_factor, 4))
56+
57+
return new_interval, new_repetitions, new_ease_factor

0 commit comments

Comments
 (0)