--- title: "Getting Started with rfsrs" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting Started with rfsrs} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## Introduction **rfsrs** provides R bindings for the FSRS (Free Spaced Repetition Scheduler) algorithm. FSRS is a modern spaced repetition algorithm that models human memory more accurately than traditional algorithms like SM-2. ```{r setup} library(rfsrs) ``` ## Understanding FSRS ### The Memory Model FSRS models memory using two key variables: 1. **Stability (S)**: Represents how long a memory will last. Higher stability means slower forgetting. Specifically, stability is the number of days until retrievability drops to 90%. 2. **Difficulty (D)**: Represents how hard the material is to learn. Ranges from 1-10. Higher difficulty means stability grows more slowly with each review. ### Retrievability Retrievability is the probability that you can recall a memory at a given time. ```{r forgetting-curve, fig.width=7, fig.height=5} # Visualize the forgetting curve for different stability values stability_values <- c(1, 5, 10, 30) days <- 0:60 # Simple base R plot plot(NULL, xlim = c(0, 60), ylim = c(0, 1), xlab = "Days since last review", ylab = "Retrievability", main = "Forgetting Curves for Different Stability Values") colors <- c("red", "orange", "blue", "green") for (i in seq_along(stability_values)) { s <- stability_values[i] r <- sapply(days, function(d) fsrs_recall_probability(s, d)) lines(days, r, col = colors[i], lwd = 2) } legend("topright", legend = paste("S =", stability_values, "days"), col = colors, lwd = 2) abline(h = 0.9, lty = 2, col = "gray") ``` ## Basic Usage ### Creating a New Card When you first learn a card, you create an initial memory state based on your first rating: ```{r initial-state} # Rating scale: 1=Again, 2=Hard, 3=Good, 4=Easy state <- fsrs_new_card_state(rating = 3) # First review, rated "Good" print(state) ``` The initial stability depends on your rating: ```{r compare-initial} ratings <- 1:4 rating_names <- c("Again", "Hard", "Good", "Easy") for (i in ratings) { s <- fsrs_new_card_state(rating = i) cat(sprintf("%s (rating=%d): stability=%.2f, difficulty=%.2f\n", rating_names[i], i, s$stability, s$difficulty)) } ``` ### Checking Retrievability After a review, you can check how well you'll remember the card over time: ```{r check-retrievability} state <- fsrs_new_card_state(rating = 3) cat("Retrievability after:\n") for (days in c(1, 3, 7, 14, 30)) { r <- fsrs_recall_probability(state$stability, elapsed_days = days) cat(sprintf(" %2d days: %.1f%%\n", days, r * 100)) } ``` ### Getting the Next Interval FSRS calculates the optimal interval for your next review based on desired retention: ```{r next-interval} state <- fsrs_new_card_state(rating = 3) # Get interval for 90% desired retention (default) interval_90 <- fsrs_interval(state$stability, desired_retention = 0.9) cat(sprintf("Interval for 90%% retention: %.1f days\n", interval_90)) # Get interval for 85% desired retention (fewer reviews) interval_85 <- fsrs_interval(state$stability, desired_retention = 0.85) cat(sprintf("Interval for 85%% retention: %.1f days\n", interval_85)) # Get interval for 95% desired retention (more reviews) interval_95 <- fsrs_interval(state$stability, desired_retention = 0.95) cat(sprintf("Interval for 95%% retention: %.1f days\n", interval_95)) ``` ### Simulating a Review After reviewing a card, update the memory state: ```{r next-state} # Initial state state <- fsrs_new_card_state(rating = 3) cat(sprintf("Initial: stability=%.2f\n", state$stability)) # Review after 2 days, rate "Good" state <- fsrs_next_memory_state( stability = state$stability, difficulty = state$difficulty, elapsed_days = 2, rating = 3 ) cat(sprintf("After review 1: stability=%.2f\n", state$stability)) # Review after 5 days, rate "Good" state <- fsrs_next_memory_state( stability = state$stability, difficulty = state$difficulty, elapsed_days = 5, rating = 3 ) cat(sprintf("After review 2: stability=%.2f\n", state$stability)) ``` ## Simulating Long-term Learning Let's simulate learning a card over many reviews: ```{r simulate-learning} simulate_learning <- function(n_reviews = 10, rating = 3, desired_retention = 0.9) { state <- fsrs_new_card_state(rating = rating) results <- data.frame( review = 0, day = 0, stability = state$stability, difficulty = state$difficulty, interval = NA ) current_day <- 0 for (i in 1:n_reviews) { interval <- fsrs_interval(state$stability, desired_retention) current_day <- current_day + interval state <- fsrs_next_memory_state(state$stability, state$difficulty, interval, rating, desired_retention) results <- rbind(results, data.frame( review = i, day = current_day, stability = state$stability, difficulty = state$difficulty, interval = interval )) } return(results) } # Simulate 10 reviews, always rating "Good" learning <- simulate_learning(n_reviews = 10, rating = 3) print(learning) ``` ```{r plot-learning, fig.width=7, fig.height=5} # Simulate a realistic learner who sometimes struggles simulate_realistic <- function(ratings, desired_retention = 0.9) { state <- fsrs_new_card_state(rating = ratings[1]) results <- data.frame( review = 0, day = 0, stability = state$stability, difficulty = state$difficulty, interval = NA, rating = ratings[1] ) current_day <- 0 for (i in seq_along(ratings)[-1]) { interval <- fsrs_interval(state$stability, desired_retention) current_day <- current_day + interval state <- fsrs_next_memory_state(state$stability, state$difficulty, interval, ratings[i], desired_retention) results <- rbind(results, data.frame( review = i - 1, day = current_day, stability = state$stability, difficulty = state$difficulty, interval = interval, rating = ratings[i] )) } results } # Always "Good" (idealized) vs. a realistic mix of ratings ideal <- simulate_learning(n_reviews = 10, rating = 3) realistic <- simulate_realistic(c(3, 2, 3, 1, 3, 3, 2, 3, 3, 3, 3)) # Log-scale plot comparing both scenarios plot(ideal$review, ideal$stability, type = "b", pch = 19, log = "y", xlab = "Review Number", ylab = "Stability (days, log scale)", main = "Stability Growth Over Reviews", ylim = range(c(realistic$stability, ideal$stability))) lines(realistic$review, realistic$stability, type = "b", pch = 17, col = "steelblue") legend("topleft", legend = c("Always Good", "Mixed ratings"), pch = c(19, 17), col = c("black", "steelblue"), lty = 1) grid() ``` ## FSRS Parameters FSRS uses 21 parameters that control the algorithm's behavior: ```{r parameters} params <- fsrs_parameters() cat("FSRS-6 default parameters (", length(params), " values):\n", sep = "") print(round(params, 4)) ``` ## Summary Key functions: | Function | Description | |----------|-------------| | `fsrs_parameters()` | Get the 21 default FSRS-6 parameters | | `fsrs_new_card_state(rating)` | Create initial memory state for a new card | | `fsrs_next_memory_state(stability, difficulty, elapsed_days, rating)` | Calculate next state after review | | `fsrs_interval(stability, desired_retention)` | Get optimal interval for desired retention | | `fsrs_recall_probability(stability, elapsed_days)` | Calculate probability of recall | For more information, see: - [ABC of FSRS](https://github.com/open-spaced-repetition/fsrs4anki/wiki/ABC-of-FSRS) - [fsrs-rs documentation](https://github.com/open-spaced-repetition/fsrs-rs)