# Fitts Memory Scheduler
Spaced repetition scheduler combining **SM-2** for interval scheduling with **Fitts' Law** for adaptive difficulty prediction.
## Key Innovation: Dual-Signal Capture
Traditional flashcard apps only capture **rating** (subjective). This library captures **both**:
| **Rating** | Subjective | User input | "How well did I recall?" → SM-2 intervals |
| **Response Time** | Objective | Measured | "How fast did I recall?" → Fitts calibration |
A user might:
- Respond **fast** but rate **Hard** (implicit memory worked, felt unsure)
- Respond **slow** but rate **Easy** (was distracted, knew the answer)
By capturing both, the model **personalizes** to each user.
## Installation
```toml
[dependencies]
fitts = "0.1"
```
## Quick Start
```rust
use fitts::{FittsScheduler, CardState, Rating, ReviewInput};
fn main() {
let mut scheduler = FittsScheduler::new();
let card = CardState::default();
// Predict difficulty before showing answer
let (predicted_rt, retrievability) = scheduler.predict(&card);
println!("Predicted: {:.2}s, Retrievability: {:.0}%", predicted_rt, retrievability * 100.0);
// User responds... app measures time
let input = ReviewInput::new(Rating::Good, 2500); // 2.5 seconds
// Process review with both signals
let result = scheduler.review(card, input);
println!("Next review in {} days", result.card.interval_days);
// See prediction error (model learns from this)
if let Some(error) = result.prediction_error {
println!("Prediction error: {:+.2}s", error);
}
}
```
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ ReviewInput │
│ ┌──────────────┬──────────────┐ │
│ │ Rating │ Response Time │ │
│ │ (subjective) │ (objective) │ │
│ └──────┬───────┴───────┬──────┘ │
│ │ │ │
│ ┌──────▼─────┐ ┌──────▼──────┐ │
│ │ SM-2 │ │ Fitts Law │ │
│ │ │ │ │ │
│ │ → interval │ │ → calibrate │ │
│ │ → ease │ │ → personalize│ │
│ └──────┬─────┘ └──────┬──────┘ │
│ │ │ │
│ ┌──────▼───────────────▼──────┐ │
│ │ ReviewResult │ │
│ │ • Updated card state │ │
│ │ • Predicted vs actual RT │ │
│ │ • Calibration results │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## Adaptive Calibration
The Fitts model learns from your actual response times using **gradient descent**:
```rust
use fitts::{FittsScheduler, CardState, Rating, ReviewInput};
let mut scheduler = FittsScheduler::with_learning_rate(0.1);
let card = CardState::default();
// Initial prediction
let (initial, _) = scheduler.predict(&card);
println!("Initial prediction: {:.2}s", initial);
// After 15 reviews, the model adapts
// If user consistently responds in ~2 seconds, model learns this
for _ in 0..15 {
let input = ReviewInput::new(Rating::Good, 2000);
scheduler.review(card.clone(), input);
}
let (calibrated, _) = scheduler.predict(&card);
println!("Calibrated prediction: {:.2}s", calibrated);
// Prediction now closer to 2 seconds
```
## Algorithms
### SM-2 (Wozniak, 1987)
Classic spaced repetition algorithm:
```text
interval(0) = 1 day
interval(1) = 6 days
interval(n) = interval(n-1) × EF
EF' = EF + (0.1 - (5-q) × (0.08 + (5-q) × 0.02))
EF ≥ 1.3
```
**Note**: This implementation uses a 0-3 quality scale (Again/Hard/Good/Easy) instead of SM-2's original 0-5 scale. Quality values are linearly mapped to 1-5 internally for ease factor calculation: `q_scaled = 1 + q × (4/3)`.
### Fitts' Law (1954) + Memory Adaptation
Original (MacKenzie, 1992): `MT = a + b × log₂(D/W + 1)`
Memory adaptation:
- **Distance** = memory decay = `ln(1 + interval) / ease`
- **Width** = accessibility = `stability × ease`
- **ID** (Index of Difficulty) = `log₂(distance/accessibility + 1)`
Formula: `RT = a + b × ID`
### Gradient Descent Calibration (Pavlik & Anderson, 2008)
```text
error = RT_actual - RT_predicted
a_new = a + α × error
b_new = b + α × error × ID
```
Where:
- α is the learning rate (default: 0.05)
- ID is the Index of Difficulty (already computed as log₂ term)
### Retrievability (Memory Strength)
Uses logistic function based on response time:
```text
R = 1 / (1 + exp((RT - τ) / σ))
```
- When RT < τ (threshold): R > 0.5 (likely to recall)
- When RT = τ: R = 0.5 (50/50 chance)
- When RT > τ: R < 0.5 (unlikely to recall)
## API Reference
### ReviewInput
```rust
// Full input with both signals
let input = ReviewInput::new(Rating::Good, 2500);
// Rating only (backward compatible)
scheduler.review(card, Rating::Good);
```
### FittsScheduler
```rust
// Default scheduler
let mut scheduler = FittsScheduler::new();
// With custom learning rate
let mut scheduler = FittsScheduler::with_learning_rate(0.1);
// Predict difficulty
let (response_time, retrievability) = scheduler.predict(&card);
// Review with adaptation
let result = scheduler.review(card, input);
// Order cards by difficulty (hardest first)
scheduler.order_by_difficulty(&mut cards);
```
### ReviewResult
```rust
pub struct ReviewResult {
pub card: CardState, // Updated card state
pub predicted_rt: f64, // What model predicted
pub actual_rt: Option<f64>, // What actually happened
pub prediction_error: Option<f64>, // actual - predicted
pub retrievability: f64, // Memory strength (0-1)
pub calibration: Option<CalibrationResult>,
}
```
### Rating & DifficultyLevel
Both use **4 values** for consistency:
| Again | VeryHard | 0 | Failure |
| Hard | Hard | 1 | Success |
| Good | Medium | 2 | Success |
| Easy | Easy | 3 | Success |
**Note**: Rating is subjective user input. Only `Again` resets the card. All others (`Hard`, `Good`, `Easy`) advance the card interval.
## Examples
```bash
# Basic usage
cargo run --example basic
# SM-2 interval progression
cargo run --example sm2_progression
# Fitts model predictions
cargo run --example fitts_model
# Adaptive calibration demo
cargo run --example adaptive_calibration
```
## Academic References
- **SM-2**: Wozniak, P.A. (1990). "Optimization of learning"
- **Fitts' Law**: Fitts, P.M. (1954). "The information capacity of the human motor system"
- **ACT-R Memory**: Anderson, J.R. (1993). "Rules of the Mind"
- **Adaptive Scheduling**: Pavlik, P.I. & Anderson, J.R. (2008). "Using a Model to Compute the Optimal Schedule of Practice"
- **Duolingo HLR**: Settles, B. & Meeder, B. (2016). "A Trainable Spaced Repetition Model for Language Learning"
## License
MIT OR Apache-2.0